C11 Multithreading: Guide to threads.h

Prior to the C11 standard, the C programming language lacked native multithreading support, requiring developers to rely on operating system-specific APIs such as POSIX Threads (pthreads) on Unix-like systems or the Win32 threading API on Windows. With the release of ISO/IEC 9899:2011 (commonly known as C11), native multithreading was integrated directly into the C language specification through the standard library header <threads.h>. This article explains the background of C11 threads, outlines the primary components provided by <threads.h>, and details how to utilize its core synchronization and execution primitives.

The Introduction of Standard Threads in C11

The multithreading library introduced in C11 is defined under the <threads.h> header. Its design is largely a simplified, platform-agnostic adaptation of the POSIX threads API, designed to provide a consistent threading interface across different operating systems.

C11 designates multithreading as an optional language feature. Conforming implementations that do not support standard multithreading define the macro __STDC_NO_THREADS__ with a value of 1. Compilers and standard C libraries supporting the feature do not define this macro and make <threads.h> available out of the box.

Core Components of <threads.h>

The C11 threading model organizes concurrent operations into four main areas: thread lifecycle management, mutual exclusion (mutexes), condition variables, and thread-specific storage.

1. Thread Management

Thread execution is handled using the thrd_t type and functions prefixed with thrd_:

  • thrd_create: Spawns a new thread that executes a function matching the signature int (*)(void *).
  • thrd_join: Blocks the calling thread until the designated thread terminates, retrieving its integer return code.
  • thrd_detach: Disconnects a thread so its resources are reclaimed automatically upon completion, without requiring a join.
  • thrd_exit: Terminates the calling thread immediately with a specified result code.
  • thrd_current: Returns the thrd_t identifier of the currently executing thread.
  • thrd_sleep: Suspends thread execution for a specified duration using struct timespec.

2. Mutexes

Mutual exclusion primitives use the mtx_t type to prevent data races on shared data:

  • mtx_init: Initializes a mutex. Flags can configure the mutex behavior, such as mtx_plain, mtx_timed, or mtx_recursive.
  • mtx_lock: Blocks until the mutex is acquired.
  • mtx_trylock: Attempts to acquire the mutex without blocking, returning thrd_busy if unavailable.
  • mtx_unlock: Releases ownership of the mutex.
  • mtx_destroy: Frees resources allocated to the mutex.

3. Condition Variables

Signaling between threads is managed with condition variables using the cnd_t type:

  • cnd_init: Initializes the condition variable.
  • cnd_wait: Atomically unlocks a specified mutex and waits until signaled.
  • cnd_signal: Unblocks at least one thread waiting on the condition variable.
  • cnd_broadcast: Unblocks all threads currently waiting on the condition variable.
  • cnd_destroy: Cleans up the condition variable.

4. Thread-Specific Storage (TSS)

Thread-specific data storage allows threads to maintain separate values under a shared identifier using tss_t:

  • tss_create: Creates a storage key, optionally binding a destructor function invoked at thread termination.
  • tss_set: Sets the thread-local pointer value associated with the key.
  • tss_get: Retrieves the thread-local pointer value for the key.
  • tss_delete: Destroys the storage key.

Integration with C11 Memory Model

Alongside <threads.h>, C11 introduced the memory model and atomic operations library under <stdatomic.h>. This standard memory model formally defines data races, synchronization points, and memory ordering semantics (memory_order_relaxed, memory_order_acquire, memory_order_release, and memory_order_seq_cst). The combination of standard threads and atomic primitives allows portable, lock-free, and thread-safe systems programming natively in C.