← Interlude: Thread API
Thread completion: pthread_join § 27.3 · pp. 290-294

pthread_join() is the natural counterpart to create — it waits for a thread to finish and gives you back what it returned. Let’s check whether you’ve got the lifecycle right.

Q1 of 5

What does pthread_join() block on?

The thread to finish

Correct

Yep — it blocks the caller until the named thread terminates. Its second argument is a void ** that receives the thread’s return value.

Q2 of 5

If two threads both try to join the same thread, what happens?

They both wait until it finishes

Wrong

Actually, pthread_join on an already-joined (or being-joined) thread is undefined behavior. Only one joiner per thread.

Q3 of 5

What’s the second argument to pthread_join — void **retval — used for?

It receives the thread's return value

Correct

Right. The joinee returned a void *; pthread_join writes that pointer into *retval so the caller can pick it up. Pass NULL if you don’t care about the return.

Q4 of 5

If a thread’s start routine returns NULL, what does pthread_join see in *retval?

NULL — that's a perfectly valid return value, same as any other pointer

Correct

Exactly. NULL isn’t ‘no return’; it’s just the value (void *)0. pthread_join faithfully writes whatever the thread returned, including NULL.

Q5 of 5

Why might you call pthread_detach instead of pthread_join?

If you don't care about the thread's return value, detach lets the runtime reclaim its resources automatically when it exits — no need to remember to join.

Correct

Spot on. Detached threads are ‘fire and forget’. Forgetting to join a joinable thread is a resource leak; detaching upfront avoids that whole class of bug.

Nice work — 4 of 5 correct. Re-study any time to retry the same questions.

Thread completion: pthread_join

pthread_join

  • Signature: int pthread_join(pthread_t t, void **retval);
  • Blocks the caller until thread t terminates.
  • *retval receives whatever the start routine returned (a void *).
  • Pass NULL as the second arg if you don’t care about the return value.

One joiner per thread

  • Each joinable thread can be joined exactly once. A second join is undefined behavior — typically returns garbage or crashes.
  • Two threads concurrently joining the same target is the same bug.
  • The OS won’t stop you — this is the kind of thing you need a habit to enforce, not a check at the API boundary.

Returning values out

void *worker(void *arg) {
  result_t *r = malloc(sizeof *r);
  r->status = OK;
  return r;                 // void * out
}

void *raw;
pthread_join(t, &raw);
result_t *r = raw;          // cast back
  • The thread returns a pointer; pthread_join writes that pointer into *retval.
  • NULL is just (void *)0 — perfectly legitimate, not “no return”.
  • Anything pointing at the joinee’s stack is invalid (see ch27-1).

Detach: skip the join

  • pthread_detach(t) tells the runtime “I won’t be joining this; clean it up yourself when it exits.”
  • Useful for fire-and-forget background work (logging, telemetry, request handlers that respond and forget).
  • Forgetting to join a joinable thread is a resource leak; detaching upfront eliminates that whole class of bug.

Quiz yourself

  • What goes wrong if two callers both try to pthread_join the same thread?
  • When should you prefer pthread_detach over pthread_join?