• What do these classes do ?

    From Bonita Montero@3:633/10 to All on Wed Mar 25 19:02:37 2026
    #pragma once
    #include <cassert>
    #include <span>
    #include "inline.hpp"

    struct tls_shared
    {
    protected:
    using clean_up = void (*)() noexcept;
    struct node
    {
    node *next;
    clean_up cleanUp = nullptr;
    };
    template<bool Clear>
    static void runScopes( std::span<node **const> anchors );
    };

    template<bool Clear>
    NOINLINE void tls_shared::runScopes( std::span<node **const> anchors )
    {
    for( node **anchor : anchors )
    {
    node *nd = *anchor;
    if( !nd )
    continue;
    do
    {
    nd->cleanUp();
    if constexpr( Clear )
    nd->cleanUp = nullptr;
    } while( (nd = nd->next) != nullptr );
    if constexpr( Clear )
    *anchor = nullptr;
    }
    }

    template<size_t Scope>
    struct tls_anchor : protected tls_shared
    {
    protected:
    template<size_t ... Scopes>
    friend struct tls_scopes;
    void chain( clean_up cleanUp );
    using node = tls_shared::node;
    node m_node;
    inline constinit static thread_local node *t_anchor = nullptr;
    };

    template<size_t Scope>
    NOINLINE void tls_anchor<Scope>::chain( clean_up cleanUp )
    {
    m_node.next = t_anchor;
    t_anchor = &m_node;
    m_node.cleanUp = cleanUp;
    }

    template<typename T, size_t Scope = 0>
    struct tls_obj : public T, protected tls_anchor<Scope>
    {
    using clean_up = tls_shared::clean_up;
    void link( clean_up cleanUp );
    using T::T;
    template<typename Assign>
    T &operator =( Assign &&assign );
    };

    template<typename T, size_t Scope>
    FORCEINLINE void tls_obj<T, Scope>::link( clean_up cleanUp )
    {
    assert(cleanUp);
    if( !tls_anchor<Scope>::m_node.cleanUp ) [[unlikely]]
    tls_anchor<Scope>::chain( cleanUp );
    }

    template<typename T, size_t Scope>
    template<typename Assign>
    FORCEINLINE T &tls_obj<T, Scope>::operator =( Assign &&assign )
    {
    return T::operator =( std::forward<Assign>( assign ) );
    }

    template<size_t ... Scopes>
    struct tls_scopes : protected tls_shared
    {
    ~tls_scopes() noexcept;
    template<bool Clear>
    static void run() noexcept;
    private:
    inline static thread_local node **const t_anchors[sizeof ...(Scopes)]
    = { &tls_anchor<Scopes>::t_anchor ... };
    };

    template<size_t ... Scopes>
    NOINLINE tls_scopes<Scopes ...>::~tls_scopes() noexcept
    {
    runScopes<true>( t_anchors );
    }

    template<size_t ... Scopes>
    template<bool Clear>
    NOINLINE void tls_scopes<Scopes ...>::run() noexcept
    {
    runScopes<Clear>( t_anchors );
    }

    using tls_scope = tls_scopes<0>;

    template<size_t Begin, typename Indices>
    struct tls_range_helper;

    template<size_t ScopeBegin, size_t ... Indices>
    struct tls_range_helper<ScopeBegin, std::index_sequence<Indices ...>> : tls_scopes<ScopeBegin + Indices ...>
    {
    };

    template<size_t ScopeBegin, size_t ScopeEnd>
    struct tls_scope_range : tls_range_helper<ScopeBegin, std::make_index_sequence<ScopeEnd - ScopeBegin + 1>>
    {
    };

    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Chris M. Thomasson@3:633/10 to All on Wed Mar 25 16:09:42 2026
    On 3/25/2026 11:02 AM, Bonita Montero wrote:
    [...]

    Why not just use the C TSS? ;^)

    https://en.cppreference.com/w/c/thread/tss_create.html

    ?



    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bonita Montero@3:633/10 to All on Thu Mar 26 11:39:18 2026
    Am 26.03.2026 um 00:09 schrieb Chris M. Thomasson:

    On 3/25/2026 11:02 AM, Bonita Montero wrote:

    [...]

    Why not just use the C TSS? ;^) https://en.cppreference.com/w/c/thread/tss_create.html

    My idea works like that: You have a function which repeatedly needs
    a buffer or a dictionary (unordered_map) and this buffer should sur-
    vive a lot of calls. This is while allocating the buffer or filling
    it repeatedly takes a lot of time.
    You could simple use a thread_local object which you resize() as
    needed; the object sticks to its maximum capacity so that there
    are no expensive reallocations.
    The problem with that is that the buffer remains allocated until
    the thread ends. So you simply encapsulate the object in a tls_obj
    like that:

    thread_local tls_obj<vector<char>, 0> vc;

    This makes the buffer able to be freed later from an outer function
    scope. tls_obj inherits all constructors so that you can handle this
    object in the same way you can handle the encapsulated objec.
    Creating a thread_local in a function creates the object only once,
    so you can't handle any further arguments for the ceanup phase.
    That's while you have to link a cleanup C-callable, usually a lambda,
    to the tls_obj. This is done like that:

    vc.link( +[]() noexcept { vc.clear(); vc.shrink_to_fit(); } );

    The registering process puts this obj in a chained link whose head
    is also thread_local. It is done only once until the obj has passed
    a cleanup. The link() call is very inexpensive if the tls_obj has
    already been registered. It's just as with the thread_local object,
    itself, whose creation is guarded by a bool so that the compiler
    can check if the object already has been created in this thread.
    The cleanup-callable isn't has to be a C-function - enforced with
    the "+" in this case. This is sufficient since a C-callable has
    all capabiities to access static and thread_local variables and
    our tls_obj always has to be a thread_local object.
    The cleanup is done in an outer scope with a function directly or
    indirectly calling the function holding the tls_obj. It looks like
    that:

    tls_scopes<0> tlsScopes;

    When this object is destructed it cleans all tls_obj's registered
    under the ID zero in this thread. The IDs are a size_t and there's
    an individual head-pointer to the registered tls_obj-s so far in
    this thread. When the object has been cleared it has to be re
    -occupied in the next call.
    You can also clear a range of scopes like that:

    tls_scopes<ScopeFirst, ScopeSecond> tlsScopes;

    You can also define a range like that (begin & n).

    tls_scope_range<0, 10> allScopes;

    You can call the cleanup code also manually like that:

    tls_scope<0>::run<true>();

    The boolean template parameter controls whether the cleanup code
    is deregistered or not. With the desstructor of tls_scope it's
    always true.


    --- PyGate Linux v1.5.13
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)