Crate stm [] [src]

This library implements software transactional memory, often abbreviated with STM.

It is designed closely to haskells STM library. Read Simon Marlow's Parallel and Concurrent Programming in Haskell for more info. Especially the chapter about Performance is also important for using STM in rust.

With locks the sequence of two threadsafe actions is no longer threadsafe because other threads may interfer in between of these actions. Applying another lock may lead to common sources of errors like deadlocks and forgotten locks.

Unlike locks Software transactional memory is composable. It is typically implemented by writing all read and write operations in a log. When the action has finished, the reads are checked for consistency and depending on the result either the writes are committed in a single atomic operation or the result is discarded and the computation run again.

Usage

STM operations are safed inside the type STM<T> where T is the return type of the inner operation. They are created with stm!:

let transaction = stm!({
    // some action
    // return value (or empty for unit)
});

and can then be run by calling.

transaction.atomically();

For running an STM-Block inside of another use the macro stm_call!:

use stm::Var;
let var = Var::new(0);
let modify = stm!({
    var.write(42);
});

let x = stm!({
    stm_call!(modify);
    var.read()  // return the value saved in var
}).atomically();

println!("var = {}", x);

STM safety

Software transactional memory is completely safe in the terms that rust considers safe. Still there are multiple rules that you should obey when dealing with software transactional memory:

  • Don't run code with side effects, especially no IO-code, because stm is designed to be run multiple times. Return a closure if you have to.
  • Don't run an STM-Block by calling STM::atomically inside of another because your thread will immediately panic. When you use STM in the inner of a function then return a STM-Object instead so that callers can safely compose it into larger blocks.
  • Don't mix locks and STM. Your code will easily deadlock and slow down on unpredictably.
  • When you put an Arc into a Var don't use inner mutability to modify it since the inner still points to the original value.
  • Don't call Var::read or Var::write from outside of a STM block. Instead start a STM or use Var::read_atomic for it.

Speed

Generally keep your atomic blocks as small as possible bacause the more time you spend the more likely it is to collide with other threads. For STM reading vars is quite slow because it need to look them up in the log every time they are written to and every used var increases the chance of collisions. You should keep the amount of accessed variables as low as needed.

Macros

stm!

declare a block that uses STM

stm_call!

call a STM function from inside of a STM block

Structs

STM

class representing a STM computation

Var

A variable that can be used in a STM-Block

Enums

StmResult

a result of each step of a STM calculation

Functions

retry

call retry in stm_call! to let the STM manually run again