2
votes

If I have multiple threads and I would like to wait for a thread to finish, why is this undefined according to the pthread_join() function?

For example, the code below shows thread 1 and 2 wait for thread 0:

void* thread1(void* t1){
..
pthread_join(pthread_t thread0, void **retval1);
return NULL
}

void* thread2(void* t2){
..
pthread_join(pthread_t thread0, void **retval2);
return NULL
}

Why would this behaviour be undefined or in other words not possible?

3
I think this example needs code, because you seem to have a misconception here. The joining of two threads is not a symmetric operation. I.e. it is fine if thread A1 joins B, and then thread A2 joins B. That means B waits for A1 to finish,and then waits for A2 to finish. What won't work is having B1 and B2 both wait for thread A to finish. - MSalters
The latter case is the is what I am referring to. I will include code to illustrate this. - duper21
BTW, note that POSIX also makes it impossible for thread B to wait on A1 and A2 or A1 or A2. You have to wait on A1 and then wait on A2, which is a bit of a hassle if you don't know up from which one will finish first. In comparison, Windows has WaitForMultipleObjects. This is far more flexible. - MSalters
@MSalters I am aware that threads can only wait on one thread in linux but what I don't understand is why multiple threads cannot wait on one thread to finish. What is stopping them from doing so? - duper21
Are you asking why you can't call pthread_join twice, or are you asking for a way to accomplish what you are trying to do? - pat

3 Answers

3
votes

The pthread_t object will generally point to some allocated data pertaining to the thread. Among this data will generally be the return value of the thread. pthread_join will read the return value and free the thread data.

If you pthread_join the same pthread id twice, you kind of have a case of a double-free. The pointed to data might be invalid on the second join, but it also could have been reused in an unpredictable fashion by another thread.

The results are difficult/impossible to reason about. Hence the UB.

1
votes

Basically, the retval of thread0 is stored until one thread calls pthread_join. After that, the retval is no longer available.

C++ has std::shared_future which explicitly is designed so multiple threads can wait for a single result, which shows your idea isn't strange.

1
votes

pthread_join is a resource-identifier-freeing operation, like close on file descriptors, free on allocated memory, unlink on filenames, etc. All such operations are inherently subject to "double-free"/"use-after-free" bugs if the identifier (here, the particular pattern of bits making up the pthread_t value) can be used again in the future. As such, the behavior has to be either completely undefined or subject to conditions that make it a serious programming error, to the point where it might as well be undefined.

Note that in your mental model you might be thinking of both pthread_join calls "starting before the thread exits", and thereby taking place during the time when the pthread_t identifier is still valid. However formally there is no ordering between them, nor can there be. There is no observable distinction between the state of "already blocked in pthread_join" and "just about to call pthread_join".