Concurrency in glibc

Memory model and atomic operations

We basically use the C11 memory model, with the following differences to pure C11 code:

Data-race-freedom, as defined by C11, is the default, and a requirement for new code. Old code may not satisfy this requirement (yet). We currently allow non-atomic initialization, as long as it happens-before all atomic accesses; however, new code should avoid non-atomic initialization if possible.

Naming scheme and currently provided functions

For a C11 function with the name atomic_OP_explicit(args, memory_order_MO), the equivalent glibc function has the name atomic_OP_MO(args). There are two small exceptions. First, we assume atomic_compare_exchange operations to only use memory_order_relaxed on the failure path, and thus only the requested memory order on the success path is part of the function name. Second, C11's atomic_thread_fence function does not have _explicit as a suffix in its name, so we just add the memory order without removing the suffix (see below).

Examples:

C11 code

glibc code

atomic_load_explicit(&x, memory_order_relaxed)

atomic_load_relaxed(&x)

atomic_store_explicit(&x, 1, memory_order_release)

atomic_store_release(&x, 1)

atomic_thread_fence(memory_order_acquire)

atomic_thread_fence_acquire()

atomic_compare_exchange_weak_explicit(&x, &expected, desired, memory_order_release, memory_order_relaxed)

atomic_compare_exchange_weak_release(&x, &expected, desired) (the failure path on CAS is always memory_order_relaxed)

atomic_fetch_and_explicit(&x, operand, memory_order_relaxed)

atomic_fetch_and_relaxed(&x, operand)

Across all architectures, we only provide atomic operations for naturally aligned unsigned/int and naturally-aligned pointers.

To see which atomic operations we currently define, open include/atomic.h and look for #if USE_ATOMIC_COMPILER_BUILTINS; the declarations that follow until the matching #else are what we currently provide.

Documentation guidelines

Concurrent code is often complex, and how it works can usually not be understood simply by going through it sequentially (unless you state space explosion is not an issue for your brain). We want to optimize for the readers of code, not for the authors; therefore, we want verbose documentation of the synchronization scheme. While this creates redundancy, we assume this helps in that it clarifies the intent of the author, and makes it easier for readers to see the intent and the high-level concurrent algorithm and cross-check between the intent and the actual code.

The high-level synchronization scheme of groups of functions that synchronize or coordinate with each other in some way should get documented. This will often include the following information:

This information should be on one of the functions in each group; the other functions in the group should explicitly refer to it in their documentation.

Code with locks

TODO

Code with atomics

If you use atomics, please make sure you are familiar with the C11 memory model.

Use the terms from the formalization of the C11 memory model by Batty et al.:

Document at least the following, unless this is trivial:

Terminology:

Things to look out for:

Development TODO

Below is a list of further steps towards transitioning existing glibc code to a state that fulfills the requirements laid out above. This is not a complete list of tasks but rather reminders and steps that have already been identified:

None: Concurrency (last edited 2017-10-06 06:27:29 by FlorianWeimer)