Why isn't a thread_local variable destroyed when the thread returns?
Asked Answered
T

1

10

For a better understanding of this question, here is the code:

// code 1
#include <iostream>
#include <thread>

struct tls_test {
    tls_test()
    { std::cout << "tls_test ctor\n"; }

    ~tls_test()
    { std::cout << "tls_test dtor\n"; }

    void print() const
    { std::cout << "tls_test print\n"; }
};

thread_local tls_test t;

void thread_1()
{
    std::cout << "thread_1 started\n";
    t.print();
    std::cout << "thread_1 return\n";
}

int main()
{
    std::thread trd{ thread_1 };
    trd.join();
    std::cout << "main return\n";
}

I'm using TDM-GCC and windows 10 to test this program. here is the output:

thread_1 started
tls_test ctor
tls_test print
thread_1 return
main return

According to basic.stc.thread, the variable t is constructed, shall be destroyed on thread exit. so I think that thread is not exit even the function thread_1 returns.

Does this behavior meet the standards? If so, why? When does the thread exit?

I also read thread.threads, but seems like the standard does not explain this.

Thanasi answered 25/6, 2023 at 2:39 Comment(12)
To me it looks like a bug in the compiler.Kunkel
Here's a possibly related issue report: github.com/msys2/MINGW-packages/issues/2519Kunkel
Does not reprocude on gcc 13.1 or clang 16.0.0Casmey
@PepijnKramer If my understanding is correct, you'd have to have Windows as the target OS to get the faulty behavior.Kunkel
@TedLyngmo And godbolt is not playing nice yet... no output for msvc. But running on my local visual studio 2022, I also do not see the problem.Casmey
@PepijnKramer No, it's (probably) a bug in the gcc port so you wouldn't be able to see it in VS.Kunkel
Oh sorry Ted, I am still waking up. Missed that detail.Casmey
Does adding extra scoping { std::thread trd{ thread_1 }; trd.join(); } std::cout << "main return\n"; change something? Could it be issue with order of deinitialization of global (std::cout, t)?Lycopodium
@Lycopodium I have tested, add extra scoping changes nothing. this behavior is meet the standard because [[thread.thread.destr]/1](timsong-cpp.github.io/cppwp/n4659/thread.thread.destr#1) said: "If joinable(), calls terminate(). Otherwise, has no effects.". and for now I think this should be a bug for TDM-GCC that will never destroy the thread_local variable.Thanasi
@Lycopodium for more detail, I have read the source code for std::thread, I noticed that it's implemented using pthread, so I trying to call pthread_cancel and pthread_exit, but dtor for that thread_local still not calledThanasi
@Thanasi No, pthread_cancel and pthread_exit does nothing to the c++ wrapper, and the thread is dead after .join() so that's not the problem. It's the cleanup of the thread local storage that's borked.Kunkel
I dont think this is a bug (it might be, likely it is not a bug): * Thread Destruction: When a thread finishes its execution, the C++ standard specifies that destructors for thread_localobjects should be called. However, the timing and order of these destructors being called can depend on the runtime library and thread implementation used by the compiler. In your case, with TDM-GCC on Windows 10, it's possible that the runtime library does not call destructors for thread_local objects in a straightforward manner due to the specifics of thread termination on the platform.Hokusai
R
0

I tried and test on clang , VS 2019 and gcc , its working. TDM gcc is not an appropriate to use , i understand.

Rubinrubina answered 3/9, 2024 at 16:15 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.