C++11: What Happens If You Don't Call Join() for Std::Thread

C++11: What happens if you don't call join() for std::thread

If you have not detached or joined a thread when the destructor is called it will call std::terminate, we can see this by going to the draft C++11 standard we see that section 30.3.1.3 thread destructor says:

If joinable(), calls std::terminate(). Otherwise, has no effects. [
Note: Either implicitly detaching or joining a joinable() thread in
its destructor could result in difficult to debug correctness (for
detach) or performance (for join) bugs encountered only when an
exception is raised. Thus the programmer must ensure that the
destructor is never executed while the thread is still joinable. —end
note ]

as for a rationale for this behavior we can find a good summary in (Not) using std::thread

Why does the destructor of a joinable thread have to call
std::terminate? After all, the destructor could join with the child
thread, or it could detach from the child thread, or it could cancel
the thread. In short, you cannot join in the destructor as this would
result in unexpected (not indicated explicitly in the code) program
freeze in case f2 throws.

and an example follows and also says:

You cannot detach as it would risk the situation where main thread
leaves the scope which the child thread was launched in, and the child
thread keeps running and keeps references to the scope that is already
gone.

The article references N2802: A plea to reconsider detach-on-destruction for thread objects which is argument against the previous proposal which was detach on destruction if joinable and it notes that one of the two alternatives would be to join which could lead to deadlocks the other alternative is what we have today which is std::terminate on destruction if joinable.

Do I need to join every thread in my application ?

no, you can detach one thread if you want it to leave it alone.
If you start a thread, either you detach it or you join it before the program ends, otherwise this is undefined behaviour.

To know that a thread needs to be detached you need to ask yourself this question: "do I want the the thread to run after the program main function is finished?". Here are some examples:

  • When you do File/New you create a new thread and you detach it: the thread will be closed when the user closes the document Here you don't need to join the threads

  • When you do a Monte Carlo simulation, some distributed computing, or any Divide And Conquer type algorithms, you launch all the threads and you need to wait for all the results so that you can combine them. Here you explicitly need to join the thread before combining the results

Why program is terminated if neither calls std::thread::detach nor std::thread::join?

This was the subject of much debate pre-C++11.

Your question makes a bold assumption: that detachment is obviously the correct behavior. But you never substantiate it. Indeed, there are many arguments against this idea, and the committee considered them.

I'll take this example from the paper outlining the argument against it:

int fib(int n) {
if (n <= 1) return n;
int fib1, fib2;

std::thread t([=, &fib1]{fib1 = fib(n-1);});
fib2 = fib(n-2);
if (fib2 < 0) throw ...
t.join();
return fib1 + fib2;
}

Once you start throwing exceptions around, the default detachment behavior stops being so useful. Indeed, you can imagine a more complex case, where the exception comes from something non-local to the thread creation routine. Consider this example from a later paper:

      std::vector<std::pair<unsigned int, unsigned int>> partitions =
utils::partition_indexes(0, size-1, num_threads);
std::vector<std::thread> threads;

LOG(LOG_DEBUG, "controller::reload_all: starting reload threads...");
for (unsigned int i=0; i<num_threads-1; i++) {
threads.push_back(std::thread(reloadrangethread(this,
partitions[i].first, partitions[i].second, size, unattended)));
}

LOG(LOG_DEBUG, "controller::reload_all: starting my own reload...");
this->reload_range(partitions[num_threads-1].first,
partitions[num_threads-1].second, size, unattended);

LOG(LOG_DEBUG, "controller::reload_all: joining other threads...");
for (size_t i=0; i<threads.size(); i++) {
threads[i].join();
}

push_back can fail due to lack of memory for reallocating the array. If that happens, you lose access to all of those threads, and your program is broken.

Both of these scenarios lead to a broken program, whether you default to detach or default to terminate. But if the program is going to be broken, it's best that it be broken immediately when the issue occurs, rather than at some later location in the code.

Now, the safer solution is to join in the destructor. But that didn't happen for various other reasons. The (unfortunate) consensus was that, if you didn't say what you want to do, then your code is broken and should blow up.

Fortunately, C++20 gave us std::jthread, which joins by default in its destructor.

The general idea of std::thread's destructor behavior is very simple:

  1. The user didn't say what to do (ie: didn't call join or detach).
  2. Neither answer is obviously the right one.

You claim that simply doing a detach is the right solution. But why is it right? Detachment is a very unsafe thing to do since you lose the ability to join with the thread ever again.

There's also the issue of RAII. An exception could cause some thread object to be destroyed unintentionally. If that happens, and the default behavior is to detach, is your program still in a functional state? What if the rest of your program was expecting to join those threads, and now that's impossible?

Why must one call join() or detach() before thread destruction?

Technically the answer is "because the spec says so" but that is an obtuse answer. We can't read the designers' minds, but here are some issues that may have contributed:

With POSIX pthreads, child threads must be joined after they have exited, or else they continue to occupy system resources (like a process table entry in the kernel). This is done via pthread_join().
Windows has a somewhat analogous issue if the process holds a HANDLE to the child thread; although Windows doesn't require a full join, the process must still call CloseHandle() to release its refcount on the thread.

Since std::thread is a cross-platform abstraction, it's constrained by the POSIX requirement which requires the join.

In theory the std::thread destructor could have called pthread_join() instead of throwing an exception, but that (subjectively) that may increase the risk of deadlock. Whereas a properly written program would know when to insert the join at a safe time.

See also:

  • https://en.wikipedia.org/wiki/Zombie_process
  • https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
  • https://learn.microsoft.com/en-us/windows/win32/procthread/terminating-a-process

Exiting from a function that has a local std::thread object without join

Your concern is valid. Reading suitable reference documentation for std::thread tells us that on destruction:

If *this has an associated thread (joinable() == true), std::terminate() is called.

So destroying t without either detaching or joining it will end your program.

You therefore have two options, depending on what you want to happen. If you want the function to wait for the thread to finish, call t.join() before returning. If you instead want the thread to keep running after the function returns, call t.detach().

When should you use std::thread::joinable?

You use joinable when you have a std::thread object which may already have been joined, or may not reference an actual thread of execution (TOE - i.e., OS thread), and you want to join it if not already joined.

e.g. if you were implementing the proposed joinable_thread class, then the destructor would say if(thr.joinable()) thr.join();, to cover the case that someone already called join explicitly.

When should I use std::thread::detach?

In the destructor of std::thread, std::terminate is called if:

  • the thread was not joined (with t.join())
  • and was not detached either (with t.detach())

Thus, you should always either join or detach a thread before the flows of execution reaches the destructor.


When a program terminates (ie, main returns) the remaining detached threads executing in the background are not waited upon; instead their execution is suspended and their thread-local objects destructed.

Crucially, this means that the stack of those threads is not unwound and thus some destructors are not executed. Depending on the actions those destructors were supposed to undertake, this might be as bad a situation as if the program had crashed or had been killed. Hopefully the OS will release the locks on files, etc... but you could have corrupted shared memory, half-written files, and the like.


So, should you use join or detach ?

  • Use join
  • Unless you need to have more flexibility AND are willing to provide a synchronization mechanism to wait for the thread completion on your own, in which case you may use detach

C++11 std::thread giving error: no matching function to call std::thread::thread

The problem is that threadLoop is a member function, but there is no object for it to be applied to. Just guessing:

std::thread connectionThread(&Connection::threadLoop, this);

But that's just the syntactic issue; there's a logic problem, too: that line creates a local object of type std::thread that goes away when the function returns. Its destructor will call std::terminate() because the thread has not been joined. Most likely, this was supposed to attach a thread to the connectionThread member. To do that:

std::thread thr(threadLoop, this);
std::swap(thr, connectionThread);


Related Topics



Leave a reply



Submit