Classic Synchronization Problems & Concurrent Programming

transactional memory

Transactional memory borrows the idea of a database transaction and brings it to ordinary in-memory data. Instead of carefully choosing which locks to take and in which order — the error-prone heart of lock-based programming — you simply wrap a block of code in 'atomic { ... }' and the system promises that the whole block appears to happen all at once: either every change inside it takes effect together, or none of them do, and no other thread ever sees a half-finished state. It is like the bank promising that a transfer either fully completes or fully rolls back, with no moment where the money is missing — except now that promise covers any block of code you mark, not just the database.

The usual implementation is optimistic. A transaction runs speculatively, recording every memory location it reads and writes in a private log, without taking locks. When it reaches the end, the system checks for conflicts: did any other transaction modify a location this one read? If not, the transaction commits — its buffered writes become visible atomically. If there was a conflict, the transaction aborts, throws away its tentative changes as if they never happened, and retries. This comes in two forms: software transactional memory (STM), implemented purely in libraries or language runtimes (as in Haskell or Clojure), and hardware transactional memory (HTM), where the CPU's cache tracks the read and write sets and detects conflicts directly (Intel's TSX was a well-known attempt).

The appeal is composability and ease: you reason about each atomic block in isolation, two correct transactions combine into a correct larger one (something locks notoriously do not give you), and there is no lock-ordering puzzle to cause deadlock. The honest reality is that it has not displaced locks in practice. STM carries real bookkeeping overhead and can livelock under high contention (transactions repeatedly abort each other); HTM has hard limits — a transaction that touches too much memory, or performs I/O or a system call that cannot be rolled back, simply cannot run transactionally and must fall back to a lock. Intel even disabled some TSX implementations over bugs. Transactional memory remains an elegant idea, widely used in a few niches and as a hardware accelerator for lock elision, but not the universal cure for concurrency it once promised to be.

A transfer as a transaction: atomic { balance[A] -= amount; balance[B] += amount; }. Either both updates commit together or, if a conflicting transaction touched these accounts, the whole block aborts and silently retries — with no lock chosen and no lock-ordering deadlock possible.

Wrap a block in atomic; the runtime makes it all-or-nothing and retries on conflict — no lock-ordering puzzle.

Transactional memory is not a finished cure for concurrency: STM has overhead and can livelock, and HTM cannot handle transactions that are too large or that do irreversible work like I/O — those must fall back to ordinary locks.

Also called
TMSTMHTMsoftware transactional memoryhardware transactional memory