Document number: N2752=08-0262
Programming Language C++
 
Peter Dimov, <pdimov@pdimov.com>
Clark Nelson, <clark.nelson@intel.com>
 
2008-09-18

Proposed Text for Bidirectional Fences

All edits are relative to N2723.

Chapter 1 edits

Change 1.10 [intro.multithreaded] p4 as follows:

The library defines a number of atomic operations (clause 29) and operations on locks (clause 30) 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 a consume operation, an acquire operation, a release operation, or both an acquire and release operation, on one or more memory locations; the semantics of these are described below. 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, also described below. [ 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 a consume or 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. —end note ]

Change 1.10 [intro.multithreaded] p6 as follows:

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

Change 1.10 [intro.multithreaded] p7 as follows:

Certain library calls synchronize with other library calls performed by another thread. In particular, an atomic operation An evaluation A that performs a release operation on an object M synchronizes with synchronizes with an evaluation 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. [ Note: 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. —end note ] [ Note: 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. —end note ]

Chapter 29 edits

Throughout 29, remove all five declarations of fence member functions (for bool, itype, address, generic and flag):

void fence(memory_order) const volatile;

Throughout 29, remove all six declarations of atomic_fence functions (two each for bool, itype and address):

void atomic_fence(const volatile atomic_type*, memory_order);

In 29.4 [atomics.types.operations], delete the description of the atomic_fence functions and the fence member functions, including paragraphs 21 and 22.

In both 29 "Header <cstdatomic> synopsis" and 29.5 [atomics.flag], delete the atomic_flag_fence function and the atomic_global_fence_compatibility object:

...
void atomic_flag_clear_explicit(volatile atomic_flag*, memory_order);
void atomic_flag_fence(const volatile atomic_flag *object, memory_order order);
#define ATOMIC_FLAG_INIT unspecified
extern const atomic_flag atomic_global_fence_compatibility;

Add new declarations at the end of 29 "Header <cstdatomic> synopsis":

template<> struct atomic<integral>;
// 29.6, fences
void atomic_thread_fence(memory_order);
void atomic_signal_fence(memory_order);

}

Change 29.1 [atomics.order] p2 as follows:

The memory_order_seq_cst operations that load a value are acquire operations on the affected locations. The memory_order_seq_cst operations that store a value are release operations on the affected locations. In addition, in a consistent execution, there must 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. [ 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. —end note ]

Add new paragraphs following 29.1 [atomics.order] p2:

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.
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.
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 on M in its modification order.

Add a new section, 29.6 [atomic.fences], with the following contents:

29.6 Fences

This section 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.
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.
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.
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.
void atomic_thread_fence(memory_order mo);
Effects: Depending on the value of mo, this operation:
void atomic_signal_fence(memory_order mo);
Effects: equivalent to atomic_thread_fence(mo), except that synchronizes with relationships are established only between a thread and a signal handler executed in the same thread.
[Note: atomic_signal_fence can be used to specify the order in which actions performed by the thread become visible to the signal handler. — end note]
[Note: Compiler optimizations or 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. — end note]

Thanks to Hans Boehm, Lawrence Crowl, Paul McKenney and Raul Silvera for reviewing this paper.

--end