Β§27.1–27.2Thread Creation … Thread Completion

Part II OSTEP pp. 303–306 Β· ~6 min read

  • join

Threads exist; now, the levers. This chapter is the reference card β€” the concepts arrive slowly (with many examples) in the chapters that follow.

The Crux: How To Create And Control Threads

What interfaces should the OS present for thread creation and control? How should these interfaces be designed to enable ease of use as well as utility?

27.1 Thread Creation

In POSIX, one call:

#include <pthread.h>
int
pthread_create(pthread_t      *thread,
         const pthread_attr_t *attr,
               void           *(*start_routine)(void*),
               void           *arg);

The declaration looks complex (function pointers, if you haven’t met them, take a breath) but decomposes cleanly:

pthread_create, argument by argument
what it is
threadpthread_t * β€” the handle, initialized by the call
attrconst pthread_attr_t * β€” attributes (stack size, scheduling priority…)
start_routinevoid *(*)(void *) β€” a function pointer: where the thread begins
argvoid * β€” the argument handed to start_routine
Dotted-underlined cells have explanations β€” click one.

Packing arguments, the standard way (Figure 27.1):

typedef struct { int a; int b; } myarg_t;

void *mythread(void *arg) {
    myarg_t *args = (myarg_t *) arg;
    printf("%d %d\n", args->a, args->b);
    return NULL;
}

int main(int argc, char *argv[]) {
    pthread_t p;
    myarg_t args = { 10, 20 };
    int rc = pthread_create(&p, NULL, mythread, &args);
    ...
}

And there it is: another live executing entity, with its own call stack, in the same address space as every other thread in the program. The fun thus begins!

27.2 Thread Completion

To wait for a thread, join it:

int pthread_join(pthread_t thread, void **value_ptr);

The first argument names the thread (the handle create initialized); the second is a pointer to your void* β€” join writes the thread’s return value through it (pass NULL if you don’t care). The full round-trip (Figure 27.2):

typedef struct { int a; int b; } myarg_t;
typedef struct { int x; int y; } myret_t;

void *mythread(void *arg) {
    myret_t *rvals = Malloc(sizeof(myret_t));
    rvals->x = 1;
    rvals->y = 2;
    return (void *) rvals;
}

int main(int argc, char *argv[]) {
    pthread_t p;
    myret_t *rvals;
    myarg_t args = { 10, 20 };
    Pthread_create(&p, NULL, mythread, &args);
    Pthread_join(p, (void **) &rvals);
    printf("returned %d %d\n", rvals->x, rvals->y);
    free(rvals);
    return 0;
}

(Those capitalized Pthread_* calls are assert-on-failure wrappers β€” a convention worth copying.) For single word-sized values, skip the structs and cast straight through the pointer (Figure 27.3):

void *mythread(void *arg) {
    long long int value = (long long int) arg;
    printf("%lld\n", value);
    return (void *) (value + 1);
}

One danger deserves its own walkthrough β€” returning a pointer to the thread’s own stack:

The stack-return trap β€” and the heap-return fix
main's stackwaiting in jointhread's stackmythread() frameoops {1, 2}on the stackheap

1The thread builds its result… on its own stack

mythread declares myret_t oops as a local β€” so it lives in the thread's stack frame. The thread returns &oops. Looks plausible; compiles (with a warning you should have heeded).

step 1 / 4

Finally, two structural notes. Creating a thread and immediately joining it is a pretty strange way to create a thread β€” there’s an easier way to accomplish that exact task, called a procedure call. And not all threaded code joins at all: a web server’s main thread may dispatch to workers indefinitely, while a parallel program joins at each stage boundary to know the work is done before moving on.

Check yourself

1.Why does pthread_create take a void* argument and expect the thread routine to return a void*?

2.What does pthread_join do, and why is its second argument a void** rather than a void*?

3.The 'oops' example returns &oops, where oops is a local variable in the thread function. What happens, and what's the rule?

4.The book jokes that pthread_create followed immediately by pthread_join is 'a pretty strange way to create a thread.' What's the punchline, and the real lesson?

5.Which programs typically SKIP pthread_join altogether β€” and which must use it?

5 questions