POSIX threads provide concurrency in one process and one address space. Shared address space does not make shared mutable state safe: mutexes, condition variables, or C atomics must establish ownership and memory visibility.
Creation, Termination, and Cancellation #
pthread_create starts void *(*start_routine)(void *) with one pointer argument. It returns an error number directly, not through errno. A thread terminates by returning from its start routine, calling pthread_exit, or acting on cancellation; returning is equivalent to pthread_exit with that return value. Calling exit or returning from main terminates the entire process, whereas pthread_exit in the initial thread leaves other threads running.
Joinable threads retain their exit value and resources until another thread successfully calls pthread_join. Detached threads release those resources at termination and cannot be joined. Every created thread therefore needs a defined join-or-detach owner. A thread must not join itself, and application logic must ensure only one thread joins a given target.
void *worker(void *arg) {
return arg;
}
pthread_t thread;
int error = pthread_create(&thread, NULL, worker, value);
if (error == 0) {
void *result;
error = pthread_join(thread, &result);
}Cancellation is enabled and deferred by default. pthread_cancel requests cancellation; it does not guarantee immediate termination. Calls designated as cancellation points, including condition waits, can act on the request. Code that acquires resources across a cancellation point installs pthread_cleanup_push handlers or otherwise establishes cleanup discipline; asynchronous cancellation is inappropriate for code that manipulates invariants.
Attributes and Resource Bounds #
Initialize a pthread_attr_t when creation needs nondefault behavior, then destroy the attribute object after pthread_create returns. pthread_attr_setdetachstate chooses joinable or detached creation. pthread_attr_setstacksize, pthread_attr_setstack, and pthread_attr_setguardsize control stack allocation and guard pages; an application-supplied stack must remain mapped and exclusively usable until the thread has terminated.
Scheduling inheritance, policy, priority parameters, and contention scope are also attributes. Support for requested scheduling policy and privilege to set it are implementation-dependent. Linux-specific interfaces such as pthread_attr_setaffinity_np should be isolated behind a platform boundary. Every thread consumes stack address space and runtime metadata, so unbounded per-request thread creation is a resource-exhaustion design.
Mutexes and Condition Variables #
A condition variable represents a change to a predicate protected by a mutex, not the predicate itself. Test the predicate while holding the mutex; wait in a loop; make the predicate true or false while holding that same mutex; then signal or broadcast.
pthread_mutex_lock(&mutex);
while (!ready)
pthread_cond_wait(&condition, &mutex);
consume();
pthread_mutex_unlock(&mutex);pthread_cond_wait atomically releases the mutex and begins waiting with respect to another thread that acquires the mutex and signals the condition. It reacquires the mutex before it returns. That atomic handoff prevents a predicate update from being lost between checking it and blocking. The loop remains necessary because wakeups can be spurious and because another consumer can change the predicate before the awakened thread reacquires the mutex.
pthread_cond_timedwait takes an absolute deadline. Its default clock is CLOCK_REALTIME; set the condition attribute clock to CLOCK_MONOTONIC before initialization when wall-clock adjustments must not affect timeouts. A timeout or cancellation does not eliminate the obligation to release the mutex after the function has reacquired it.
Mutex unlock and a later successful lock establish the synchronization needed to publish protected state. Condition signaling does not replace the mutex, and unsynchronized reads of non-atomic shared objects remain data races. Use atomics only with an explicitly designed memory-order protocol.
Process-Shared Objects #
PTHREAD_PROCESS_SHARED asks an implementation to make a mutex or condition variable usable across processes. The object and its associated state must reside in a genuinely shared mapping, such as shm_open plus MAP_SHARED, rather than separately allocated memory at equal virtual addresses. Check attribute and initialization errors because process-shared support is optional for some object types and environments. Do not destroy a mutex or condition variable until no thread or process can still use it.
The POSIX condition-variable specification and pthreads(7) specify the API and Linux behavior.
Comments
Read or comment on GitHub (sign-in required to comment)