module Concurrent
def atomically
b.value += 10
a.value -= 10
Concurrent::atomically do
b = new TVar(100)
a = new TVar(100_000)
@example
Transactions within transactions are flattened to a single transaction.
the transaction. Creating a thread counts as a side-effect.
* If you create a new thread within an atomically, it will not be part of
* It is undefined behaviour to use callcc or Fiber with atomically.
* If an exception escapes an atomically block it will abort the transaction.
side-effects, except for via TVar.
more than once. In most cases your code should be free of
* Most importantly, the block that you pass to atomically may be executed
There are some very important and unusual semantics that you must be aware of:
properties from database transactions.
transactions never interfere with each other. You may recognise these
objects involved will never enter an illegal state, and isolated, in that
that it either happens or it does not, consistent, in that the `TVar`
With respect to the value of `TVar` objects, the transaction is atomic, in
Run a block that reads and writes `TVar`s as a single atomic transaction.
def atomically raise ArgumentError.new('no block given') unless block_given? # Get the current transaction transaction = Transaction::current # Are we not already in a transaction (not nested)? if transaction.nil? # New transaction begin # Retry loop loop do # Create a new transaction transaction = Transaction.new Transaction::current = transaction # Run the block, aborting on exceptions begin result = yield rescue Transaction::AbortError => e transaction.abort result = Transaction::ABORTED rescue Transaction::LeaveError => e transaction.abort break result rescue => e transaction.abort raise e end # If we can commit, break out of the loop if result != Transaction::ABORTED if transaction.commit break result end end end ensure # Clear the current transaction Transaction::current = nil end else # Nested transaction - flatten it and just run the block yield end end