Transactional memory - cppreference.com

##### cppreference.com

##### Search

Create account

##### Namespaces

- Page
- Discussion

##### Variants

##### Views

- View
- Edit
- History

##### Actions

# Transactional memory

From cppreference.com
< cpp‎ | language

| Work in progress This page is under construction

| Experimental Feature The functionality described on this page is part of the Transactional Memory Technical Specification ISO/IEC TS 19841:2015 (TM TS)

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.

### [edit] 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.

### [edit] Atomic blocks

| This section is incomplete

`atomic_noexcept` compound-statement

`atomic_cancel` compound-statement

`atomic_commit` compound-statement

1) If an exception is thrown, std::abort is called
2) If an exception is thrown, std::abort is called, unless the exception is one of the exceptions uses for transaction cancellation (see below) in which case the transaction is cancelled: the values of all memory locations in the program that were modified by side effects of the operations of the atomic block are restored to the values they had at the time the start of the atomic block was executed, and the exception continues stack unwinding as usual.
3) If an exception is thrown, the transaction is committed normally.

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.

### [edit] 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.

#### [edit] 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.

### [edit] Attributes

The attribute [[optimize_for_synchronized]] may be applied to a declarator in a function declaration and must appear on the first declaration of the function.

If a function is declared [[optimize_for_synchronized]] in one translation unit and the same function is declared without [[optimize_for_synchronized]] in another translation unit, the program is ill-formed; no diagnostic required.

It indicates that a the function definition should be optimized for invocation from a synchronized statement. In particular, it avoids serializing synchronized blocks that make a call to a function that is transaction-safe for the majority of calls, but not for all calls (e.g. hash table insertion that may have to rehash, allocator that may have to request a new block, a simple function that may rarely log)

    std::atomic<bool> rehash{false};

    // maintenance thread runs this loop
    void maintenance_thread(void*) {
        while (!shutdown) {
            synchronized {
                if (rehash) {
                    hash.rehash();
                    rehash = false;
                }
            }
        }
    }

    // worker threads execute hundreds of thousands of calls to this function
    // every second. Calls to insert_key() from synchronized blocks in other
    // translation units will cause those blocks to serialize, unless insert_key()
    // is marked [[optimize_for_synchronized]]
    [[optimize_for_synchronized]] void insert_key(char* key, char* value) {
      bool concern = hash.insert(key, value);
      if (concern) rehash = true;
    }

    // GCC assembly without the attribute: the entire function is serialized
    insert_key(char*, char*):
    	subq	$8, %rsp
    	movq	%rsi, %rdx
    	movq	%rdi, %rsi
    	movl	$hash, %edi
    	call	Hash::insert(char*, char*)
    	testb	%al, %al
    	je	.L20
    	movb	$1, rehash(%rip)
    	mfence
    .L20:
    	addq	$8, %rsp
    	ret

    // GCC assembly with the attribute:
    transaction clone for insert_key(char*, char*):
    	subq	$8, %rsp
    	movq	%rsi, %rdx
    	movq	%rdi, %rsi
    	movl	$hash, %edi
    	call	transaction clone for Hash::insert(char*, char*)
    	testb	%al, %al
    	je	.L27
    	xorl	%edi, %edi
    	call	_ITM_changeTransactionMode // Note: this is the serialization point
    	movb	$1, rehash(%rip)
    	mfence
    .L27:
    	addq	$8, %rsp
    	ret

| This section is incomplete Reason: check assembly with trunk, also show caller-side changes

### [edit] Notes

| This section is incomplete Reason: experience notes from Wyatt paper/talk

### [edit] Compiler support

An older variant of this specification was supported in GCC as of 4.7, current gcc trunk (gcc 6.0) supports the technical specification (requires -fgnu-tm to enable)

Retrieved from "http://en.cppreference.com/mwiki/index.php?title=cpp/language/transactional_memory&oldid=81695"
Categories:

- Todo without reason
- Todo with reason