mirror of git://sourceware.org/git/glibc.git
manual: Document __libc_single_threaded
Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com> Reviewed-by: DJ Delorie <dj@redhat.com>
This commit is contained in:
parent
706ad1e7af
commit
01ffa6002e
|
@ -628,6 +628,7 @@ the standard.
|
||||||
* Initial Thread Signal Mask:: Setting the initial mask of threads.
|
* Initial Thread Signal Mask:: Setting the initial mask of threads.
|
||||||
* Waiting with Explicit Clocks:: Functions for waiting with an
|
* Waiting with Explicit Clocks:: Functions for waiting with an
|
||||||
explicit clock specification.
|
explicit clock specification.
|
||||||
|
* Single-Threaded:: Detecting single-threaded execution.
|
||||||
* Restartable Sequences:: Linux-specific Restartable Sequences
|
* Restartable Sequences:: Linux-specific Restartable Sequences
|
||||||
integration.
|
integration.
|
||||||
@end menu
|
@end menu
|
||||||
|
@ -845,6 +846,118 @@ Behaves like @code{pthread_timedjoin_np} except that the absolute time in
|
||||||
@var{abstime} is measured against the clock specified by @var{clockid}.
|
@var{abstime} is measured against the clock specified by @var{clockid}.
|
||||||
@end deftypefun
|
@end deftypefun
|
||||||
|
|
||||||
|
@node Single-Threaded
|
||||||
|
@subsubsection 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. @Theglibc{} 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.
|
||||||
|
|
||||||
|
@deftypevar char __libc_single_threaded
|
||||||
|
@standards{GNU, sys/single_threaded.h}
|
||||||
|
This variable is non-zero if the current process is definitely
|
||||||
|
single-threaded. If it is zero, the process may be multi-threaded,
|
||||||
|
or @theglibc{} cannot determine at this point of the program execution
|
||||||
|
whether the process is single-threaded or not.
|
||||||
|
|
||||||
|
Applications must never write to this variable.
|
||||||
|
@end deftypevar
|
||||||
|
|
||||||
|
Most applications should perform the same actions whether or not
|
||||||
|
@code{__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:
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
if (__libc_single_threaded)
|
||||||
|
atomic_fetch_add (&reference_count, 1, memory_order_relaxed);
|
||||||
|
else
|
||||||
|
atomic_fetch_add (&reference_count, 1, memory_order_acq_rel);
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
@c Note: No memory order on __libc_single_threaded. The
|
||||||
|
@c implementation must ensure that exit of the critical
|
||||||
|
@c (second-to-last) thread happens-before setting
|
||||||
|
@c __libc_single_threaded to true. Otherwise, acquire MO might be
|
||||||
|
@c needed for reading the variable in some scenarios, and that would
|
||||||
|
@c completely defeat its purpose.
|
||||||
|
|
||||||
|
This still requires some form of synchronization on the
|
||||||
|
single-threaded branch, so it can be beneficial not to declare the
|
||||||
|
reference count as @code{_Atomic}, and use the GCC @code{__atomic}
|
||||||
|
built-ins. @xref{__atomic Builtins,, Built-in Functions for Memory
|
||||||
|
Model Aware Atomic Operations, gcc, Using the GNU Compiler Collection
|
||||||
|
(GCC)}. Then the code to increment a reference count looks like this:
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
if (__libc_single_threaded)
|
||||||
|
++refeference_count;
|
||||||
|
else
|
||||||
|
__atomic_fetch_add (&reference_count, 1, __ATOMIC_ACQ_REL);
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
(Depending on the data associated with the reference count, it may be
|
||||||
|
possible to use the weaker @code{__ATOMIC_RELAXED} memory ordering on
|
||||||
|
the multi-threaded branch.)
|
||||||
|
|
||||||
|
Several functions in @theglibc{} can change the value of the
|
||||||
|
@code{__libc_single_threaded} variable. For example, creating new
|
||||||
|
threads using the @code{pthread_create} or @code{thrd_create} function
|
||||||
|
sets the variable to false. This can also happen indirectly, say via
|
||||||
|
a call to @code{dlopen}. Therefore, applications need to make a copy
|
||||||
|
of the value of @code{__libc_single_threaded} if after such a function
|
||||||
|
call, behavior must match the value as it was before the call, like
|
||||||
|
this:
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
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 ();
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
Since the value of @code{__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
|
||||||
|
@code{__libc_single_threaded} to optimize atomic operations, unless it
|
||||||
|
is able to prove that the memory is not shared.
|
||||||
|
|
||||||
|
@strong{Implementation Note:} The @code{__libc_single_threaded}
|
||||||
|
variable is not declared as @code{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 @theglibc{} does not set the
|
||||||
|
@code{__libc_single_threaded} variable to a true value if a process
|
||||||
|
turns single-threaded again. Future versions of @theglibc{} 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 @code{malloc} do not write to global variables, and setting
|
||||||
|
@code{__libc_single_threaded} would introduce a data race and
|
||||||
|
undefined behavior.) In any case, an application must not write to
|
||||||
|
@code{__libc_single_threaded} even if it has joined the last
|
||||||
|
application-created thread because future versions of @theglibc{} may
|
||||||
|
create background threads after the first thread has been created, and
|
||||||
|
the application has no way of knowning that these threads are present.
|
||||||
|
|
||||||
@node Restartable Sequences
|
@node Restartable Sequences
|
||||||
@subsubsection Restartable Sequences
|
@subsubsection Restartable Sequences
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue