What Happens to a Detached Thread When Main() Exits

What happens to a detached thread when main() exits?

The answer to the original question "what happens to a detached thread when main() exits" is:

It continues running (because the standard doesn't say it is stopped), and that's well-defined, as long as it touches neither (automatic|thread_local) variables of other threads nor static objects.

This appears to be allowed to allow thread managers as static objects (note in [basic.start.term]/4 says as much, thanks to @dyp for the pointer).

Problems arise when the destruction of static objects has finished, because then execution enters a regime where only code allowed in signal handlers may execute ([basic.start.term]/1, 1st sentence). Of the C++ standard library, that is only the <atomic> library ([support.runtime]/9, 2nd sentence). In particular, that—in general—excludes condition_variable (it's implementation-defined whether that is save to use in a signal handler, because it's not part of <atomic>).

Unless you've unwound your stack at this point, it's hard to see how to avoid undefined behaviour.

The answer to the second question "can detached threads ever be joined again" is:

Yes, with the *_at_thread_exit family of functions (notify_all_at_thread_exit(), std::promise::set_value_at_thread_exit(), ...).

As noted in footnote [2] of the question, signalling a condition variable or a semaphore or an atomic counter is not sufficient to join a detached thread (in the sense of ensuring that the end of its execution has-happened-before the receiving of said signalling by a waiting thread), because, in general, there will be more code executed after e.g. a notify_all() of a condition variable, in particular the destructors of automatic and thread-local objects.

Running the signalling as the last thing the thread does (after destructors of automatic and thread-local objects has-happened) is what the _at_thread_exit family of functions was designed for.

So, in order to avoid undefined behaviour in the absence of any implementation guarantees above what the standard requires, you need to (manually) join a detached thread with an _at_thread_exit function doing the signalling or make the detached thread execute only code that would be safe for a signal handler, too.

What happens to a detached thread inside a forked process when the process dies?

When you call std::thread::detach it does not decouple the thread from your process, it simply decouples the std::thread instance from the thread. It's stack is allocated from the process' memory, it's still sharing memory and resources with the process: when the process stops, it takes the thread out with it.

And it's not done gracefully, it doesn't have any of it's destructors called or even it's stack deallocated (which is why you are seeing a leak).

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <atomic>

struct OnExit
{
const char* id = "none";
~OnExit()
{
std::cout << "Exiting " << id << std::endl;
}
};

thread_local OnExit onExit;

void threadFn1()
{
onExit.id = "threadFn1";
for (size_t i = 0; i < 100000; ++i) {
std::cout << onExit.id << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(50));
}
}

std::atomic<bool> g_running { true };

void threadFn2()
{
onExit.id = "threadFn2";
while (g_running) {
std::cout << onExit.id << std::endl;
std::this_thread::sleep_for(std::chrono::microseconds(50));
}
}

int main()
{
std::thread t1(threadFn1);
std::cout << "started t1\n";
t1.detach();
std::cout << "detached t1\n";

std::thread t2(threadFn2);
std::cout << "started t2\n";
std::this_thread::sleep_for(std::chrono::microseconds(500));

std::cout << "ending\n";
g_running = false;
t2.join();
}

Live demo: http://coliru.stacked-crooked.com/a/aa775a2960db09db

Output

started t1
detached t1
started t2
threadFn2
threadFn1
threadFn2
threadFn2
ending
Exiting threadFn2

Because we self-terminate threadFn2, it gets to call the OnExit dtor, but threadFn1 is terminated brutally.

is it safe to detach a thread and then let it go out of scope (and have it still running)?

Thread t goes out of scope. I can't remember now if it is safe to do
this after you have called detach()

You detach() because you want to disassociate the actual running thread with the thread object. So after } t goes out of scope but the actual thread will keep on running until its instruction completes.

If it weren't for detach() std::terminate would have killed the thread at }

When the main thread exits, do other threads also exit?

When the main thread returns (i.e., you return from the main function), it terminates the entire process. This includes all other threads. The same thing happens when you call exit. You can avoid this by calling pthread_exit.

The purpose of pthread_detach is to make it so you don't need to join with other threads in order to release their resources. Detaching a thread does not make it exist past process termination, it will still be destroyed along with all the other threads.

How to reattach thread or wait for thread completion before exiting

Assuming that the IO thread is coded by you, you can handle this with a combination of std::promise and std::future, something like this:

#include <chrono>
#include <thread>
#include <future>
#include <iostream>

using namespace std::chrono_literals;

void demo_thread (std::promise <bool> *p)
{
std::cout << "demo thread waiting...\n";
std::this_thread::sleep_for (1000ms);
std::cout << "demo thread terminating\n";
p->set_value (true);
}

int main ()
{
std::promise <bool> p;
std::thread t = std::thread (demo_thread, &p);
t.detach ();

// ...

std::cout << "main thread waiting...\n";
std::future <bool> f = p.get_future();
f.wait ();

std::cout << "main thread terminating\n";
}

Live demo



Related Topics



Leave a reply



Submit