• C++ equivalent of C tss_create

    From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Mon Oct 7 12:03:42 2024
    From Newsgroup: comp.lang.c++

    tss_create lets you dynamically create thread local storage.
    thread_local is static. Gets resolved at ld time. Doesn't
    work too well if you want a per object instance of thread
    local storage. Something like that in C++?

    Joe Seigh

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Mon Oct 7 12:50:07 2024
    From Newsgroup: comp.lang.c++

    On 10/7/2024 9:03 AM, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    I don't think so. Fwiw, a while back I was trying to port one of my
    thread local memory allocators that used pthread tss to pure C++ and
    gave up. It has important logic in its destructor. The function pointer
    in: pthread_key_create ala tss_create. So, shit happens! ;^o
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Mon Oct 7 12:54:02 2024
    From Newsgroup: comp.lang.c++

    On 10/7/2024 12:50 PM, Chris M. Thomasson wrote:
    On 10/7/2024 9:03 AM, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    I don't think so. Fwiw, a while back I was trying to port one of my
    thread local memory allocators that used pthread tss to pure C++ and
    gave up. It has important logic in its destructor. The function pointer
    in: pthread_key_create ala tss_create. So, shit happens! ;^o

    Perhaps I should revisit it to clear my mind. Humm...
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Mon Oct 7 18:16:58 2024
    From Newsgroup: comp.lang.c++

    On 10/7/24 15:50, Chris M. Thomasson wrote:
    On 10/7/2024 9:03 AM, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    I don't think so. Fwiw, a while back I was trying to port one of my
    thread local memory allocators that used pthread tss to pure C++ and
    gave up. It has important logic in its destructor. The function pointer
    in: pthread_key_create ala tss_create. So, shit happens! ;^o

    I suppose you could use a thread local map with logic on top of that.

    I probably don't need it. I was using it in C to get notification
    when a thread exited for clean up of any resources the thread had
    not explicitly cleaned up. I could come up with a thread exit
    listener if need be.

    Joe Seigh
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Mon Oct 7 17:46:06 2024
    From Newsgroup: comp.lang.c++

    On 10/7/2024 3:16 PM, jseigh wrote:
    On 10/7/24 15:50, Chris M. Thomasson wrote:
    On 10/7/2024 9:03 AM, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    I don't think so. Fwiw, a while back I was trying to port one of my
    thread local memory allocators that used pthread tss to pure C++ and
    gave up. It has important logic in its destructor. The function
    pointer in: pthread_key_create ala tss_create. So, shit happens! ;^o

    I suppose you could use a thread local map with logic on top of that.

    That would be like recreating a PThread impl? C with tss_create is okay.
    I remember way back trying to get a dtor per thread by using an object
    with thread_local. I need to revisit it. Perhaps compilers got much
    better. They should have! Or else, the C API is fine as it more closely
    goes with PThreads?



    I probably don't need it.  I was using it in C to get notification
    when a thread exited for clean up of any resources the thread had
    not explicitly cleaned up.  I could come up with a thread exit
    listener if need be.

    Joe Seigh

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Mon Oct 7 17:47:29 2024
    From Newsgroup: comp.lang.c++

    On 10/7/2024 3:16 PM, jseigh wrote:
    On 10/7/24 15:50, Chris M. Thomasson wrote:
    On 10/7/2024 9:03 AM, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    I don't think so. Fwiw, a while back I was trying to port one of my
    thread local memory allocators that used pthread tss to pure C++ and
    gave up. It has important logic in its destructor. The function
    pointer in: pthread_key_create ala tss_create. So, shit happens! ;^o

    I suppose you could use a thread local map with logic on top of that.

    I probably don't need it.  I was using it in C to get notification
    when a thread exited for clean up of any resources the thread had
    not explicitly cleaned up.

    Ditto!


    I could come up with a thread exit
    listener if need be.



    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Tue Oct 8 06:53:41 2024
    From Newsgroup: comp.lang.c++

    Am 07.10.2024 um 18:03 schrieb jseigh:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    Joe Seigh


    A thread_local object declared globally is constructed when a new
    thread starts and destructed when a thread ends. A thread_local
    object declared locally is constructed when the code comes across
    its declaration and destructed when the thread ends.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Mon Oct 7 23:29:41 2024
    From Newsgroup: comp.lang.c++

    On 10/7/2024 9:53 PM, Bonita Montero wrote:
    Am 07.10.2024 um 18:03 schrieb jseigh:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    Joe Seigh


    A thread_local object declared globally is constructed when a new
    thread starts and destructed when a thread ends. A thread_local
    object declared locally is constructed when the code comes across
    its declaration and destructed when the thread ends.


    Well, that should do it. Last time I checked, which was a while back,
    well, I was having trouble getting dtors to be called.

    So, psuedo code here:

    void ct_thread()
    {
    thread_local per_thread_data();
    }

    If I create a single thread, then I should get one ctor and one dtor
    with the dtor being called within the thread, right?

    If I create three threads, three ctors and three dtors and per their
    threads context, right?
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Tue Oct 8 09:02:35 2024
    From Newsgroup: comp.lang.c++

    Am 08.10.2024 um 08:29 schrieb Chris M. Thomasson:

    So, psuedo code here:

    void ct_thread()
    {
        thread_local per_thread_data();
    }

    If I create a single thread, then I should get one ctor and
    one dtor with the dtor being called within the thread, right?

    per_thread_data is created only if the thread comes across the
    definition. If you need reliable behaviour according to each
    created thread make per_thread_data global.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Sam@sam@email-scan.com to comp.lang.c++ on Tue Oct 8 08:26:56 2024
    From Newsgroup: comp.lang.c++

    jseigh writes:

    tss_create lets you dynamically create thread local storage.
    thread_local is static. Gets resolved at ld time. Doesn't
    work too well if you want a per object instance of thread
    local storage. Something like that in C++?

    Not in C++ proper, but gcc supports it:

    https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c++ on Tue Oct 8 16:32:33 2024
    From Newsgroup: comp.lang.c++

    Am 08.10.2024 um 14:26 schrieb Sam:
    jseigh writes:

    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    Not in C++ proper, but gcc supports it:

    https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html


    This doesn't help you if you want to get notified when a thread-local
    object is being destructed.

    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Tue Oct 8 12:00:19 2024
    From Newsgroup: comp.lang.c++

    On 10/8/2024 12:02 AM, Bonita Montero wrote:
    Am 08.10.2024 um 08:29 schrieb Chris M. Thomasson:

    So, psuedo code here:

    void ct_thread()
    {
         thread_local per_thread_data();
    }

    If I create a single thread, then I should get one ctor and
    one dtor with the dtor being called within the thread, right?

    per_thread_data is created only if the thread comes across the
    definition. If you need reliable behaviour according to each
    created thread make per_thread_data global.

    Sounds right to me. The last time I tried it (C++11 years ago), some of
    the dtors were not being called. The ctors were called... So, I just
    need to get back into it...

    Thanks Bonita. :^)
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Tue Oct 8 12:02:12 2024
    From Newsgroup: comp.lang.c++

    On 10/8/2024 7:32 AM, Bonita Montero wrote:
    Am 08.10.2024 um 14:26 schrieb Sam:
    jseigh writes:

    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    Not in C++ proper, but gcc supports it:

    https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html


    This doesn't help you if you want to get notified when a thread-local
    object is being destructed.


    That is very important for a lot of my older work.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Tue Oct 8 17:03:23 2024
    From Newsgroup: comp.lang.c++

    On 10/7/24 12:03, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    Joe Seigh


    I figured something out so all good I think.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Tue Oct 8 14:16:17 2024
    From Newsgroup: comp.lang.c++

    On 10/8/2024 2:03 PM, jseigh wrote:
    On 10/7/24 12:03, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    [...]


    I figured something out so all good I think.

    Iirc, a while back I was using placement new to create an object in tss.
    Then in the tss dtor function I would explicitly call the dtor of said
    object. Shit happens. ;^)
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Thu Oct 10 11:55:11 2024
    From Newsgroup: comp.lang.c++

    On 10/7/2024 9:03 AM, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?


    Something like this seems "okay" to me unless I am missing something.
    Humm... I still prefer the tss_create function.

    _________________________
    #include <iostream>
    #include <functional>
    #include <thread>
    #include <atomic>
    #include <mutex>


    #define CT_THREADS 5


    struct ct_shared
    {
    std::mutex m_cout_lock;
    };


    struct ct_per_thread
    {
    ct_shared& m_shared;
    unsigned long m_id;

    ct_per_thread(
    ct_shared& shared,
    unsigned long id

    ): m_shared(shared),
    m_id(id)
    {
    {
    std::unique_lock<std::mutex> lock(m_shared.m_cout_lock);
    std::cout << "ct_per_thread::ct_per_thread(" << m_id << ")"
    << std::endl;
    }
    }

    ~ct_per_thread()
    {
    {
    std::unique_lock<std::mutex> lock(m_shared.m_cout_lock);
    std::cout << "ct_per_thread::~ct_per_thread(" << m_id <<
    ")" << std::endl;
    }
    }
    };



    thread_local ct_per_thread* g_per_thread = nullptr;



    void
    ct_foo()
    {
    // Okay, what about this shit!
    {
    std::unique_lock<std::mutex> lock(g_per_thread->m_shared.m_cout_lock);
    std::cout << "ct_foo(" << g_per_thread->m_id << ")" << std::endl;
    }
    }


    void
    ct_thread(
    ct_shared& shared,
    unsigned long id
    ) {
    ct_per_thread self(shared, id);

    g_per_thread = &self;

    ct_foo();
    }


    int
    main()
    {
    std::cout << "ct_plot_pre_alpha... Testing 123! :^)\n\n";
    std::cout << "_____________________________________________" << std::endl;

    {
    ct_shared shared;

    {
    std::thread threads[CT_THREADS];

    std::cout << "launching " << CT_THREADS << " threads..." << std::endl;

    for (unsigned long i = 0; i < CT_THREADS; ++i)
    {
    threads[i] = std::thread(ct_thread, std::ref(shared), i);
    }

    for (unsigned long i = 0; i < CT_THREADS; ++i)
    {
    threads[i].join();
    }
    }
    }

    std::cout << "_____________________________________________\n";
    std::cout << "complete!\n";

    return 0;
    }
    _________________________
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From jseigh@jseigh_es00@xemaps.com to comp.lang.c++ on Sun Oct 13 13:56:22 2024
    From Newsgroup: comp.lang.c++

    On 10/8/24 17:03, jseigh wrote:
    On 10/7/24 12:03, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    Joe Seigh


    I figured something out so all good I think.


    So basically just declare a thread local array. It will have
    fixed size and you will have to manage allocation of array
    slots and dtors and stuff. E.g.

    thread_local void * x2[20];

    Accessing a slot value, if you inline it with a uint64_t
    key on x86 will get you something like

    movq %fs:x2@tpoff(,%rax,8), %rdx

    with %rax containing the index, i.e. the key,

    which will beat calling tss_get which calls
    pthread_getspecific.

    Joe Seigh
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c++ on Mon Oct 14 16:09:14 2024
    From Newsgroup: comp.lang.c++

    On 10/13/2024 10:56 AM, jseigh wrote:
    On 10/8/24 17:03, jseigh wrote:
    On 10/7/24 12:03, jseigh wrote:
    tss_create lets you dynamically create thread local storage.
    thread_local is static.  Gets resolved at ld time.  Doesn't
    work too well if you want a per object instance of thread
    local storage.  Something like that in C++?

    Joe Seigh


    I figured something out so all good I think.


    So basically just declare a thread local array.  It will have
    fixed size and you will have to manage allocation of array
    slots and dtors and stuff.  E.g.

    thread_local void * x2[20];

    Accessing a slot value, if you inline it with a uint64_t
    key on x86 will get you something like

        movq    %fs:x2@tpoff(,%rax,8), %rdx

    with %rax containing the index, i.e. the key,

    which will beat calling tss_get which calls
    pthread_getspecific.

    Interesting. I need to check the call stack of accessing a thread_local pointer. The g_per_thread wrt my recent code's ct_foo function that is
    called from ct_thread:

    void
    ct_foo()
    {
    // Okay, what about this shit!
    {
    std::unique_lock<std::mutex> lock(g_per_thread->m_shared.m_cout_lock);
    std::cout << "ct_foo(" << g_per_thread->m_id << ")" << std::endl;
    }
    }


    Full code for reference:
    _______________________________________
    #include <iostream>
    #include <functional>
    #include <thread>
    #include <atomic>
    #include <mutex>


    #define CT_THREADS 5


    struct ct_shared
    {
    std::mutex m_cout_lock;
    };


    struct ct_per_thread
    {
    ct_shared& m_shared;
    unsigned long m_id;

    ct_per_thread(
    ct_shared& shared,
    unsigned long id

    ): m_shared(shared),
    m_id(id)
    {
    {
    std::unique_lock<std::mutex> lock(m_shared.m_cout_lock);
    std::cout << "ct_per_thread::ct_per_thread(" << m_id << ")"
    << std::endl;
    }
    }

    ~ct_per_thread()
    {
    {
    std::unique_lock<std::mutex> lock(m_shared.m_cout_lock);
    std::cout << "ct_per_thread::~ct_per_thread(" << m_id <<
    ")" << std::endl;
    }
    }
    };



    thread_local ct_per_thread* g_per_thread = nullptr;



    void
    ct_foo()
    {
    // Okay, what about this shit!
    {
    std::unique_lock<std::mutex> lock(g_per_thread->m_shared.m_cout_lock);
    std::cout << "ct_foo(" << g_per_thread->m_id << ")" << std::endl;
    }
    }


    void
    ct_thread(
    ct_shared& shared,
    unsigned long id
    ) {
    {
    thread_local ct_per_thread self(shared, id);

    g_per_thread = &self;
    }

    ct_foo();
    }


    int
    main()
    {
    std::cout << "ct_plot_pre_alpha... Testing 123! :^)\n\n";
    std::cout << "_____________________________________________" << std::endl;

    {
    ct_shared shared;

    {
    std::thread threads[CT_THREADS];

    std::cout << "launching " << CT_THREADS << " threads..." << std::endl;

    for (unsigned long i = 0; i < CT_THREADS; ++i)
    {
    threads[i] = std::thread(ct_thread, std::ref(shared), i);
    }

    for (unsigned long i = 0; i < CT_THREADS; ++i)
    {
    threads[i].join();
    }
    }
    }

    std::cout << "_____________________________________________\n";
    std::cout << "complete!\n";

    return 0;
    }
    _______________________________________

    --- Synchronet 3.20a-Linux NewsLink 1.114