Linux System Programming · intermediate · ~10 min

Returning data from threads

Return a value from a thread back to the thread that joins it.

Lesson

How a thread returns a value

A thread function returns a void * (a generic pointer that can point to any type). The thread that calls pthread_join passes a void ** to receive that pointer back.

The key rule: the returned pointer must stay valid after the thread exits. When a thread ends, its stack is destroyed. Any pointer into that stack becomes invalid.

Safe ways to return data

  • Cast a small value into the pointer (for example, (void *)(intptr_t)42). This is safe for integers that fit in a pointer. intptr_t is an integer type guaranteed to hold a pointer value.
  • Return a static or global variable. This works, but it is not reentrant: every thread shares the same storage, so concurrent threads would overwrite each other.
  • Return a malloc'd struct. The pointer stays valid after the thread exits. The joiner is then responsible for calling free.

What never works

  • Returning the address of a stack (local) variable is always wrong. The thread's stack is gone the moment it exits, so the pointer dangles.

Code examples

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

typedef struct { int sum; int count; } result_t;

static void *worker(void *arg) {
    int n = (int)(intptr_t)arg;
    result_t *r = malloc(sizeof *r);
    r->sum = n * (n + 1) / 2;
    r->count = n;
    return r;
}

int main(void) {
    pthread_t t;
    pthread_create(&t, NULL, worker, (void *)(intptr_t)10);

    void *raw;
    pthread_join(t, &raw);
    result_t *r = raw;
    printf("sum=%d count=%d\n", r->sum, r->count);
    free(r);
    return 0;
}

Common mistakes

  • Returning a stack address. The thread's stack is invalid after it exits, so the returned pointer dangles.
  • Forgetting to free a malloc'd return value on the joiner side. This causes a memory leak.

Summary

  • A thread returns a void *; the joiner receives it through a void ** passed to pthread_join.
  • The returned pointer must stay valid after the thread exits.
  • Safe options: a value-cast integer, a global/static, or a malloc'd buffer the joiner frees.
  • Never return the address of a stack-local variable.

Practice with these exercises