diff options
-rw-r--r-- | LICENSE | 21 | ||||
-rw-r--r-- | README.md | 72 |
2 files changed, 24 insertions, 69 deletions
@@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Luke Champine + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. @@ -1,5 +1,7 @@ # stm +[](https://godoc.org/github.com/lukechampine/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 @@ -8,75 +10,7 @@ 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 `Select`. If the first transaction -calls `Retry`, the next transaction will be run, and so on. If all of the -transactions call `Retry`, the call will block and the entire selection will -be retried. 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) func(*stm.Tx) { - return func(tx *stm.Tx) { - cur := tx.Get(v).(int) - if cur == 0 { - tx.Retry() - } - tx.Set(v, cur-1) - } - } - - // Note that Select does not perform any work itself, but merely - // returns a new transaction. - stm.Atomically(stm.Select(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 +The `stm` API tries to mimic that of Haskell's [`Control.Concurrent.STM`](https://hackage.haskell.org/package/stm-2.4.4.1/docs/Control-Concurrent-STM.html), 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 |