Intel has made available for free download Prototype Edition 3.0 of the Intel C++ STM Compiler. (STM is short for "Software Transactional Memory".)
The Transactional Memory C++ language constructs that are included open the door for users to exercise the new language constructs for parallel programming, understand the transaction memory programming model, and provide feedback on the usefulness of these extensions with Intel C++ STM Compiler Prototype Edition. This posting includes the Intel C++ STM Compiler Prototype Edition 2.0 and runtime libraries for Intel transactional memory language construct extensions.
Atomic blocks
The tm atomic statement defines a basic atomic block,
similarly to the atomic construct previously defined in the
literature [2, 22] and introduced as a C language pragma in
[46]:
__tm_atomic {
// block of arbitrary C/C++ statements
}
The TM system executes atomic blocks as transactions
and isolates concurrently executing transactions from each
other with the net effect that all the operations in one transaction
appear to complete either before or after all the operations
in any other transaction.
Within each atomic block, the compiler instruments each
shared-memory access so that its execution is delegated to
the TM runtime. The TM runtime tracks all transactional accesses
and detects conflicting accesses among concurrently
executing transactions. Two transactions conflict if they both
access the same memory location at the same time and at
least one of them writes to that location. On a potential conflict,
the TM runtime transparently rolls back the side effects
of one of the conflicting transactions and re-executes it until
it succeeds without conflicts.
Atomic blocks can contain arbitrary code of all regular
C/C++ statements, including direct and indirect function
calls, and virtual function calls. This includes calls to precompiled
libraries (i.e., code that has not been compiled by
the transactional compiler) and those that perform arbitrary
I/O operations. Since the TM runtime cannot track the accesses
inside precompiled code or roll back I/O operations,
calling into such code inside a transaction causes the runtime
to serialize execution of the transaction with respect to
other transactions – no other transactions are allowed to be
in-flight concurrently with the serial one. Section 4 describes
the details of this serial execution mode.
Abort statements
The tm abort statement (a user abort) allows the programmer
to roll back an atomic block explicitly. This statement
must appear in the lexical scope of an atomic block.
It rolls back all side effects of the atomic block that statically
encloses it and transfers control to the statement immediately
following the block. It ends the transaction if the
enclosing atomic block is the outermost atomic block.
Because the runtime cannot log the side effects of precompiled
functions, the tm abort statement can execute
only if the innermost atomic block containing it has not
called a precompiled function (unless that function is a
tm pure function as described in Section 2.4.2). A runtime
error will occur if an atomic block executes a user abort after
it has called a precompiled function:
__tm_atomic {
print(‘‘HelloWorld!’’);
__tm_abort; // error (runtime failure)
}
This does not preclude calling a precompiled function in
an atomic block that is at an outer dynamic nesting level
relative to the block containing the abort statement:
__tm_atomic {
print(‘‘HelloWorld!’’);
__tm_atomic {
__tm_abort; // OK
}
}
Nested atomic blocks have closed nesting semantics [37],
which means that the side effects of a nested transaction
commit become visible only when the dynamically outermost
transaction commits. Abort statements allow a programmer
to roll back a transaction partially by aborting the
innermost nested atomic block. The compiler and runtime,
therefore, may not flatten those nested atomic blocks that
contain user abort statements.
Single lock semantics
Atomic blocks provide single lock atomicity (SLA) semantics
[34]: A program behaves as if a single global lock guards
each atomic block. This guarantees that programs that are
race free under a single global lock will execute correctly
under transactional execution. These semantics support the
privatization and race free publication patterns [34]. A trivial
implementation of atomic blocks can use a single global
lock to implement isolation (though such an implementation
must still perform undo logging to support abort statements).
In fact the serial execution mode (which supports calling precompiled
binaries) falls back to using a single global lock to
implement atomic blocks.
Consistent with the emerging C/C++ memory model
specification [8], SLA semantics provides no guarantees for
programs containing data races. In the presence of data races
between transactions and non-transactional code, code executing
outside a transaction may see speculative or intermediate
values produced by a transaction, and it may violate
the isolation of a transaction by writing to memory locations
accessed inside a transaction.
To support SLA semantics correctly, the STM implementation
must guarantee several important safety properties,
namely privatization safety, granular safety, and observable
consistency[34]. These safety properties ensure that code
that is race free under a single global lock remains race free
under transactional execution; that is, they ensure that the
STM implementation does not introduce a data race into a
program that is otherwise race free under a single lock. Most
prior STM systems did not properly maintain these properties
and thus cannot be used to implement single global lock
semantics for C/C++ atomic blocks.