Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #120670 > unrolled thread

thread_local question...

Started by"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
First post2024-10-09 19:02 -0700
Last post2024-10-12 13:43 -0700
Articles 20 on this page of 23 — 4 participants

Back to article view | Back to comp.lang.c++


Contents

  thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-09 19:02 -0700
    Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-09 19:08 -0700
    Re: thread_local question... Andrey Tarasevich <andreytarasevich@hotmail.com> - 2024-10-09 19:33 -0700
      Re: thread_local question... Andrey Tarasevich <andreytarasevich@hotmail.com> - 2024-10-09 19:40 -0700
        Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-09 20:08 -0700
      Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-09 20:19 -0700
    Re: thread_local question... jseigh <jseigh_es00@xemaps.com> - 2024-10-10 07:52 -0400
      Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-10 12:04 -0700
        Re: thread_local question... wij <wyniijj5@gmail.com> - 2024-10-11 03:20 +0800
          Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-20 19:12 -0700
            Re: thread_local question... wij <wyniijj5@gmail.com> - 2024-10-21 19:06 +0800
              Re: thread_local question... wij <wyniijj5@gmail.com> - 2024-10-21 19:20 +0800
        Re: thread_local question... jseigh <jseigh_es00@xemaps.com> - 2024-10-10 15:57 -0400
          Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-11 13:36 -0700
        Re: thread_local question... jseigh <jseigh_es00@xemaps.com> - 2024-10-11 09:50 -0400
          Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-11 12:21 -0700
            Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-11 22:53 -0700
              Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-11 22:56 -0700
          Re: thread_local question... jseigh <jseigh_es00@xemaps.com> - 2024-10-11 17:05 -0400
            Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-12 13:44 -0700
              Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-12 13:46 -0700
    Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-12 13:29 -0700
      Re: thread_local question... "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2024-10-12 13:43 -0700

Page 1 of 2  [1] 2  Next page →


#120670 — thread_local question...

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-09 19:02 -0700
Subjectthread_local question...
Message-ID<ve7cj8$2vun0$1@dont-email.me>
How can I get access to self in my ct_foo() function below? Am I 
misusing thread_local here? I want self to be able to be accessible from 
any function that my ct_thread() function calls. It feels like I am 
doing something wrong here. However, I am getting correct ctor's and 
dtor's, I am not sure how to access self from ct_foo() called from 
ct_thread()?

I must be missing something obvious.

First of all can you compile and run it?
_______________________________
#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;
         }
     }
};



void
ct_foo()
{
     // Ummm, how to get at self?
}


void
ct_thread(
     ct_shared& shared,
     unsigned long id
) {
     // Well, self should be per thread...
     thread_local ct_per_thread self(shared, id);

     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;
}
_______________________________


Here is one of the outputs I get:
____________
ct_plot_pre_alpha... Testing 123! :^)

_____________________________________________
launching 5 threads...
ct_per_thread::ct_per_thread(0)
ct_per_thread::ct_per_thread(2)
ct_per_thread::ct_per_thread(3)
ct_per_thread::ct_per_thread(1)
ct_per_thread::ct_per_thread(4)
ct_per_thread::~ct_per_thread(0)
ct_per_thread::~ct_per_thread(2)
ct_per_thread::~ct_per_thread(3)
ct_per_thread::~ct_per_thread(1)
ct_per_thread::~ct_per_thread(4)
_____________________________________________
complete!
____________


Still, I think the tss_create() is better. Perhaps because it's more in 
line with PThreads... ;^)

Any thoughts?

[toc] | [next] | [standalone]


#120671

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-09 19:08 -0700
Message-ID<ve7cut$2vun0$2@dont-email.me>
In reply to#120670
On 10/9/2024 7:02 PM, Chris M. Thomasson wrote:
> How can I get access to self in my ct_foo() function below? Am I 
> misusing thread_local here? I want self to be able to be accessible from 
> any function that my ct_thread() function calls. It feels like I am 
> doing something wrong here. However, I am getting correct ctor's and 
> dtor's, I am not sure how to access self from ct_foo() called from 
> ct_thread()?
> 
> I must be missing something obvious.
> 
> First of all can you compile and run it?
> _______________________________
[...]

> Still, I think the tss_create() is better. Perhaps because it's more in 
> line with PThreads... ;^)
> 
> Any thoughts?

I think I could store a per-thread pointer to it in a global via 
thread_local. Humm...

[toc] | [prev] | [next] | [standalone]


#120672

FromAndrey Tarasevich <andreytarasevich@hotmail.com>
Date2024-10-09 19:33 -0700
Message-ID<ve7eek$30408$1@dont-email.me>
In reply to#120670
On 10/09/24 7:02 PM, Chris M. Thomasson wrote:
> 
> void
> ct_thread(
>      ct_shared& shared,
>      unsigned long id
> ) {
>      // Well, self should be per thread...
>      thread_local ct_per_thread self(shared, id);
> 
>      ct_foo();
> }
> 

Um... Declaring a `thread_local` variable in block scope can serve only 
one purpose: to _hide_ the variable, to prevent anyone else from 
accessing it directly. `thread_local` is somewhat similar to `static` in 
single-threaded applications: the variable retains its value even when 
you exit and re-enter the scope. In all other respects the variable 
itself remain invisible to the outside scopes, which is the whole 
purpose of declaring it in block scope.

This is exactly what you did: you declared a variable as `thread_local` 
in block scope. Which means it will retain its value when you exit and 
re-enter the scope, but (as opposed to `static`) it is going to be a 
per-thread variable. Since you declared it locally, it implies that you 
_wanted_ to make it inaccessible from other scopes. Why are asking "how 
to get at self?" then?

The only way to access such variable from other scopes is to access it 
indirectly: through a reference/pointer that you create in advance 
(where the variable is visible) and then somehow hand that 
reference/pointer over to other scopes. If you don't what to fiddle with 
indirect access, then declare your variable as `thread_local` in 
namespace scope. That way you will easily be able to get at it from 
other functions.

However, the real question here is: why you are even declaring your 
variable as `thread_local`? A regular local variable inside a thread 
function is already as "thread local" as it can possibly get. The only 
thing that `thread_local` will give you is the retention of the old 
value between calls. Do you really need that?

-- 
Best regards,
Andrey

[toc] | [prev] | [next] | [standalone]


#120673

FromAndrey Tarasevich <andreytarasevich@hotmail.com>
Date2024-10-09 19:40 -0700
Message-ID<ve7ero$308e7$1@dont-email.me>
In reply to#120672
On 10/09/24 7:33 PM, Andrey Tarasevich wrote:
> On 10/09/24 7:02 PM, Chris M. Thomasson wrote:
>>
>> void
>> ct_thread(
>>      ct_shared& shared,
>>      unsigned long id
>> ) {
>>      // Well, self should be per thread...
>>      thread_local ct_per_thread self(shared, id);
>>
>>      ct_foo();
>> }
>>
> 
> 
> However, the real question here is: why you are even declaring your 
> variable as `thread_local`? A regular local variable inside a thread 
> function is already as "thread local" as it can possibly get. The only 
> thing that `thread_local` will give you is the retention of the old 
> value between calls. Do you really need that?
> 

An additional remark:

The "retention of the old value between calls" would apply if you 
declared a variable like that in some nested function invoked multiple 
times from the top-level thread function.

But in your example the variable is declared in the top-level thread 
function itself. In that case there will be no "retention of the old 
value between calls" simply because once the thread function ends, the 
thread ends as well, and all its `thread_local` variables die anyway. 
So, your declaration does not seem to make any practical sense.

-- 
Best regards,
Andrey

[toc] | [prev] | [next] | [standalone]


#120674

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-09 20:08 -0700
Message-ID<ve7gg8$30d1s$1@dont-email.me>
In reply to#120673
On 10/9/2024 7:40 PM, Andrey Tarasevich wrote:
> On 10/09/24 7:33 PM, Andrey Tarasevich wrote:
>> On 10/09/24 7:02 PM, Chris M. Thomasson wrote:
>>>
>>> void
>>> ct_thread(
>>>      ct_shared& shared,
>>>      unsigned long id
>>> ) {
>>>      // Well, self should be per thread...
>>>      thread_local ct_per_thread self(shared, id);
>>>
>>>      ct_foo();
>>> }
>>>
>>
>>
>> However, the real question here is: why you are even declaring your 
>> variable as `thread_local`? A regular local variable inside a thread 
>> function is already as "thread local" as it can possibly get. The only 
>> thing that `thread_local` will give you is the retention of the old 
>> value between calls. Do you really need that?
>>
> 
> An additional remark:
> 
> The "retention of the old value between calls" would apply if you 
> declared a variable like that in some nested function invoked multiple 
> times from the top-level thread function.
> 
> But in your example the variable is declared in the top-level thread 
> function itself. In that case there will be no "retention of the old 
> value between calls" simply because once the thread function ends, the 
> thread ends as well, and all its `thread_local` variables die anyway. 
> So, your declaration does not seem to make any practical sense.
> 

How can I make my thread_local ct_per_thread self(shared, id) local to 
all threads? I am missing something obvious, I think... Shit. This is 
rather easily handled with tss_create or pthread_get/setspecific. I am 
missing something here, big time. Sorry!, shit.

Well, I am not sure how to pass per thread information into a 
thread_local _object_ that is global wrt a per thread ctor. I am so used 
to the pthread way of doing things where the pthread_key_t would be 
global... Actually, the C way now. tss_create and friends. Or the 
tss_key in:

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

Perhaps a global of:

thread_local ct_per_thread* g_tss = nullptr; // ?

? Ahh shit. I just need to try it out. I would want the tss dtors to be 
called on a per thread basis, way deep in thread shutdown...

[toc] | [prev] | [next] | [standalone]


#120675

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-09 20:19 -0700
Message-ID<ve7h3g$30h8k$1@dont-email.me>
In reply to#120672
On 10/9/2024 7:33 PM, Andrey Tarasevich wrote:
> On 10/09/24 7:02 PM, Chris M. Thomasson wrote:
>>
>> void
>> ct_thread(
>>      ct_shared& shared,
>>      unsigned long id
>> ) {
>>      // Well, self should be per thread...
>>      thread_local ct_per_thread self(shared, id);
>>
>>      ct_foo();
>> }
>>
> 
> Um... Declaring a `thread_local` variable in block scope can serve only 
> one purpose: to _hide_ the variable, to prevent anyone else from 
> accessing it directly. `thread_local` is somewhat similar to `static` in 
> single-threaded applications: the variable retains its value even when 
> you exit and re-enter the scope. In all other respects the variable 
> itself remain invisible to the outside scopes, which is the whole 
> purpose of declaring it in block scope.[...]

Okay, well, is this any better, or worse!

;^o


_________________________
#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;
}
_________________________

[toc] | [prev] | [next] | [standalone]


#120683

Fromjseigh <jseigh_es00@xemaps.com>
Date2024-10-10 07:52 -0400
Message-ID<ve8f6l$34vt2$1@dont-email.me>
In reply to#120670
On 10/9/24 22:02, Chris M. Thomasson wrote:
> How can I get access to self in my ct_foo() function below? Am I 
> misusing thread_local here? I want self to be able to be accessible from 
> any function that my ct_thread() function calls. It feels like I am 
> doing something wrong here. However, I am getting correct ctor's and 
> dtor's, I am not sure how to access self from ct_foo() called from 
> ct_thread()?
> 

std::thread::id id = std::this_thread::get_id();

C tss uses a thread local array I think but I don't know
it gets notification on individual thread exits so it
can run the dtor.  C++ has a cvar notify all on thread
exits.

I have a different hack which I won't get around to
until I need it.

Joe Seigh

[toc] | [prev] | [next] | [standalone]


#120696

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-10 12:04 -0700
Message-ID<ve98fe$392tv$2@dont-email.me>
In reply to#120683
On 10/10/2024 4:52 AM, jseigh wrote:
> On 10/9/24 22:02, Chris M. Thomasson wrote:
>> How can I get access to self in my ct_foo() function below? Am I 
>> misusing thread_local here? I want self to be able to be accessible 
>> from any function that my ct_thread() function calls. It feels like I 
>> am doing something wrong here. However, I am getting correct ctor's 
>> and dtor's, I am not sure how to access self from ct_foo() called from 
>> ct_thread()?
>>
> 
> std::thread::id id = std::this_thread::get_id();
> 
> C tss uses a thread local array I think but I don't know
> it gets notification on individual thread exits so it
> can run the dtor.  C++ has a cvar notify all on thread
> exits.
> 
> I have a different hack which I won't get around to
> until I need it.

I still don't know how C++ calls dtors wrt per-thread objects via 
thread_local. My hack would be to use tss_create and be done with it.

However, this "works", but the only reason the dtor is being called has 
nothing to do with thread_local. I resorted to using thread_local for a 
pointer to a local per_thread object on the threads stack. It's dtor 
means the thread exited. However, this is a lot different than 
tss_create or pthread_key_create. Iirc, those dtors get called a lot 
deeper in thread shutdown.

_______________________
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();
}
_______________________


I guess that is a hack.

[toc] | [prev] | [next] | [standalone]


#120697

Fromwij <wyniijj5@gmail.com>
Date2024-10-11 03:20 +0800
Message-ID<9dc17426ed663b192a4e1467b8fc488e6be377bd.camel@gmail.com>
In reply to#120696
On Thu, 2024-10-10 at 12:04 -0700, Chris M. Thomasson wrote:
> On 10/10/2024 4:52 AM, jseigh wrote:
> > On 10/9/24 22:02, Chris M. Thomasson wrote:
> > > How can I get access to self in my ct_foo() function below? Am I 
> > > misusing thread_local here? I want self to be able to be accessible 
> > > from any function that my ct_thread() function calls. It feels like I 
> > > am doing something wrong here. However, I am getting correct ctor's 
> > > and dtor's, I am not sure how to access self from ct_foo() called from 
> > > ct_thread()?
> > > 
> > 
> > std::thread::id id = std::this_thread::get_id();
> > 
> > C tss uses a thread local array I think but I don't know
> > it gets notification on individual thread exits so it
> > can run the dtor.  C++ has a cvar notify all on thread
> > exits.
> > 
> > I have a different hack which I won't get around to
> > until I need it.
> 
> I still don't know how C++ calls dtors wrt per-thread objects via 
> thread_local. My hack would be to use tss_create and be done with it.

That's why thread in libwy (RAII) is implemented as a class, 'thread' is an overload
virtual function in the thread class Wy::Thread (inspired by old day Qt thread). And,
'thread local' mostly is data member of the Wy::PThread. Not many app. really need
'thread local' (Other threads can access the 'local thread data').

> However, this "works", but the only reason the dtor is being called has 
> nothing to do with thread_local. I resorted to using thread_local for a 
> pointer to a local per_thread object on the threads stack. It's dtor 
> means the thread exited. However, this is a lot different than 
> tss_create or pthread_key_create. Iirc, those dtors get called a lot 
> deeper in thread shutdown.

Yes, RRAD is a problem of multi-threaded program.

> _______________________
> 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();
> }
> _______________________
> 
> 
> I guess that is a hack.

[toc] | [prev] | [next] | [standalone]


#120746

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-20 19:12 -0700
Message-ID<vf4db2$l6cb$6@dont-email.me>
In reply to#120697
On 10/10/2024 12:20 PM, wij wrote:
[...]

Check this shit out:

Here is my crude code:
_______________________________________
#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;
}
_______________________________________

[toc] | [prev] | [next] | [standalone]


#120747

Fromwij <wyniijj5@gmail.com>
Date2024-10-21 19:06 +0800
Message-ID<324eb5eb8a81aa1109d9ff6cf05c29886569e368.camel@gmail.com>
In reply to#120746
On Sun, 2024-10-20 at 19:12 -0700, Chris M. Thomasson wrote:
> On 10/10/2024 12:20 PM, wij wrote:
> [...]
> 
> Check this shit out:
> 
> Here is my crude code:
> _______________________________________
> #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;
> }
> _______________________________________

Don't know what the program is demonstrating.
The following is what I thought what ct_thread is doing.

1. In libwy, each thread is represented by a PThread object, following the RAII
   principle. (your program looked to me std::thread API has no idea of RAII).
2. libwy only has Mutex (wrapper of pthread_mutex), because other kinds of 
   mutexes are considered not often used and easily implemented (except futex)
   and many application variations, i.e. read/write mutex.
3. Wy::PThread has no public facilities for 'thread specific' (thread local), 
   because in most cases, 'thread local' can be implemented as class data 
   member. 
   (Wy::PThread is old from the time the underlying Linux Thread is buggy,
    and I am tired to revise it without real cases showing the necessarity.)

// t.cpp
#include <Wy.pthread.h>
#include <Wy.stdio.h>
    
using namespace Wy;

class Thread : public PThread {
    Mutex *m_gmtx;  // for 'thread common' data
    int   m_id;

    // Many 'thread local' data can be put here.
  public:
    Thread(Mutex& mtx, int id) : m_gmtx(&mtx), m_id(id) {};
    Errno begin() override { return PThread::begin(); };
    ~Thread() {
       NoCancel noc;
       tmain_close(); // Stop possibly active thread, then destruction begins.
       cout << "~Thread(mtx," << m_id << ")" WY_ENDL;
    };

  protected:
    Errno tmain() override {
      Mutex::Lock aa(*m_gmtx);
      cout << "Thread(mtx," << m_id << ")" WY_ENDL;
      return Ok;
    };
};

#define CT_THREADS 5

int main()
try {        
  Errno r;
  Mutex shared_mutex;
  Thread thrd[CT_THREADS]={
                   Thread(shared_mutex,0),
                   Thread(shared_mutex,1), 
                   Thread(shared_mutex,2),
                   Thread(shared_mutex,3),
                   Thread(shared_mutex,4),
                 };

 for(int i=0; i<CT_THREADS; ++i) {
   if((r=thrd[i].begin())!=Ok) {
     WY_THROW(r);
   }
 }

 for(int i=0; i<CT_THREADS; ++i) {
   if((r=thrd[i].wait_if(PThread::Running))!=Ok) {
     WY_THROW(r);
   }
 }

 return 0;
}
catch(const Errno& e) {
  cerr << wrd(e) << WY_ENDL;
  throw;
}
catch(...) {
  cerr << "Unknown thrown object" WY_ENDL;
  throw;
};

-----------------------------
[]$ g++ t.cpp -lwy
[]$ ./a.out 
Thread(mtx,0)
Thread(mtx,1)
Thread(mtx,4)
Thread(mtx,2)
Thread(mtx,3)
~Thread(mtx,4)
~Thread(mtx,3)
~Thread(mtx,2)
~Thread(mtx,1)
~Thread(mtx,0)



[toc] | [prev] | [next] | [standalone]


#120748

Fromwij <wyniijj5@gmail.com>
Date2024-10-21 19:20 +0800
Message-ID<4ba69cbf99403319abd6209d4423dabfebec0600.camel@gmail.com>
In reply to#120747
On Mon, 2024-10-21 at 19:06 +0800, wij wrote:
> On Sun, 2024-10-20 at 19:12 -0700, Chris M. Thomasson wrote:
> > On 10/10/2024 12:20 PM, wij wrote:
> > [...]
> > 
> > Check this shit out:
> > 
> > Here is my crude code:
> > _______________________________________
> > #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;
> > }
> > _______________________________________
> 
> Don't know what the program is demonstrating.
> The following is what I thought what ct_thread is doing.
> 
> 1. In libwy, each thread is represented by a PThread object, following the RAII
>    principle. (your program looked to me std::thread API has no idea of RAII).
> 2. libwy only has Mutex (wrapper of pthread_mutex), because other kinds of 
>    mutexes are considered not often used and easily implemented (except futex)
>    and many application variations, i.e. read/write mutex.
> 3. Wy::PThread has no public facilities for 'thread specific' (thread local), 
>    because in most cases, 'thread local' can be implemented as class data 
>    member. 
>    (Wy::PThread is old from the time the underlying Linux Thread is buggy,
>     and I am tired to revise it without real cases showing the necessarity.)
> 
> // t.cpp
> #include <Wy.pthread.h>
> #include <Wy.stdio.h>
>     
> using namespace Wy;
> 
> class Thread : public PThread {
>     Mutex *m_gmtx;  // for 'thread common' data
>     int   m_id;
> 
>     // Many 'thread local' data can be put here.
>   public:
>     Thread(Mutex& mtx, int id) : m_gmtx(&mtx), m_id(id) {};
>     Errno begin() override { return PThread::begin(); };
>     ~Thread() {
>        NoCancel noc;
>        tmain_close(); // Stop possibly active thread, then destruction begins.
>        cout << "~Thread(mtx," << m_id << ")" WY_ENDL;
>     };
> 
>   protected:
>     Errno tmain() override {
>       Mutex::Lock aa(*m_gmtx);
>       cout << "Thread(mtx," << m_id << ")" WY_ENDL;
>       return Ok;
>     };
> };
> 
> #define CT_THREADS 5
> 
> int main()
> try {        
>   Errno r;
>   Mutex shared_mutex;
>   Thread thrd[CT_THREADS]={
>                    Thread(shared_mutex,0),
>                    Thread(shared_mutex,1), 
>                    Thread(shared_mutex,2),
>                    Thread(shared_mutex,3),
>                    Thread(shared_mutex,4),
>                  };
> 
>  for(int i=0; i<CT_THREADS; ++i) {
>    if((r=thrd[i].begin())!=Ok) {
>      WY_THROW(r);
>    }
>  }
> 
>  for(int i=0; i<CT_THREADS; ++i) {
>    if((r=thrd[i].wait_if(PThread::Running))!=Ok) {
>      WY_THROW(r);
>    }
>  }
> 
>  return 0;
> }
> catch(const Errno& e) {
>   cerr << wrd(e) << WY_ENDL;
>   throw;
> }
> catch(...) {
>   cerr << "Unknown thrown object" WY_ENDL;
>   throw;
> };
> 
> -----------------------------
> []$ g++ t.cpp -lwy
> []$ ./a.out 
> Thread(mtx,0)
> Thread(mtx,1)
> Thread(mtx,4)
> Thread(mtx,2)
> Thread(mtx,3)
> ~Thread(mtx,4)
> ~Thread(mtx,3)
> ~Thread(mtx,2)
> ~Thread(mtx,1)
> ~Thread(mtx,0)




Correction of the critic "your program looked to me std::thread API has no idea of RAII"
from "threads[i] = std::thread(ct_thread, std::ref(shared), i);"
It may just difference of implementation.

[toc] | [prev] | [next] | [standalone]


#120698

Fromjseigh <jseigh_es00@xemaps.com>
Date2024-10-10 15:57 -0400
Message-ID<ve9bjm$39fjg$1@dont-email.me>
In reply to#120696
On 10/10/24 15:04, Chris M. Thomasson wrote:
> On 10/10/2024 4:52 AM, jseigh wrote:
>> On 10/9/24 22:02, Chris M. Thomasson wrote:
>>> How can I get access to self in my ct_foo() function below? Am I 
>>> misusing thread_local here? I want self to be able to be accessible 
>>> from any function that my ct_thread() function calls. It feels like I 
>>> am doing something wrong here. However, I am getting correct ctor's 
>>> and dtor's, I am not sure how to access self from ct_foo() called 
>>> from ct_thread()?
>>>
>>
>> std::thread::id id = std::this_thread::get_id();
>>
>> C tss uses a thread local array I think but I don't know
>> it gets notification on individual thread exits so it
>> can run the dtor.  C++ has a cvar notify all on thread
>> exits.
>>
>> I have a different hack which I won't get around to
>> until I need it.
> 
> I still don't know how C++ calls dtors wrt per-thread objects via 
> thread_local. My hack would be to use tss_create and be done with it.
> 

I got some stack traces from C vs C++ threads (not the main threads
which have identical stack traces).

C
#4  0x00000000004011c6 in test ()
#5  0x00007ff68a489be1 in start_thread (arg=<optimized out>) at 
pthread_create.c:440
#6  0x00007ff68a50ed40 in clone3 () at 
../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

C++
#4  0x00000000004012b6 in test(void*) ()
#5  0x00007fa23f4dbad4 in std::execute_native_thread_routine 
(__p=0x173ceb0) at ../../../../../libstdc++-v3/src/c++11/thread.cc:82
#6  0x00007fa23f089d22 in start_thread (arg=<optimized out>) at 
pthread_create.c:443
#7  0x00007fa23f10ed40 in clone3 () at 
../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

So the tss uses the pthread stuff and C++ uses
std::execute_native_thread_routine on top of that.

C and C++ main
#4  0x0000000000401056 in main ()
#5  0x00007fae98e295d0 in __libc_start_call_main 
(main=main@entry=0x401050 <main>, argc=argc@entry=1,
     argv=argv@entry=0x7ffff84f1d18) at 
../sysdeps/nptl/libc_start_call_main.h:58
#6  0x00007fae98e29680 in __libc_start_main_impl (main=0x401050 <main>, 
argc=1, argv=0x7ffff84f1d18, init=<optimized out>,
     fini=<optimized out>, rtld_fini=<optimized out>, 
stack_end=0x7ffff84f1d08) at ../csu/libc-start.c:389
#7  0x0000000000401085 in _start ()

No extra c++ magic sauce there that I can see.

I wonder how many people realize a thread's entry point isn't
necessarily the first thing that's called.

Joe Seigh


[toc] | [prev] | [next] | [standalone]


#120704

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-11 13:36 -0700
Message-ID<vec27j$3purf$1@dont-email.me>
In reply to#120698
On 10/10/2024 12:57 PM, jseigh wrote:
>> On 10/10/24 15:04, Chris M. Thomasson wrote:
[...]
> I wonder how many people realize a thread's entry point isn't
> necessarily the first thing that's called.

No shit! Good one. :^)

[toc] | [prev] | [next] | [standalone]


#120702

Fromjseigh <jseigh_es00@xemaps.com>
Date2024-10-11 09:50 -0400
Message-ID<vebael$3make$1@dont-email.me>
In reply to#120696
On 10/10/24 15:04, Chris M. Thomasson wrote:
> On 10/10/2024 4:52 AM, jseigh wrote:
>> On 10/9/24 22:02, Chris M. Thomasson wrote:
>>> How can I get access to self in my ct_foo() function below? Am I 
>>> misusing thread_local here? I want self to be able to be accessible 
>>> from any function that my ct_thread() function calls. It feels like I 
>>> am doing something wrong here. However, I am getting correct ctor's 
>>> and dtor's, I am not sure how to access self from ct_foo() called 
>>> from ct_thread()?
>>>
>>
>> std::thread::id id = std::this_thread::get_id();
>>
>> C tss uses a thread local array I think but I don't know
>> it gets notification on individual thread exits so it
>> can run the dtor.  C++ has a cvar notify all on thread
>> exits.
>>
>> I have a different hack which I won't get around to
>> until I need it.
> 
> I still don't know how C++ calls dtors wrt per-thread objects via 
> thread_local. My hack would be to use tss_create and be done with it.
> 

Ok, it looks like they are using libc atexit function (stdlib.h) to
invoke tls cleanup on thread exit.

Joe Seigh

[toc] | [prev] | [next] | [standalone]


#120703

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-11 12:21 -0700
Message-ID<vebtr6$3p9aj$1@dont-email.me>
In reply to#120702
On 10/11/2024 6:50 AM, jseigh wrote:
> On 10/10/24 15:04, Chris M. Thomasson wrote:
>> On 10/10/2024 4:52 AM, jseigh wrote:
>>> On 10/9/24 22:02, Chris M. Thomasson wrote:
>>>> How can I get access to self in my ct_foo() function below? Am I 
>>>> misusing thread_local here? I want self to be able to be accessible 
>>>> from any function that my ct_thread() function calls. It feels like 
>>>> I am doing something wrong here. However, I am getting correct 
>>>> ctor's and dtor's, I am not sure how to access self from ct_foo() 
>>>> called from ct_thread()?
>>>>
>>>
>>> std::thread::id id = std::this_thread::get_id();
>>>
>>> C tss uses a thread local array I think but I don't know
>>> it gets notification on individual thread exits so it
>>> can run the dtor.  C++ has a cvar notify all on thread
>>> exits.
>>>
>>> I have a different hack which I won't get around to
>>> until I need it.
>>
>> I still don't know how C++ calls dtors wrt per-thread objects via 
>> thread_local. My hack would be to use tss_create and be done with it.
>>
> 
> Ok, it looks like they are using libc atexit function (stdlib.h) to
> invoke tls cleanup on thread exit.

Thanks for all of the info Joe! I have an idea that might allow me to 
get a dtor called from thread_local in my test code. I just need to try 
it. Wrt:
_________________________________

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();
}
_________________________________


Well, what if I made self in ct_thread thread_local as well? That should 
call self's dtor deeper down the pipe, so to speak. Humm... It should 
work fine. Then any function that ct_thread calls would be able to 
access self via g_per_thread...

;^)

[toc] | [prev] | [next] | [standalone]


#120706

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-11 22:53 -0700
Message-ID<ved2sr$28p8$2@dont-email.me>
In reply to#120703
On 10/11/2024 12:21 PM, Chris M. Thomasson wrote:
> On 10/11/2024 6:50 AM, jseigh wrote:
>> On 10/10/24 15:04, Chris M. Thomasson wrote:
>>> On 10/10/2024 4:52 AM, jseigh wrote:
>>>> On 10/9/24 22:02, Chris M. Thomasson wrote:
>>>>> How can I get access to self in my ct_foo() function below? Am I 
>>>>> misusing thread_local here? I want self to be able to be accessible 
>>>>> from any function that my ct_thread() function calls. It feels like 
>>>>> I am doing something wrong here. However, I am getting correct 
>>>>> ctor's and dtor's, I am not sure how to access self from ct_foo() 
>>>>> called from ct_thread()?
>>>>>
>>>>
>>>> std::thread::id id = std::this_thread::get_id();
>>>>
>>>> C tss uses a thread local array I think but I don't know
>>>> it gets notification on individual thread exits so it
>>>> can run the dtor.  C++ has a cvar notify all on thread
>>>> exits.
>>>>
>>>> I have a different hack which I won't get around to
>>>> until I need it.
>>>
>>> I still don't know how C++ calls dtors wrt per-thread objects via 
>>> thread_local. My hack would be to use tss_create and be done with it.
>>>
>>
>> Ok, it looks like they are using libc atexit function (stdlib.h) to
>> invoke tls cleanup on thread exit.
> 
> Thanks for all of the info Joe! I have an idea that might allow me to 
> get a dtor called from thread_local in my test code. I just need to try 
> it. Wrt:
> _________________________________
> 
> 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();
> }
> _________________________________
> 
> 
> Well, what if I made self in ct_thread thread_local as well? That should 
> call self's dtor deeper down the pipe, so to speak. Humm... It should 
> work fine. Then any function that ct_thread calls would be able to 
> access self via g_per_thread...
> 
> ;^)

Humm... It would still not be "dynamic" like the pthread and/or 
tss_create version.

[toc] | [prev] | [next] | [standalone]


#120707

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-11 22:56 -0700
Message-ID<ved325$28pd$1@dont-email.me>
In reply to#120706
On 10/11/2024 10:53 PM, Chris M. Thomasson wrote:
[...]

> Humm... It would still not be "dynamic" like the pthread and/or 
> tss_create version.

It might be good enough for my purposes...

[toc] | [prev] | [next] | [standalone]


#120705

Fromjseigh <jseigh_es00@xemaps.com>
Date2024-10-11 17:05 -0400
Message-ID<vec3ut$3q5q6$1@dont-email.me>
In reply to#120702
On 10/11/24 09:50, jseigh wrote:
> On 10/10/24 15:04, Chris M. Thomasson wrote:
>> On 10/10/2024 4:52 AM, jseigh wrote:
>>> On 10/9/24 22:02, Chris M. Thomasson wrote:
>>>> How can I get access to self in my ct_foo() function below? Am I 
>>>> misusing thread_local here? I want self to be able to be accessible 
>>>> from any function that my ct_thread() function calls. It feels like 
>>>> I am doing something wrong here. However, I am getting correct 
>>>> ctor's and dtor's, I am not sure how to access self from ct_foo() 
>>>> called from ct_thread()?
>>>>
>>>
>>> std::thread::id id = std::this_thread::get_id();
>>>
>>> C tss uses a thread local array I think but I don't know
>>> it gets notification on individual thread exits so it
>>> can run the dtor.  C++ has a cvar notify all on thread
>>> exits.
>>>
>>> I have a different hack which I won't get around to
>>> until I need it.
>>
>> I still don't know how C++ calls dtors wrt per-thread objects via 
>> thread_local. My hack would be to use tss_create and be done with it.
>>
> 
> Ok, it looks like they are using libc atexit function (stdlib.h) to
> invoke tls cleanup on thread exit.

Correction.  atexit is for process exit not thread exit.  So those
start_ functions are where they are doing the exit handling.

Joe Seigh

[toc] | [prev] | [next] | [standalone]


#120717

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2024-10-12 13:44 -0700
Message-ID<veen2q$a82s$2@dont-email.me>
In reply to#120705
On 10/11/2024 2:05 PM, jseigh wrote:
> On 10/11/24 09:50, jseigh wrote:
>> On 10/10/24 15:04, Chris M. Thomasson wrote:
>>> On 10/10/2024 4:52 AM, jseigh wrote:
>>>> On 10/9/24 22:02, Chris M. Thomasson wrote:
>>>>> How can I get access to self in my ct_foo() function below? Am I 
>>>>> misusing thread_local here? I want self to be able to be accessible 
>>>>> from any function that my ct_thread() function calls. It feels like 
>>>>> I am doing something wrong here. However, I am getting correct 
>>>>> ctor's and dtor's, I am not sure how to access self from ct_foo() 
>>>>> called from ct_thread()?
>>>>>
>>>>
>>>> std::thread::id id = std::this_thread::get_id();
>>>>
>>>> C tss uses a thread local array I think but I don't know
>>>> it gets notification on individual thread exits so it
>>>> can run the dtor.  C++ has a cvar notify all on thread
>>>> exits.
>>>>
>>>> I have a different hack which I won't get around to
>>>> until I need it.
>>>
>>> I still don't know how C++ calls dtors wrt per-thread objects via 
>>> thread_local. My hack would be to use tss_create and be done with it.
>>>
>>
>> Ok, it looks like they are using libc atexit function (stdlib.h) to
>> invoke tls cleanup on thread exit.
> 
> Correction.  atexit is for process exit not thread exit.  So those
> start_ functions are where they are doing the exit handling.

Well, here is a call stack for my most recent try wrt tss dtors in pure C++:

 >	ct_plot_pre_alpha.exe!ct_per_thread::~ct_per_thread() Line 39	C++
  	ct_plot_pre_alpha.exe!`ct_thread'::`3'::`dynamic atexit destructor 
for 'self''()	C++
  	ct_plot_pre_alpha.exe!__dyn_tls_dtor(void * __formal, const unsigned 
long dwReason, void * __formal) Line 122	C++
  	ntdll.dll!00007ffbed20bfca()	Unknown
  	ntdll.dll!00007ffbed1b8b5f()	Unknown
  	ntdll.dll!00007ffbed1b97e9()	Unknown
  	ntdll.dll!00007ffbed1b9428()	Unknown
  	ntdll.dll!00007ffbed1eaf7e()	Unknown
  	KernelBase.dll!00007ffbea68e1ca()	Unknown
  	ucrtbased.dll!00007ffb83e932cb()	Unknown
  	ucrtbased.dll!00007ffb83e93931()	Unknown
  	ucrtbased.dll!00007ffb83e93017()	Unknown
  	kernel32.dll!00007ffbec15257d()	Unknown
  	ntdll.dll!00007ffbed1eaf08()	Unknown


atexit is in there.

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.c++


csiph-web