diff options
author | lukechampine <luke.champine@gmail.com> | 2016-03-29 22:42:46 -0400 |
---|---|---|
committer | lukechampine <luke.champine@gmail.com> | 2016-03-29 23:14:44 -0400 |
commit | d3d383777e0d50da39c32852a45db558f05cfcb6 (patch) | |
tree | 2827dc0a11717ffd8d6c929996be8801c7ee4846 /README.md | |
download | stm-d3d383777e0d50da39c32852a45db558f05cfcb6.tar.gz stm-d3d383777e0d50da39c32852a45db558f05cfcb6.tar.xz |
initial commit
Diffstat (limited to 'README.md')
-rw-r--r-- | README.md | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..f470283 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# stm + +Package `stm` provides [Software Transactional Memory](https://en.wikipedia.org/wiki/Software_transactional_memory) operations for Go. This is +an alternative to the standard way of writing concurrent code (channels and +mutexes). STM makes it easy to perform arbitrarily complex operations in an +atomic fashion. One of its primary advantages over traditional locking is that +STM transactions are composable, whereas locking functions are not -- the +composition will either deadlock or release the lock between functions (making +it non-atomic). + +To begin, create an STM object that wraps the data you want to access +concurrently: + +```go + x := stm.NewVar(3) +``` +You can then use the Atomically method to atomically read and/or write the the +data. This code atomically decrements `x`: + +```go + stm.Atomically(func(tx *stm.Tx) { + cur := tx.Get(x).(int) + tx.Set(x, cur-1) + }) +``` + +An important part of STM transactions is retrying. At any point during the +transaction, you can call `tx.Retry()`, which will abort the transaction, +but not cancel it entirely. The call to `Atomically` will block until another +call to `Atomically` finishes, at which point the transaction will be rerun. +Specifically, one of the values read by the transaction (via `tx.Get`) must be +updated before the transaction will be rerun. As an example, this code will +try to decrement `x`, but will block as long as `x` is zero: + +```go + stm.Atomically(func(tx *stm.Tx) { + cur := tx.Get(x).(int) + if cur == 0 { + tx.Retry() + } + tx.Set(x, cur-1) + }) +``` + +Internally, `tx.Retry` simply calls `panic(stm.Retry)`. Panicking with any +other value will cancel the transaction; no values will be changed. However, +it is the responsibility of the caller to catch such panics. + +Multiple transactions can be composed using `OrElse`. If the first transaction +calls `Retry`, the second transaction will be run. If the second transaction +also calls `Retry`, the entire call will block. For example, this code +implements the "decrement-if-nonzero" transaction above, but for two values. +It will first try to decrement `x`, then `y`, and block if both values are zero. + +```go + func dec(v *stm.Var) { + return func(tx *stm.Tx) { + cur := tx.Get(v).(int) + if cur == 0 { + panic(stm.Retry) + } + tx.Set(x, cur-1) + } + } + + // Note that OrElse does not perform any work itself, but merely + // returns a new transaction. + stm.Atomically(stm.OrElse(dec(x), dec(y)) +``` + +An important caveat: transactions must not have side effects! This is because a +transaction may be restarted several times before completing, meaning the side +effects may execute more than once. This will almost certainly cause incorrect +behavior. One common way to get around this is to build up a list of operations +to perform inside the transaction, and then perform them after the transaction +completes. + +The `stm` API tries to mimic that of Haskell's `Control.Concurrent.STM`, but +this is not entirely possible due to Go's type system; we are forced to use +`interface{}` and type assertions. Furthermore, Haskell can enforce at compile +time that STM variables are not modified outside the STM monad. This is not +possible in Go, so be especially careful when using pointers in your STM code. + +It remains to be seen whether this style of concurrency has practical +applications in Go. If you find this package useful, please tell me about it! |