<div dir="ltr">Hi all,<div><br></div><div>I've been using static thread_locals and I observed confusing behavior for thread_local destruction at program exit. In my simplified example, an object owns a thread and joins on it when the object is destroyed. This is derived from a thread pool construct.</div><div><br></div><div>The object, defined as Wrapper, is held in a static global and I expect it to be destroyed at the end of main. The issue is that the static thread_local destructor from the spawned thread owned by Wrapper is not called and is reported as a leak by leak sanitizer. However, if I destroy the object before the end of main, the static thread_local destructor is indeed called.</div><div><br></div><div>My understanding is that thread_local destructors should be invoked before join returns. Why is it that the thread_local destructor is not invoked when the static global object is destroyed and joins on the thread after main ends?</div><div><br></div><div>In the output below, there is a message for the construction of the thread_local, but no corresponding message for the destruction of the thread_local.</div><div>







<p class="">clang --version</p><p class="">clang version 3.7.0 (tags/RELEASE_370/final)</p><p class="">Target: x86_64-redhat-linux-gnu</p><p class="">Thread model: posix</p><p class=""><br></p><p class="">clang++ -g -o foo foo.cc -fsanitize=leak -std=c++11 -lpthread</p><p class="">













</p><p class="">./foo</p><p class="">Started 140182605002496 from 140182628901568</p><p class="">Constructed thread_local from 140182605002496</p><p class="">Joining 140182605002496 from 140182628901568</p><p class="">










</p><p class=""><br></p><p class="">=================================================================</p><p class=""><b>==18828==ERROR: LeakSanitizer: detected memory leaks                                                                                                                                          </b></p><p class=""><b>Direct leak of 24 byte(s) in 1 object(s) allocated from:                                                                                                                                      </b></p><p class="">    #0 0x40a565 in operator new(unsigned long, std::nothrow_t const&) (/home/rliao/tmp/foo+0x40a565)</p><p class="">    #1 0x424d85 in __cxa_thread_atexit (/home/rliao/tmp/foo+0x424d85)</p><p class=""><br></p><p class=""><b>Direct leak of 8 byte(s) in 1 object(s) allocated from:                                                                                                                                       </b></p><p class="">    #0 0x40a325 in operator new(unsigned long) (/home/rliao/tmp/foo+0x40a325)</p><p class="">    #1 0x423273 in foo() /home/rliao/tmp/foo.cc:23:41</p><p class="">    #2 0x424698 in Wrapper::WorkerLoop(void*) /home/rliao/tmp/foo.cc:59:5</p><p class="">    #3 0x7f2ddd1baa50 in start_thread (/lib64/libpthread.so.0+0x7a50)</p><p class=""><br></p><p class="">
























</p><p class="">SUMMARY: LeakSanitizer: 32 byte(s) leaked in 2 allocation(s).</p></div><div><br clear="all"><div><div class="gmail_signature">--<br>Roger Liao</div></div>
</div></div>