Doc. No.: N1349
Date: 2009-02-23
Reply to: Clark Nelson Hans-J. Boehm Lawrence Crowl
Phone: +1-503-712-8433 +1-650-857-3406 +1-650-253-3677
Email: clark.nelson@intel.com Hans.Boehm@hp.com
boehm@acm.org
crowl@google.com
Lawrence@Crowl.org

Parallel memory sequencing model proposal

Introduction

This paper proposes changes to the WD for the next revision of the C standard to incorporate the C++ parallel memory sequencing model.

For rationale and explanation of the memory model overall, see N1276. For additional rationale and explanation of the design of the atomic (library) facilities, see N1284, the predecessor of this paper.

Changes since N1284 include:

Most of the atomic operations specified herein are type-generic. The style of the specifications herein is similar to that of the <math.h> classification and comparison macros. All these specifications, plus those in <tgmath.h>, are expected to be cleaned up by a proposal for general-purpose type-generic macro facilities.

WG21 has also adopted modifications to the memory model for memory dependence ordering. That is not incorporated in this proposal; it is expected to be the subject of a later proposal.

Changes to the Standard

The definition of "memory location"

Add a new subclause to clause 3:

memory location
either an object of scalar type, or a maximal sequence of adjacent bit-fields all having non-zero width

NOTE 1 Two threads of execution can update and access separate memory locations without interfering with each other.

NOTE 2 A bit-field and an adjacent non-bit-field member are in separate memory locations. The same applies to two bit-fields, if one is declared inside a nested struct declaration and the other is not, or if the two are separated by a zero-length bit-field declaration, or if they are separated by a non-bit-field member declaration. It is not safe to concurrently update two bit-fields in the same struct if all members declared between them are also bit-fields, no matter what the sizes of those intervening bit-fields happen to be.

EXAMPLE A structure declared as

struct {
	char a;
	int b:5, c:11, :0, d:8;
	struct { int ee:8; } e;
}

contains four separate memory locations: The member a, and bit-fields d and e.ee are each separate memory locations, and can be modified concurrently without interfering with each other. The bit-fields b and c together constitute the fourth memory location. The bit-fields b and c cannot be concurrently modified, but b and a, for example, can be.

5.1.2.4 Multi-threaded executions and data races

Insert a new subclause after 5.1.2.3, with the following paragraphs.

5.1.2.4p1:

Under a hosted implementation, a C program can have more than one thread of execution (a.k.a. thread) running concurrently. The execution of each thread proceeds as defined by the remainder of this standard. The execution of the entire program consists of an execution of all of its threads. « Footnote: Usually the execution can be viewed as an interleaving of all its threads. However some kinds of atomic operations, for example, allow executions inconsistent with a simple interleaving, as described below. » Under a freestanding implementation, it is implementation-defined whether a program can have more than one thread of execution.

5.1.2.4p2:

The value of an object visible to a thread T at a particular point might be the initial value of the object, a value assigned to the object by T, or a value assigned to the object by another thread, according to the rules below.

5.1.2.4p3:

NOTE In some cases, there may instead be undefined behavior. Much of this section is motivated by the desire to support atomic operations with explicit and detailed visibility constraints. However, it also implicitly supports a simpler view for more restricted programs.

5.1.2.4p4:

Two expression evaluations conflict if one of them modifies a memory location and the other one accesses or modifies the same memory location.

5.1.2.4p5:

The library defines a number of atomic operations («ATM») and operations on locks (???) that are specially identified as synchronization operations. These operations play a special role in making assignments in one thread visible to another. A synchronization operation on one or more memory locations is either an acquire operation or a release operation, or both an acquire and release operation. A synchronization operation without an associated memory location is a fence and can be either an acquire fence, a release fence, or both an acquire and release fence. In addition, there are relaxed atomic operations, which are not synchronization operations, and atomic read-modify-write operations, which have special characteristics.

5.1.2.4p6:

NOTE For example, a call that acquires a lock will perform an acquire operation on the locations comprising the lock. Correspondingly, a call that releases the same lock will perform a release operation on those same locations. Informally, performing a release operation on A forces prior side effects on other memory locations to become visible to other threads that later perform an acquire operation on A. We do not include relaxed atomic operations as synchronization operations although, like synchronization operations, they cannot contribute to data races.

5.1.2.4p7:

All modifications to a particular atomic object M occur in some particular total order, called the modification order of M. If A and B are modifications of an atomic object M, and A happens before B, then A shall precede B in the modification order of M, which is defined below.

5.1.2.4p8:

NOTE 1 This states that the modification orders must respect the "happens before" relation.

5.1.2.4p9:

NOTE 2 There is a separate order for each atomic object. There is no requirement that these can be combined into a single total order for all objects. In general this will be impossible since different threads may observe modifications to different variables in inconsistent orders.

5.1.2.4p10:

A release sequence on an atomic object M is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first operation is a release, and every subsequent operation

5.1.2.4p11:

Certain library calls synchronize with other library calls performed by another thread. In particular, an atomic operation A that performs a release operation on an object M synchronizes with an atomic operation B that performs an acquire operation on M and reads a value written by any side effect in the release sequence headed by A.

5.1.2.4p12:

NOTE 1 Except in the specified cases, reading a later value does not necessarily ensure visibility as described below. Such a requirement would sometimes interfere with efficient implementation.

5.1.2.4p13:

NOTE 2 The specifications of the synchronization operations define when one reads the value written by another. For atomic variables, the definition is clear. All operations on a given lock occur in a single total order. Each lock acquisition "reads the value written" by the last lock release.

5.1.2.4p14:

An evaluation A inter-thread happens before an evaluation B if

5.1.2.4p15:

An evaluation A happens before an evaluation B if:

5.1.2.4p16:

A visible side effect A on an object M with respect to a value computation B of M satisfies the conditions:

The value of a non-atomic scalar object M, as determined by evaluation B, shall be the value stored by the visible side effect A.

5.1.2.4p17:

NOTE 1 If there is ambiguity about which side effect to a non-atomic object is visible, then there is a data race, and the behavior is undefined.

5.1.2.4p18:

NOTE 2 This states that operations on ordinary variables are not visibly reordered. This is not actually detectable without data races, but it is necessary to ensure that data races, as defined here, and with suitable restrictions on the use of atomics, correspond to data races in a simple interleaved (sequentially consistent) execution.

5.1.2.4p19:

The visible sequence of side effects on an atomic object M, with respect to a value computation B of M, is a maximal contiguous sub-sequence of side effects in the modification order of M, where the first side effect is visible with respect to B, and for every subsequent side effect, it is not the case that B happens before it. The value of an atomic object M, as determined by evaluation B, shall be the value stored by some operation in the visible sequence of M with respect to B. Furthermore, if a value computation A of an atomic object M happens before a value computation B of M, and the value computed by A corresponds to the value stored by side effect X, then the value computed by B shall either equal the value computed by A, or be the value stored by side effect Y, where Y follows X in the modification order of M.

5.1.2.4p20:

NOTE 1 This effectively disallows compiler reordering of atomic operations to a single object, even if both operations are "relaxed" loads. By doing so, we effectively make the "cache coherence" guarantee provided by most hardware available to C atomic operations.

5.1.2.4p21:

NOTE 2 The visible sequence depends on the "happens before" relation, which depends on the values observed by loads of atomics, which we are restricting here. The intended reading is that there must exist an association of atomic loads with modifications they observe that, together with suitably chosen modification orders and the "happens before" relation derived as described above, satisfy the resulting constraints as imposed here.

5.1.2.4p22:

The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

5.1.2.4p23:

NOTE 1 It can be shown that programs that correctly use simple locks to prevent all data races, and use no other synchronization operations, behave as though the executions of their constituent threads were simply interleaved, with each observed value of an object being the last value assigned in that interleaving. This is normally referred to as "sequential consistency". However, this applies only to race-free programs, and race-free programs cannot observe most program transformations that do not change single-threaded program semantics. In fact, most single-threaded program transformations continue to be allowed, since any program that behaves differently as a result must perform an undefined operation.

5.1.2.4p24:

NOTE 2 Compiler transformations that introduce assignments to a potentially shared memory location that would not be modified by the abstract machine are generally precluded by this standard, since such an assignment might overwrite another assignment by a different thread in cases in which an abstract machine execution would not have encountered a data race. This includes implementations of data member assignment that overwrite adjacent members in separate memory locations. We also generally preclude reordering of atomic loads in cases in which the atomics in question may alias, since this may violate the "visible sequence" rules.

5.1.2.4p25:

NOTE 3 Transformations that introduce a speculative read of a potentially shared memory location may not preserve the semantics of the C program as defined in this standard, since they potentially introduce a data race. However, they are typically valid in the context of an optimizing compiler that targets a specific machine with well-defined semantics for data races. They would be invalid for a hypothetical machine that is not tolerant of races or provides hardware race detection.

Non-terminating loops

Add a new paragraph as 6.8.5p6:

An iteration statement that

in its body, controlling expression, or, in the case of a for statement, its expression-3, may be assumed by the implementation to terminate. « Footnote: This is intended to allow compiler transformations, such as removal of empty loops, even when termination cannot be proven. »

«ATM» Atomics <stdatomic.h>

Add a new library subclause with the following paragraphs and subclauses. «ATM» will be used to refer to the number of this new section.

«ATM»p1:

The header <stdatomic.h> defines three macros and declares several types and functions for performing atomic operations on data shared between threads.

«ATM»p2:

The macros defined are

ATOMIC_INTEGRAL_LOCK_FREE
ATOMIC_ADDRESS_LOCK_FREE

which indicate the general lock-free property of integer and address atomic types; and

ATOMIC_FLAG_INIT

which expands to an initializer for an object of type atomic_flag.

«ATM»p3:

The types include

memory_order

which is an enumerated type whose enumerators identify memory ordering constraints;

atomic_flag

which is structure type representing a lock-free, primitive atomic flag;

atomic_bool

which is a structure type representing the atomic analog of the type _Bool;

atomic_address

which is a structure type representing the atomic analog of a pointer type; and several atomic analogs of integer types.

«ATM».1 Order and consistency

«ATM».1p1:

The enumeration memory_order specifies the detailed regular (non-atomic) memory synchronization operations as defined in 5.1.2.4 and may provide for operation ordering. Its enumerated values are as follows:

memory_order_relaxed
memory_order_acquire
memory_order_release
memory_order_acq_rel
memory_order_seq_cst

«ATM».1p2:

Their meanings are as follows.

memory_order_relaxed
no operation orders memory.
memory_order_release, memory_order_acq_rel, and memory_order_seq_cst
a store operation performs a release operation on the affected memory location.
memory_order_acquire, memory_order_acq_rel, and memory_order_seq_cst
a load operation performs an acquire operation on the affected memory location.

«ATM».1p3:

There shall be a single total order S on all memory_order_seq_cst operations, consistent with the "happens before" order and modification orders for all affected locations, such that each memory_order_seq_cst operation that loads a value observes either the last preceding modification according to this order S, or the result of an operation that is not memory_order_seq_cst.

«ATM».1p4:

NOTE Although it is not explicitly required that S include locks, it can always be extended to an order that does include lock and unlock operations, since the ordering between those is already included in the "happens before" ordering.

«ATM».1p5:

For an atomic operation B that reads the value of an atomic object M, if there is a memory_order_seq_cst fence X sequenced before B, then B observes either the last memory_order_seq_cst modification of M preceding X in the total order S or a later modification of M in its modification order.

«ATM».1p6:

For atomic operations A and B on an atomic object M, where A modifies M and B takes its value, if there is a memory_order_seq_cst fence X such that A is sequenced before X and B follows X in S, then B observes either the effects of A or a later modification of M in its modification order.

«ATM».1p7:

For atomic operations A and B on an atomic object M, where A modifies M and B takes its value, if there are memory_order_seq_cst fences X and Y such that A is sequenced before X, Y is sequenced before B, and X precedes Y in S, then B observes either the effects of A or a later modification of M in its modification order.

«ATM».1p8:

An atomic store shall only store a value that has been computed from constants and program input values by a finite sequence of program evaluations, such that each evaluation observes the values of variables as computed by the last prior assignment in the sequence. « Footnote: Among other implications, atomic variables shall not decay. » The ordering of evaluations in this sequence shall be such that

«ATM».1p9:

NOTE The second requirement disallows "out-of-thin-air", or "speculative" stores of atomics when relaxed atomics are used. Since unordered operations are involved, evaluations may appear in this sequence out of thread order. For example, with x and y initially zero,

// Thread 1:
r1 = atomic_load_explicit(&y, memory_order_relaxed);
atomic_store_explicit(&x, r1, memory_order_relaxed);
// Thread 2:
r2 = atomic_load_explicit(&x, memory_order_relaxed);
atomic_store_explicit(&y, 42, memory_order_relaxed);

is allowed to produce r1 == 42 && r2 == 42. The sequence of evaluations justifying this consists of:

atomic_store_explicit(&y, 42, memory_order_relaxed);
r1 = atomic_load_explicit(&y, memory_order_relaxed);
atomic_store_explicit(&x, r1, memory_order_relaxed);
r2 = atomic_load_explicit(&x, memory_order_relaxed);

On the other hand,

// Thread 1:
r1 = atomic_load_explicit(&y, memory_order_relaxed);
atomic_store_explicit(&x, r1, memory_order_relaxed);
// Thread 2:
r2 = atomic_load_explicit(&x, memory_order_relaxed);
atomic_store_explicit(&y, r2, memory_order_relaxed);

may not produce r1 == 42 && r2 = 42, since there is no sequence of evaluations that results in the computation of 42. In the absence of "relaxed" operations and read-modify-write operations with weaker than memory_order_acq_rel ordering, the second requirement has no impact.

Recommended practice

«ATM».1p10:

The requirements do not forbid r1 == 42 && r2 == 42 in the following example, with x and y initially zero:

// Thread 1:
r1 = atomic_load_explicit(&x, memory_order_relaxed);
if (r1 == 42) atomic_store_explicit(&y, r1, memory_order_relaxed);
// Thread 2:
r2 = atomic_load_explicit(&y, memory_order_relaxed);
if (r2 == 42) atomic_store_explicit(&x, 42, memory_order_relaxed);

However, this is not useful behavior, and implementations should not allow it.

«ATM».1p11:

Implementations should make atomic stores visible to atomic loads within a reasonable amount of time. Implementations shall not move an atomic operation out of an unbounded loop.

«ATM».2 Fences

«ATM».2p1:

This subclause introduces synchronization primitives called fences. Fences can have acquire semantics, release semantics, or both. A fence with acquire semantics is called an acquire fence. A fence with release semantics is called a release fence.

«ATM».2p2:

A release fence A synchronizes with an acquire fence B if there exist atomic operations X and Y, both operating on some atomic object M, such that A is sequenced before X, X modifies M, Y is sequenced before B, and Y reads the value written by X or a value written by any side effect in the hypothetical release sequence X would head if it were a release operation.

«ATM».2p3:

A release fence A synchronizes with an atomic operation B that performs an acquire operation on an atomic object M if there exists an atomic operation X such that A is sequenced before X, X modifies M, and B reads the value written by X or a value written by any side effect in the hypothetical release sequence X would head if it were a release operation.

«ATM».2p4:

An atomic operation A that is a release operation on an atomic object M synchronizes with an acquire fence B if there exists some atomic operation X on M such that X is sequenced before B and reads the value written by A or a value written by any side effect in the release sequence headed by A.

«ATM».2.1 The atomic_thread_fence function

Synopsis

«ATM».2.1p1:

#include <stdatomic.h>
void atomic_thread_fence(memory_order order);

Description

«ATM».2.1p2:

Depending on the value of order, this operation:

«ATM».2.2 The atomic_signal_fence function

Synopsis

«ATM».2.2p1:

#include <stdatomic.h>
void atomic_signal_fence(memory_order order);

Description

«ATM».2.2p2:

Equivalent to atomic_thread_fence(order), except that "synchronizes with" relationships are established only between a thread and a signal handler executed in the same thread.

«ATM».2.2p3:

NOTE 1 atomic_signal_fence can be used to specify the order in which actions performed by the thread become visible to the signal handler.

«ATM».2.2p4:

NOTE 2 Compiler optimizations and reorderings of loads and stores are inhibited in the same way as with atomic_thread_fence, but the hardware fence instructions that atomic_thread_fence would have inserted are not emitted.

«ATM».3 Lock-free property

«ATM».3p1:

The macros ATOMIC_INTEGRAL_LOCK_FREE and ATOMIC_ADDRESS_LOCK_FREE indicates the general lock-free property of integer and address atomic types. A value of 0 indicates that the types are never lock-free. A value of 1 indicates that the types are sometimes lock-free. A value of 2 indicates that the types are always lock-free.

«ATM».3p2:

NOTE Operations that are lock-free should also be also address-free. That is, atomic operations on the same memory location via two different addresses will communicate atomically. The implementation shall not depend on any per-process state. This restriction enables communication via memory mapped into a process more than once and memory shared between two processes.

«ATM».3.1 The atomic_is_lock_free generic function

Synopsis

«ATM».3.1p1:

#include <stdatomic.h>
_Bool atomic_is_lock_free(atomic_type const volatile *object);

Description

«ATM».3.1p2:

The function atomic_is_lock_free indicates whether or not the object is lock-free. atomic_type can be any atomic type.

Returns

«ATM».3.1p3:

True if the object's operations are lock-free, false otherwise. The result of a lock-free query on one object cannot be inferred from the result of a lock-free query on another object.

«ATM».4 Atomic integer and address types

«ATM».4p1:

For each line in the following table, the atomic type name is declared as the name of a structure type which is the atomic analog of the corresponding integer type.

Atomic type name Integer type
atomic_char char
atomic_schar signed char
atomic_uchar unsigned char
atomic_short short
atomic_ushort unsigned short
atomic_int int
atomic_uint unsigned int
atomic_long long
atomic_ulong unsigned long
atomic_llong long long
atomic_ullong unsigned long long

«ATM».4p2:

For each line in the following table, the atomic type name is declared as a structure type which is the atomic analog of the corresponding integer typedef.

Atomic type name Integer typedef
atomic_char16_t char16_t
atomic_char32_t char32_t
atomic_wchar_t wchar_t
atomic_int_least8_t int_least8_t
atomic_uint_least8_t uint_least8_t
atomic_int_least16_t int_least16_t
atomic_uint_least16_t uint_least16_t
atomic_int_least32_t int_least32_t
atomic_uint_least32_t uint_least32_t
atomic_int_least64_t int_least64_t
atomic_uint_least64_t uint_least64_t
atomic_int_fast8_t int_fast8_t
atomic_uint_fast8_t uint_fast8_t
atomic_int_fast16_t int_fast16_t
atomic_uint_fast16_t uint_fast16_t
atomic_int_fast32_t int_fast32_t
atomic_uint_fast32_t uint_fast32_t
atomic_int_fast64_t int_fast64_t
atomic_uint_fast64_t uint_fast64_t
atomic_intptr_t intptr_t
atomic_uintptr_t uintptr_t
atomic_size_t size_t
atomic_ssize_t ssize_t
atomic_ptrdiff_t ptrdiff_t
atomic_intmax_t intmax_t
atomic_uintmax_t uintmax_t

«ATM».4p3:

The semantics of the operations on these types are defined in «ATM».5.

«ATM».4p4:

The atomic_bool type provides an atomic boolean.

«ATM».4p5:

The atomic_address type provides atomic void* operations. The unit of addition/subtraction shall be one byte.

«ATM».4p6:

NOTE The representation of atomic integer and address types need not have the same size as their corresponding regular types. They should have the same size whenever possible, as it eases effort required to port existing code.

«ATM».5 Operations on integer and address types

«ATM».5p1:

There are only a few kinds of operations on atomic types, though there are many instances on those kinds. This section specifies each general kind.

«ATM».5p2:

In the following operation definitions:

«ATM».5p3: [last two sentences deleted]

NOTE Many operations are volatile-qualified. The "volatile as device register" semantics have not changed in the standard. This qualification means that volatility is preserved when applying these operations to volatile objects.

«ATM».5.1 The atomic_store generic functions

Synopsis

«ATM».5.1p1:

#include <stdatomic.h>
void atomic_store(volatile A *object, C desired);
void atomic_store_explicit(volatile A *object, C desired,
	memory_order order);

Description

«ATM».5.1p2:

The order argument shall not be memory_order_acquire, nor memory_order_acq_rel. Atomically replace the value pointed to by object with the value of desired. Memory is affected according to the value of order.

«ATM».5.2 The atomic_load generic functions

Synopsis

«ATM».5.2p1:

#include <stdatomic.h>
C atomic_load(volatile A *object);
C atomic_load_explicit(volatile A *object,
	memory_order order);

Description

«ATM».5.2p2:

The order argument shall not be memory_order_release nor memory_order_acq_rel. Memory is affected according to the value of order.

Returns

Atomically returns the value pointed to by object.

«ATM».5.3 The atomic_exchange generic functions

Synopsis

«ATM».5.3p1:

#include <stdatomic.h>
C atomic_exchange(volatile A *object, C desired);
C atomic_exchange_explicit(volatile A *object, C desired,
	memory_order order);

Description

«ATM».5.3p2:

Atomically replace the value pointed to by object with desired. Memory is affected according to the value of order. These operations are read-modify-write operations (5.1.2.4).

Returns

«ATM».5.3p3:

Atomically returns the value pointed to by object immediately before the effects.

«ATM».5.4 The atomic_compare_exchange generic functions

Synopsis

«ATM».5.4p1:

#include <stdatomic.h>
_Bool atomic_compare_exchange_strong(volatile A *object, C *expected, C desired);
_Bool atomic_compare_exchange_strong_explicit(volatile A *object, C *expected, C desired,
	memory_order success, memory_order failure);
_Bool atomic_compare_exchange_weak(volatile A *object, C *expected, C desired);
_Bool atomic_compare_exchange_weak_explicit(volatile A *object, C *expected, C desired,
	memory_order success, memory_order failure);

Description

«ATM».5.4p2:

The failure argument shall not be memory_order_release nor memory_order_acq_rel. The failure argument shall be no stronger than the success argument. Atomically, compares the value pointed to by object for equality with that in expected, and if true, replaces the value pointed to by object with desired, and if false, updates the value in expected with the value pointed to by object. Further, if the comparison is true, memory is affected according to the value of success, and if the comparison is false, memory is affected according to the value of failure. These operations are atomic read-modify-write operations (5.1.2.4).

«ATM».5.4p2:

NOTE The effect of the compare-and-exchange operations is

if (*object == *expected)
	*object = desired;
else
	*expected = *object;

«ATM».5.4p3:

The weak compare-and-exchange operations may fail spuriously, that is, return false while leaving the value pointed to by expected unchanged.

«ATM».5.4p4:

NOTE This spurious failure enables implementation of compare-and-exchange on a broader class of machines, e.g. load-locked store-conditional machines.

«ATM».5.4p5:

EXAMPLE A consequence of spurious failure is that nearly all uses of weak compare-and-exchange will be in a loop.

expected = atomic_load(&current);
do desired = function(expected);
while (!atomic_compare_exchange(&current, expected, desired));

When a compare-and-exchange is in a loop, the weak version will yield better performance on some platforms. When a weak compare-and-exchange would require a loop and a strong one would not, the strong one is preferable.

Returns

«ATM».5.4p6:

The result of the comparison.

«ATM».5.5 The atomic_fetch-and-modify generic functions

«ATM».5.5p1:

The following operations perform arithmetic and bitwise computations. All of these operations are applicable to an object of any atomic integer type. Only addition and subtraction are applicable to atomic_address. None of these operations is applicable to atomic_bool. The key, operator, and computation correspondence is:

key op computation key op computation
add + addition sub - subtraction
or | bitwise inclusive or xor ^ bitwise exclusive or
and & bitwise and

Synopsis

«ATM».5.5p2:

#include <stdatomic.h>
C atomic_fetch_key(volatile A *object, M operand);
C atomic_fetch_key_explicit(volatile A *object, M operand,
	memory_order order);

Description

«ATM».5.5p3:

Atomically replaces the value pointed to by object with the result of the computation applied to the value pointed to by object and the given operand. Memory is affected according to the value of order. These operations are atomic read-modify-write operations (5.1.2.4). For signed integer types, arithmetic is defined to use two's-complement representation. There are no undefined results. For address types, the result may be an undefined address, but the operations otherwise have no undefined behavior.

Returns

«ATM».5.5p4:

Atomically, the value pointed to by object immediately before the effects.

«ATM».5.5p5:

NOTE The operation of an atomic_fetch_key function is nearly equivalent to the operation of the corresponding op= compound assignment operator. The only differences are that a compound assignment operator is not guaranteed to operate atomically, and that the result of a compound assignment operation is the updated value of the object, whereas the value returned by an atomic_fetch_key function is the previous value of the atomic object.

«ATM».6 Atomic flag type and operations

«ATM».6p1:

The atomic_flag type provides the classic test-and-set functionality. It has two states, set and clear.

«ATM».6p2:

Operations on an object of type atomic_flag shall be lock free.

«ATM».6p3:

NOTE Hence the operations should also be address-free. No other type requires lock-free operations, so the atomic_flag type is the minimum hardware-implemented type needed to conform to this International standard. The remaining types can be emulated with atomic_flag, though with less than ideal properties.

«ATM».6p4:

The macro ATOMIC_FLAG_INIT may be used to initialize an atomic_flag to the clear state. An atomic_flag that is not explicitly initialized with ATOMIC_FLAG_INIT is initially in an indeterminate state.

«ATM».6p5:

EXAMPLE

atomic_flag guard = ATOMIC_FLAG_INIT;

«ATM».6.1 The atomic_flag_test_and_set functions

Synopsis

«ATM».6.1p1:

#include <stdatomic.h>
bool atomic_flag_test_and_set(volatile atomic_flag *object);
bool atomic_flag_test_and_set_explicit(volatile atomic_flag *object,
	memory_order order);

Description

«ATM».6.1p2:

Atomically sets the value pointed to by object to true. Memory is affected according to the value of order. These operations are atomic read-modify-write operations (5.1.2.4).

Returns

«ATM».6.1p3:

Atomically, the value of the object immediately before the effects.

«ATM».6.2 The atomic_flag_clear functions

Synopsis

«ATM».6.2p1:

#include <stdatomic.h>
void atomic_flag_clear(volatile atomic_flag *object);
void atomic_flag_clear_explicit(volatile atomic_flag *object,
	memory_order order);

Description

«ATM».6.2p2:

The order argument shall not be memory_order_acquire nor memory_order_acq_rel. Atomically sets the value pointed to by object to false. Memory is affected according to the value of order.