концепции безопасности POSIX (POSIX safety concepts)
Имя (Name)
attributes - POSIX safety concepts
Описание (Description)
Note: the text of this man page is based on the material taken
from the "POSIX Safety Concepts" section of the GNU C Library
manual. Further details on the topics described here can be
found in that manual.
Various function manual pages include a section ATTRIBUTES that
describes the safety of calling the function in various contexts.
This section annotates functions with the following safety
markings:
MT-Safe
MT-Safe or Thread-Safe functions are safe to call in the
presence of other threads. MT, in MT-Safe, stands for
Multi Thread.
Being MT-Safe does not imply a function is atomic, nor
that it uses any of the memory synchronization mechanisms
POSIX exposes to users. It is even possible that calling
MT-Safe functions in sequence does not yield an MT-Safe
combination. For example, having a thread call two MT-
Safe functions one right after the other does not
guarantee behavior equivalent to atomic execution of a
combination of both functions, since concurrent calls in
other threads may interfere in a destructive way.
Whole-program optimizations that could inline functions
across library interfaces may expose unsafe reordering,
and so performing inlining across the GNU C Library
interface is not recommended. The documented MT-Safety
status is not guaranteed under whole-program optimization.
However, functions defined in user-visible headers are
designed to be safe for inlining.
MT-Unsafe
MT-Unsafe functions are not safe to call in a
multithreaded programs.
Other keywords that appear in safety notes are defined in
subsequent sections.
Conditionally safe features
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 indicates 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.
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.
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, which causes both readers and writers of
internal objects to be regarded as MT-Unsafe, this mark is
applied to writers only. Writers remain MT-Unsafe to
call, but the then-mandatory constness of objects they
modify enables readers to be regarded as MT-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-recursive read-write lock 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.
sig Functions marked with sig as a MT-Safety issue 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.
term Functions marked with term as an MT-Safety issue may
change the terminal settings in the recommended way,
namely: call tcgetattr(3), modify some flags, and then
call tcsetattr(3), this creates a window in which changes
made by other threads are lost. Thus, functions marked
with term are MT-Unsafe.
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.
Other safety remarks
Additional keywords may be attached to functions, indicating
features that do not make a function unsafe to call, but that may
need to be taken into account in certain classes of programs:
locale Functions annotated with locale as an MT-Safety issue read
from the locale object without any form of
synchronization. Functions annotated with locale called
concurrently with locale changes may behave in ways that
do not correspond to any of the locales active during
their execution, but an unpredictable mix thereof.
We do not mark these functions as MT-Unsafe, however,
because functions that modify the locale object are marked
with const:locale and regarded as unsafe. Being unsafe,
the latter are not to be called when multiple threads are
running or asynchronous signals are enabled, and so the
locale can be considered effectively constant in these
contexts, which makes the former safe.
env Functions marked with env as an MT-Safety issue access the
environment with getenv(3) or similar, without any guards
to ensure safety in the presence of concurrent
modifications.
We do not mark these functions as MT-Unsafe, however,
because functions that modify the environment are all
marked with const:env and regarded as unsafe. Being
unsafe, the latter are not to be called when multiple
threads are running or asynchronous signals are enabled,
and so the environment can be considered effectively
constant in these contexts, which makes the former safe.
hostid The function marked with hostid as an MT-Safety issue
reads from the system-wide data structures that hold the
"host ID" of the machine. These data structures cannot
generally be modified atomically. Since it is expected
that the "host ID" will not normally change, the function
that reads from it (gethostid(3)) is regarded as safe,
whereas the function that modifies it (sethostid(3)) is
marked with const:hostid, indicating it may require
special care if it is to be called. In this specific
case, the special care amounts to system-wide (not merely
intra-process) coordination.
sigintr
Functions marked with sigintr as an MT-Safety issue access
the GNU C Library _sigintr internal data structure without
any guards to ensure safety in the presence of concurrent
modifications.
We do not mark these functions as MT-Unsafe, however,
because functions that modify this data structure are all
marked with const:sigintr and regarded as unsafe. Being
unsafe, the latter are not to be called when multiple
threads are running or asynchronous signals are enabled,
and so the data structure can be considered effectively
constant in these contexts, which makes the former safe.
cwd Functions marked with cwd as an MT-Safety issue may
temporarily change the current working directory during
their execution, which may cause relative pathnames to be
resolved in unexpected ways in other threads or within
asynchronous signal or cancellation handlers.
This is not enough of a reason to mark so-marked functions
as MT-Unsafe, but when this behavior is optional (e.g.,
nftw(3) with FTW_CHDIR
), avoiding the option may be a good
alternative to using full pathnames or file descriptor-
relative (e.g., openat(2)) system calls.
:identifier
Annotations may sometimes be followed by identifiers,
intended to group several functions that, for example,
access the data structures in an unsafe way, as in race
and const, or to provide more specific information, such
as naming a signal in a function marked with sig. It is
envisioned that it may be applied to lock and corrupt as
well in the future.
In most cases, the identifier will name a set of
functions, but it may name global objects or function
arguments, or identifiable properties or logical
components associated with them, with a notation such as,
for example, :buf(arg) to denote a buffer associated with
the argument arg, or :tcattr(fd) to denote the terminal
attributes of a file descriptor fd.
The most common use for identifiers is to provide logical
groups of functions and arguments that need to be
protected by the same synchronization primitive in order
to ensure safe operation in a given context.
/condition
Some safety annotations may be conditional, in that they
only apply if a boolean expression involving arguments,
global variables or even the underlying kernel evaluates
to true. For example, /!ps and /one_per_line indicate the
preceding marker only applies when argument ps is NULL, or
global variable one_per_line is nonzero.
When all marks that render a function unsafe are adorned
with such conditions, and none of the named conditions
hold, then the function can be regarded as safe.