Transactional memory
Transactional memory is a concurrency synchronization mechanism that combines groups of statements in transactions, that are
- atomic (either all statements occur, or nothing occurs)
- isolated (statements in a transaction may not observe half-written writes made by another transaction, even if they execute in parallel)
Typical implementations use hardware transactional memory where supported and to the limits that it is available (e.g. until the changeset is saturated) and fall back to software transactional memory, usually implemented with optimistic concurrency: if another transaction updated some of the variables used by a transaction, it is silently retried. For that reason, retriable transactions ("atomic blocks") can only call transaction-safe functions.
Note that accessing a variable in a transaction and out of a transaction without other external synchronization is a data race.
If feature testing is supported, the features described here are indicated by the macro constant __cpp_transactional_memory with a value equal or greater 201505.
Contents |
Synchronized blocks
synchronized
compound-statement
This section is incomplete |
All outermost synchronized blocks execute in a single total order. The end of each synchronized block synchronizes with the beginning of the next synchronized block in that order.
Synchronized blocks may call transaction-unsafe functions.
// the following function is thread-safe, and guarantees consistent before-after pairs int f() { static int i = 0; synchronized { // begin transaction printf("before %d\n", i); ++i; printf("after %d\n", i); return i; // commit transaction } }
This section is incomplete |
Leaving a synchronized block by any means (reaching the end, goto, break, continue, return, exception) commits the transaction.
Entering a synchronized block by goto or switch is not allowed.
Atomic blocks
This section is incomplete |
atomic_noexcept
compound-statement
atomic_cancel
compound-statement
atomic_commit
compound-statement
The exceptions used for transaction cancellation in atomic_cancel
blocks are std::bad_alloc, std::bad_array_length, std::bad_array_new_length, std::bad_cast, std::bad_typeid, std::bad_exception, std::exception and all standard library exceptions derived from it, and the new exception std::tx_exception<T>
The compound-statement in an atomic block is not allowed to execute any expression or statement or call any function that isn't transaction_safe
(this is a compile time error)
// each call to f() retrieves a unique value of i, even when done in parallel int f() { static int i = 0; atomic_noexcept { // begin transaction // printf("before %d\n", i); // error: cannot call a non transaction-safe function ++i; return i; // commit transaction } }
Leaving an atomic block by any means other than exception (reaching the end, goto, break, continue, return) commits the transaction.
Transaction-safe functions
This section is incomplete |
A function can be explicitly declared to be transaction-safe by using the keyword transaction_safe in its declaration.
This section is incomplete |
In a lambda declaration, it appears either immediately after the capture list, or immediately after the (keyword mutable
(if one is used).
This section is incomplete |
extern volatile int * p = 0; struct S { virtual ~S(); }; int f() transaction_safe { int x = 0; // ok: not volatile p = &x; // ok: the pointer is not volatile int i = *p; // error: read through volatile glvalue S s; // error: invocation of unsafe destructor }
int f(int x) { // implicitly transaction-safe if (x <= 0) return 0; return x + f(x-1); }
If a function that is not transaction-safe is called through a reference or pointer to a non-transaction-safe function, the behavior is undefined.
Pointers to transaction-safe functions and pointers to transaction-safe member functions are implicitly convertible to pointers to functions and pointers to member functions respectively. It is unspecified if the resulting pointer compares equal to the original.
Transaction-safe virtual functions
This section is incomplete |
If the final overrider of a transaction_safe_dynamic
function is not declared transaction_safe
, calling it in an atomic block is undefined behavior.
Attributes
This section is incomplete Reason: optimize_for_synchronized |
Notes
This section is incomplete Reason: experience notes from Wyatt paper/talk |