Properly specify the interaction of library calls for condition variables

Jens Gustedt, INRIA and ICube, France

2025-10-07

target

integration into IS ISO/IEC 9899:202y

document history

C document ID: C4243

document number date comment
n3673 202507 Original proposal
n3709 202510 Be careful when waiting for the mutex
Describe the atomicity of the wait operations
Factorize the description of the waits

license

CC BY, see https://creativecommons.org/licenses/by/4.0

1 Motivation

This paper draws part of its motivation from n3655

‘’Make implicit undefined behaviour in mtx_destroy() explicit’’,

and n3708

‘’Properly specify the interaction of library calls for mutexes’’.

When discussing the former on the reflector it became quickly apparent, that not only the undefined behavior is currently poorly described, but that the description also lacks a proper integration into C11’s model of inter-thread time. The same observations hold for the type cnd_t and clause 7.3, condition variable functions.

The only sensible way to provide this integration is to add condition variables to the set of atomic types, but much as atomic_flag this type then has only atomic operations and an internal state that is not observable directly.

Additions by this paper here are:

1.1 Novelty

This proposal is not supposed to change the behavior of any implementation (even for related specifications such as C++ or POSIX), but formulates semantics that are conforming with the terminology of the C standard and that is consistent with the existing practice.

2 Wording

Modifications are against n3550 with the accepted modifcations in n3559 applied. New text is underlined green, removed text is stroke-out red.

Add a new clause

7.30.3.1 General
1 The type cnd_t of condition variables is a complete atomic object type that implements specific operations as described in this subclause but has no observable value.
2 Prior to a wait operation, that is a call to cnd_timedwait or cnd_wait, the mutex pointed to by the mtx_t* argument shall be locked by the calling thread. A wait operation performs
A thread that performs a wait operation on a condition variable is said to be blocked after writing the wait-entry and unblocked when the wait-completion is written. Wait operations with the same mutex synchronize by the call to mtx_unlock (a release operation) and the call to mtx_lock (an acquire operation). A wait operation as a whole, including the calls to mtx_unlock and mtx_lock, is perceived as one indivisible operation by the calling thread.FNT)
FTN) In contrast to that, in general, synchronizing operations on lock-free atomic objects in signal handlers for signals that are raised during a wait operation will not synchronize via the mutex.
3 Wait-entry, wait-completion, and cnd_broadcast and cnd_signal operations are atomic read-modify-write operations on the condition variable with a memory order that is at least as strong as memory_order_relaxed. The cnd_t type is not necessarily lock-free. In addition to the atomic operations, there are the cnd_init and cnd_destroy operations as well as modifications of the object representationFTN1) that also are modifications in the modification order of a condition variable.
FTN1) These include for example a copy operation of a structure type that has a cnd_t member.
4 A condition variable that is not initialized by a cnd_init operation, or whose last modification has been by a cnd_destroy operation, or such that its object representation has been modified directly, has an indeterminate representation; performing any of the described operations other than cnd_init on such a condition variable has undefined behavior.
5 Before a cnd_destroy operation or before a direct modification of the object representation of a condition variable, for all preceding wait operations in the modification order, the wait-entry and wait-completion modifications shall have been performed.
6 NOTE 1 That means that if a thread has entered a wait operation but the wait-completion modification has not been performed and another thread concurrently calls cnd_destroy on that same condition variable, the behavior is undefined. On the other hand, if after the wait-completion the calling thread is yet suspended waiting for the lock on the associated mutex the execution remains in a defined state.
7 NOTE 2 There is no synchronization operation for the type cnd_t itself. Synchronization is only implied by the described lock and unlock operations on the mutex that is associated with a wait operation.

7.30.3.5 The cnd_timedwait function

2 The cnd_timedwait function atomically unlocks the mutex pointed to by mtx (as if by mtx_unlock) and blocks suspends the execution of the calling thread until the condition variable pointed to by cond is unblocked,

WhenThereafter the calling thread becomes unblocked it locks (as if by mtx_lock) the object pointed to by mtx before it returns. Prior to the call, the mutex pointed to by mtx shall be locked by the calling thread. Wait-entry and wait-completion of calls with the same mutex pointed to by mtx synchronize as if by calls to mtx_unlock and mtx_lock, respectively.

Returns

3 The cnd_timedwait function returns thrd_success upon success if the calling thread is unblocked by a call to cnd_signal or cnd_broadcast, or otherwise thrd_timedout if the time specified in the call was reached without acquiring the requested resource, or thrd_error if the request could not be honored otherwise.

7.30.3.6 The cnd_wait function

2 The cnd_wait function atomically unlocks the mutex pointed to by mtx (as if by mtx_unlock) and blocks suspends the execution of the calling thread until the condition variable pointed to by cond is unblocked,

WhenThereafter the calling thread becomes unblocked it locks (as if by mtx_lock) the object pointed to by mtx before it returns. Prior to the call, the mutex pointed to by mtx shall be locked by the calling thread. Entry and completion of calls with the same mutex pointed to by mtx synchronize as if by calls to mtx_unlock and mtx_lock, respectively.

Returns

3 The cnd_wait function returns thrd_success upon success if the calling thread is unblocked by a call to cnd_signal or cnd_broadcast or thrd_error if the request could not be honored otherwise.

Annex J .2 Undefined behavior

(181a) An operation other than a call to cnd_init is performed on a condition variable that has an indeterminate representation (7.30.3.1).
(181b) A wait operation on a condition variable C has not resulted in the wait-completion modification when a call to cnd_destroy with C is performed or when the object representation of C is modified directly (7.30.3.1).

3 Note to the editors

The changes here add cnd_t to the notion of atomic types. If n3706 is accepted into C2Y (there should be an online vote for inclusion before the end of 2025) the list of atomic types that are exempted from the generic atomic operations (7.17.7.2) should be amended with cnd_t.

… that operate on all atomic types other than atomic_flag and cnd_t

Acknowledgment

Thanks to Dave Banham, Hans Boehm and Joseph Myers for discussions and suggestions for improvements.