This document proposes to allow implementations to use thread storage duration for the internal state used by the pseudo-random number generator functions and the multibyte/wide conversion functions. It is related to papers N2225, N2226, N2228.
The core observation which motivates this document is that while the standard does not require to detect data races for the affected functions, it does rule out an implementation based on internal state objects with thread storage duration. The reason is that a strictly conforming program can use external synchronization and call these functions from different threads. With internal objects of thread storage duration, the lack of global state sharing would be observable.
Many libraries which are thread-aware (because they use
synchronization primitives or even create threads
themselves) still call rand
, which introduces
data races. One way to fix all these libraries is to give
the random number generator state optional thread storage
duration.
Another option would be to encourage implementations to avoid data races, either by using locks or atomic memory accesses internally. However, historically, some benchmarks are quite dependent on random number generator performance, so this change might not be desirable to implementors. The implementation in the GNU C library uses a lock.
POSIX suggests the possiblity of a thread-safe
implementation of rand
and srand
,
but does not voice any preference for per-thread state or
synchronized access to the global state.
Likewise, the multibyte/wide character and string conversion
functions use internal state if the ps
argument
is a null pointer. For these functions, eliminating the data
race would still leave a semantic race condition because the
state needs to remain consistent between subsequent calls of
these functions.
The proposal is to give permission to assign thread storage duration to the internal state. The functions have error return values, so the necessary storage could even be allocated dynamically, on demand, to avoid penalizing programs which use an explicit conversion state argument.
A simpler approach would make it undefined to call the
conversion functions from a multi-threaded program (similar
to what the signal
function does today).
POSIX suggests that a thread-safe implementation of these functions is possible, but does not go into details. It does not discuss the ability of strictly conforming programs to discover whether the hidden internal state has thread storage duration or not.
rand
function), add:
The rand
function is not required to avoid data
races with other calls to pseudo-random sequence generation functions.
It is implementation-defined whether the internal random
number generator state has thread storage duration, and if
a newly created thread continues the same sequence of
random numbers as the current thread, or if starts again
based on the specified seed.
In 7.22.2.2 (The rand
function), change:
If the pseudo-random number generator state has thread-storage duration, theIn J.3.12 (Library functions), add:srand
function affects subsequent results of therand
function called by the current thread. Otherwise, theThesrand
function is not required to avoid data races with other calls to pseudo-random sequence generation functions.
— Whether the random number generator state has thread storage duration, and its initial state in a newly created thread (7.22.2.1, 7.22.2.2).
IfIn 7.29.6.4 (Restartable multibyte/wide string conversion functions), add:ps
is a null pointer, each function uses its own internalmbstate_t
object instead, which is initialized at program startup to the initial conversion state; the functions are not required to avoid data races with other calls to the same function in this case. It is implementation-defined whether the internalmbstate_t
object has thread-storage definition, and whether a newly created thread has the same state has the current thread at the time of creation, or the initial conversion state. The implementation behaves as if no library function calls these functions with a null pointer forps
.
IfIn J.3.12 (Library functions), add:ps
is a null pointer, each function uses its own internal mbstate_t object instead, which is initialized at program startup to the initial conversion state; the functions are not required to avoid data races with other calls to the same function in this case. It is implementation-defined whether the internalmbstate_t
object has thread-storage definition, and whether a newly created thread has the same state has the current thread at the time of creation, or the initial conversion state. The implementation behaves as if no library function calls these functions with a null pointer forps
.
— Whether the internal mbstate_t
object has thread-storage duration, and its initial value in
a newly created thread (7.28.1, 7.29.6.4).