aboutsummaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
authorlukechampine <luke.champine@gmail.com>2016-03-29 22:42:46 -0400
committerlukechampine <luke.champine@gmail.com>2016-03-29 23:14:44 -0400
commitd3d383777e0d50da39c32852a45db558f05cfcb6 (patch)
tree2827dc0a11717ffd8d6c929996be8801c7ee4846 /README.md
downloadstm-d3d383777e0d50da39c32852a45db558f05cfcb6.tar.gz
stm-d3d383777e0d50da39c32852a45db558f05cfcb6.tar.xz
initial commit
Diffstat (limited to 'README.md')
-rw-r--r--README.md85
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!