For some features that make functions unsafe to call in certain contexts, there are known ways to avoid the safety problem other than refraining from calling the function altogether. The keywords that follow refer to such features, and each of their definitions indicate how the whole program needs to be constrained in order to remove the safety problem indicated by the keyword. Only when all the reasons that make a function unsafe are observed and addressed, by applying the documented constraints, does the function become safe to call in a context.
init
Functions marked with init
as an MT-Unsafe feature perform
MT-Unsafe initialization when they are first called.
Calling such a function at least once in single-threaded mode removes this specific cause for the function to be regarded as MT-Unsafe. If no other cause for that remains, the function can then be safely called after other threads are started.
Functions marked with init
as an AS- or AC-Unsafe feature use the
internal libc_once
machinery or similar to initialize internal
data structures.
If a signal handler interrupts such an initializer, and calls any
function that also performs libc_once
initialization, it will
deadlock if the thread library has been loaded.
Furthermore, if an initializer is partially complete before it is canceled or interrupted by a signal whose handler requires the same initialization, some or all of the initialization may be performed more than once, leaking resources or even resulting in corrupt internal data.
Applications that need to call functions marked with init
as an
AS- or AC-Unsafe feature should ensure the initialization is performed
before configuring signal handlers or enabling cancellation, so that the
AS- and AC-Safety issues related with libc_once
do not arise.
race
Functions annotated with race
as an MT-Safety issue operate on
objects in ways that may cause data races or similar forms of
destructive interference out of concurrent execution. In some cases,
the objects are passed to the functions by users; in others, they are
used by the functions to return values to users; in others, they are not
even exposed to users.
We consider access to objects passed as (indirect) arguments to
functions to be data race free. The assurance of data race free objects
is the caller’s responsibility. We will not mark a function as
MT-Unsafe or AS-Unsafe if it misbehaves when users fail to take the
measures required by POSIX to avoid data races when dealing with such
objects. As a general rule, if a function is documented as reading from
an object passed (by reference) to it, or modifying it, users ought to
use memory synchronization primitives to avoid data races just as they
would should they perform the accesses themselves rather than by calling
the library function. FILE
streams are the exception to the
general rule, in that POSIX mandates the library to guard against data
races in many functions that manipulate objects of this specific opaque
type. We regard this as a convenience provided to users, rather than as
a general requirement whose expectations should extend to other types.
In order to remind users that guarding certain arguments is their
responsibility, we will annotate functions that take objects of certain
types as arguments. We draw the line for objects passed by users as
follows: objects whose types are exposed to users, and that users are
expected to access directly, such as memory buffers, strings, and
various user-visible struct
types, do not give reason for
functions to be annotated with race
. It would be noisy and
redundant with the general requirement, and not many would be surprised
by the library’s lack of internal guards when accessing objects that can
be accessed directly by users.
As for objects that are opaque or opaque-like, in that they are to be
manipulated only by passing them to library functions (e.g.,
FILE
, DIR
, obstack
, iconv_t
), there might be
additional expectations as to internal coordination of access by the
library. We will annotate, with race
followed by a colon and the
argument name, functions that take such objects but that do not take
care of synchronizing access to them by default. For example,
FILE
stream unlocked
functions will be annotated, but
those that perform implicit locking on FILE
streams by default
will not, even though the implicit locking may be disabled on a
per-stream basis.
In either case, we will not regard as MT-Unsafe functions that may access user-supplied objects in unsafe ways should users fail to ensure the accesses are well defined. The notion prevails that users are expected to safeguard against data races any user-supplied objects that the library accesses on their behalf.
This user responsibility does not apply, however, to objects controlled
by the library itself, such as internal objects and static buffers used
to return values from certain calls. When the library doesn’t guard
them against concurrent uses, these cases are regarded as MT-Unsafe and
AS-Unsafe (although the race
mark under AS-Unsafe will be omitted
as redundant with the one under MT-Unsafe). As in the case of
user-exposed objects, the mark may be followed by a colon and an
identifier. The identifier groups all functions that operate on a
certain unguarded object; users may avoid the MT-Safety issues related
with unguarded concurrent access to such internal objects by creating a
non-recursive mutex related with the identifier, and always holding the
mutex when calling any function marked as racy on that identifier, as
they would have to should the identifier be an object under user
control. The non-recursive mutex avoids the MT-Safety issue, but it
trades one AS-Safety issue for another, so use in asynchronous signals
remains undefined.
When the identifier relates to a static buffer used to hold return
values, the mutex must be held for as long as the buffer remains in use
by the caller. Many functions that return pointers to static buffers
offer reentrant variants that store return values in caller-supplied
buffers instead. In some cases, such as tmpname
, the variant is
chosen not by calling an alternate entry point, but by passing a
non-NULL
pointer to the buffer in which the returned values are
to be stored. These variants are generally preferable in multi-threaded
programs, although some of them are not MT-Safe because of other
internal buffers, also documented with race
notes.
const
Functions marked with const
as an MT-Safety issue non-atomically
modify internal objects that are better regarded as constant, because a
substantial portion of the GNU C Library accesses them without
synchronization. Unlike race
, that causes both readers and
writers of internal objects to be regarded as MT-Unsafe and AS-Unsafe,
this mark is applied to writers only. Writers remain equally MT- and
AS-Unsafe to call, but the then-mandatory constness of objects they
modify enables readers to be regarded as MT-Safe and AS-Safe (as long as
no other reasons for them to be unsafe remain), since the lack of
synchronization is not a problem when the objects are effectively
constant.
The identifier that follows the const
mark will appear by itself
as a safety note in readers. Programs that wish to work around this
safety issue, so as to call writers, may use a non-recursve
rwlock
associated with the identifier, and guard all calls
to functions marked with const
followed by the identifier with a
write lock, and all calls to functions marked with the identifier
by itself with a read lock. The non-recursive locking removes the
MT-Safety problem, but it trades one AS-Safety problem for another, so
use in asynchronous signals remains undefined.
sig
Functions marked with sig
as a MT-Safety issue (that implies an
identical AS-Safety issue, omitted for brevity) may temporarily install
a signal handler for internal purposes, which may interfere with other
uses of the signal, identified after a colon.
This safety problem can be worked around by ensuring that no other uses of the signal will take place for the duration of the call. Holding a non-recursive mutex while calling all functions that use the same temporary signal; blocking that signal before the call and resetting its handler afterwards is recommended.
There is no safe way to guarantee the original signal handler is restored in case of asynchronous cancellation, therefore so-marked functions are also AC-Unsafe.
Besides the measures recommended to work around the MT- and AS-Safety problem, in order to avert the cancellation problem, disabling asynchronous cancellation and installing a cleanup handler to restore the signal to the desired state and to release the mutex are recommended.
term
Functions marked with term
as an MT-Safety issue may change the
terminal settings in the recommended way, namely: call tcgetattr
,
modify some flags, and then call tcsetattr
; this creates a window
in which changes made by other threads are lost. Thus, functions marked
with term
are MT-Unsafe. The same window enables changes made by
asynchronous signals to be lost. These functions are also AS-Unsafe,
but the corresponding mark is omitted as redundant.
It is thus advisable for applications using the terminal to avoid
concurrent and reentrant interactions with it, by not using it in signal
handlers or blocking signals that might use it, and holding a lock while
calling these functions and interacting with the terminal. This lock
should also be used for mutual exclusion with functions marked with
race:tcattr(fd)
, where fd is a file descriptor for
the controlling terminal. The caller may use a single mutex for
simplicity, or use one mutex per terminal, even if referenced by
different file descriptors.
Functions marked with term
as an AC-Safety issue are supposed to
restore terminal settings to their original state, after temporarily
changing them, but they may fail to do so if cancelled.
Besides the measures recommended to work around the MT- and AS-Safety problem, in order to avert the cancellation problem, disabling asynchronous cancellation and installing a cleanup handler to restore the terminal settings to the original state and to release the mutex are recommended.