Single-Threaded (The GNU C Library)

#### 36.2.9.6 Detecting Single-Threaded Execution ¶

Multi-threaded programs require synchronization among threads. This synchronization can be costly even if there is just a single thread and no data is shared between multiple processors. The GNU C Library offers an interface to detect whether the process is in single-threaded mode. Applications can use this information to avoid synchronization, for example by using regular instructions to load and store memory instead of atomic instructions, or using relaxed memory ordering instead of stronger memory ordering.

Variable: `char` __libc_single_threaded ¶

  This variable is non-zero if the current process is definitely single-threaded. If it is zero, the process may be multi-threaded, or the GNU C Library cannot determine at this point of the program execution whether the process is single-threaded or not.

  Applications must never write to this variable.

Most applications should perform the same actions whether or not `__libc_single_threaded` is true, except with less synchronization. If this rule is followed, a process that subsequently becomes multi-threaded is already in a consistent state. For example, in order to increment a reference count, the following code can be used:

    if (__libc_single_threaded)
      atomic_fetch_add (&reference_count, 1, memory_order_relaxed);
    else
      atomic_fetch_add (&reference_count, 1, memory_order_acq_rel);

This still requires some form of synchronization on the single-threaded branch, so it can be beneficial not to declare the reference count as `_Atomic`, and use the GCC `__atomic` built-ins. See Built-in Functions for Memory Model Aware Atomic Operations in Using the GNU Compiler Collection (GCC). Then the code to increment a reference count looks like this:

    if (__libc_single_threaded)
      ++reference_count;
    else
      __atomic_fetch_add (&reference_count, 1, __ATOMIC_ACQ_REL);

(Depending on the data associated with the reference count, it may be possible to use the weaker `__ATOMIC_RELAXED` memory ordering on the multi-threaded branch.)

Several functions in the GNU C Library can change the value of the `__libc_single_threaded` variable. For example, creating new threads using the `pthread_create` or `thrd_create` function sets the variable to false. This can also happen indirectly, say via a call to `dlopen`. Therefore, applications need to make a copy of the value of `__libc_single_threaded` if after such a function call, behavior must match the value as it was before the call, like this:

    bool single_threaded = __libc_single_threaded;
    if (single_threaded)
      prepare_single_threaded ();
    else
      prepare_multi_thread ();

    void *handle = dlopen (shared_library_name, RTLD_NOW);
    lookup_symbols (handle);

    if (single_threaded)
      cleanup_single_threaded ();
    else
      cleanup_multi_thread ();

Since the value of `__libc_single_threaded` can change from true to false during the execution of the program, it is not useful for selecting optimized function implementations in IFUNC resolvers.

Atomic operations can also be used on mappings shared among single-threaded processes. This means that a compiler must not use `__libc_single_threaded` to optimize atomic operations, unless it is able to prove that the memory is not shared.

Implementation Note: The `__libc_single_threaded` variable is not declared as `volatile` because it is expected that compilers optimize a sequence of single-threaded checks into one check, for example if several reference counts are updated. The current implementation in the GNU C Library does not set the `__libc_single_threaded` variable to a true value if a process turns single-threaded again. Future versions of the GNU C Library may do this, but only as the result of function calls which imply an acquire (compiler) barrier. (Some compilers assume that well-known functions such as `malloc` do not write to global variables, and setting `__libc_single_threaded` would introduce a data race and undefined behavior.) In any case, an application must not write to `__libc_single_threaded` even if it has joined the last application-created thread because future versions of the GNU C Library may create background threads after the first thread has been created, and the application has no way of knowing that these threads are present.