Is it valid to call pthread_join on the main thread?
Asked Answered
T

4

9

Is the behavior of this code well-defined?

#include <stdio.h>
#include <pthread.h>

pthread_t mt;

void *start(void *x)
{
    void *y;
    pthread_join(mt, &y);
    printf("joined main thread\n");
    return 0;
}

int main()
{
    pthread_t t;
    mt = pthread_self();
    pthread_create(&t, 0, start, 0);
    pthread_exit(0);
}
Teressaterete answered 19/11, 2010 at 18:31 Comment(3)
I would modify this program by merely detaching the main thread rather than exiting it -- I think that would provide a better confirmation. Then have the main thread sleep before exiting or return a value with pthread_exit and confirm that pthread_join is picking it up.Komarek
Check the return value of that join -- I don't think it is actually working. (OK, sorry, only doesn't work when I try to detach the main thread -- which makes sense b/c I'm trying to join a detached thread.)Komarek
@AlexLeibowitz: Trying to join a detached thread is undefined behavior, not necessarily a reportable error. So, a very bad idea. And the whole point of the question was essentially whether the main thread is guaranteed not to start detached (in which case the call would not be valid, because it'd be UB).Teressaterete
B
11

Yes, this is possible. Indeed, this possibility is one of the main reasons why pthread_detach() exists. From the POSIX docs for pthread_detach() (see man pthread_detach):

   It  has  been suggested that a "detach" function is not necessary; the
   detachstate thread creation attribute is sufficient,  since  a  thread
   need  never  be dynamically detached. However, need arises in at least
   two cases:

    1. In a cancellation handler for a pthread_join() it is nearly essen-
       tial  to  have  a pthread_detach() function in order to detach the
       thread on which pthread_join() was waiting. Without it,  it  would
       be  necessary  to  have  the  handler do another pthread_join() to
       attempt to detach the thread, which would both delay the cancella-
       tion  processing  for an unbounded period and introduce a new call
       to pthread_join(), which might itself need a cancellation handler.
       A dynamic detach is nearly essential in this case.


    2. In  order  to  detach the "initial thread" (as may be desirable in
       processes that set up server threads).

So what you're proposing is fine according to the standards.

Edit: Just to confirm that further, the POSIX description of exec() states:

The initial thread in the new process image shall be joinable, as if created with the detachstate attribute set to PTHREAD_CREATE_JOINABLE.

Bev answered 19/11, 2010 at 19:3 Comment(2)
Would you consider adding a link to your source for the last edit? I believe you got it from over here: pubs.opengroup.org/onlinepubs/009695399/functions/exec.html, but I wanted to confirm first. Great answer, though!Ambrogino
@currysensei: Added!Bev
K
1

This modification to your code seems to confirm that you are successfully joining the main_thread from another thread:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

pthread_t mt;

void *start(void *x)
{
    void *y;
    int val = pthread_join(mt, &y);
    if (val != 0) {
        printf("Error when joining: %s\n", strerror(val));
    } else {
        printf("joined main thread with return %d\n", (int) y);
    }
    return 0;
}

int main()
{
    pthread_t t;
    mt = pthread_self();
    pthread_create(&t, 0, start, 0);
    sleep(1);
    
    printf("main thread done\n");
    pthread_exit((void*) 5);
}
Komarek answered 27/8, 2022 at 19:14 Comment(0)
T
0

I can't imagine why you would do this in a real-world application (someone please comment an example if you can think of one), but I don't believe it is even possible. All of the searches regarding pthreads that I have looked at always have the join being called from the main thread, not a secondary thread.

The join also requires that the thread you are trying to join is created with the PTHREAD_CREATE_JOINABLE flag. I have not been able to find any documentation stating whether or not the main thread is created as such.

Codeguru has a similar question on it here which may or may not help clear it up.

If what you want to accomplish is the child thread continuing after the main thread has exited, you should create the child thread detached, or use fork/vfork instead, depending on what platform you're on.

Toitoiboid answered 19/11, 2010 at 18:59 Comment(2)
PTHREAD_CREATE_JOINABLE is the default according to POSIX; you don't need to use a pthread_attr_t to obtain it.Teressaterete
By the way, I agree that this is probably not a good practice. I asked the question from a standpoint of whether an implementation must support this dubious usage, not whether an application writer should use it. :-)Teressaterete
P
0

Your approach looks formally valid to me, in particular since you are doing the pthread_exit at the end of main.

On the other hand I didn't find any indication in POSIX whether or not the initial thread of main should be joinable or not. The rationale in psmears' answer only asks that main should be detachable whence it is created joinable.

Also something else malicious might call pthread_detach from a subroutine called by main or so. So you should definitively check the return value of pthread_join to check for such a case.

Also in your example code you have a race between the creation of start and the termination of main:

  • main might be terminated before start arrives at pthread_join

Mutexing the access to the mt variable should capure that.

Pauperism answered 19/11, 2010 at 22:33 Comment(5)
If pthread_detach has already been called on a thread, the result of calling pthread_join on that thread is undefined; it need not return any error, and in fact it might crash if the thread has already terminated and its resources were deallocated.Teressaterete
Assuming the main thread is joinable, there's no problem if main calls pthread_exit before start calls pthread_join. The semantics of a joinable thread are like those of a pid for a child process. Just like the pid is reserved (zombie process) until you wait for it and collect its exit status, the pthread_t is valid until you detach or join the thread, even if it has already exited.Teressaterete
I don't agree with your first remark. A non-joinable thread is detectable and doesn't lead to an undefined result. The pthread_join() function shall fail if: *EINVAL* The implementation has detected that the value specified by thread does not refer to a joinable thread.Pauperism
R. is correct. The wording of the standard is confusing - but it says that pthread_join() shall fail if the implementation detects an invalid thread id. It doesn't say that the implementation must be able to detect such an invalid thread id, and indeed glibc on Linux can and will crash in these circumstances: see udrepper.livejournal.com/tag/programming%20posixBev
POSIX 2008 makes this much more clear and changes all those bogus shall fail statements for undetectable conditions (many of which involve the values of uninitialized variables) to may fail. It's not that unreasonable (although POSIX 2008 makes it UB) to expect pthread_join to return EINVAL on a detached thread if it is still running, but if the thread has terminated, you should expect all hell to break loose. SIGSEGV if you're lucky, and randomly joining or corrupting a different thread if the thread id got reassigned or memory reallocated for something else.Teressaterete

© 2022 - 2024 — McMap. All rights reserved.