diff options
Diffstat (limited to 'README.md')
-rw-r--r-- | README.md | 37 |
1 files changed, 37 insertions, 0 deletions
@@ -19,6 +19,13 @@ Another significant departure is that `stm.Atomically` does not return a value. This shortens transaction code a bit, but I'm not 100% it's the right decision. (The alternative would be for every transaction function to return an `interface{}`.) +Unlike Haskell, data in Go is not immutable by default, which means you have +to be careful when using STM to manage pointers. If two goroutines have access +to the same pointer, it doesn't matter whether they retrieved the pointer +atomically; modifying the pointer can still cause a data race. To resolve +this, either use immutable data structures, or replace pointers with STM +variables. A more concrete example is given below. + 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! @@ -82,6 +89,36 @@ stm.Atomically(stm.Select( See [example_santa_test.go](example_santa_test.go) for a more complex example. +## Pointers + +Don't use stm to manage pointers! + +```go +p := stm.NewVar([]byte{1,2,3}) +stm.Atomically(func(tx *stm.Tx) { + b := tx.Get(p).([]byte) + b[0] = 7 + tx.Set(p, b) +}) +``` + +This transaction looks innocent enough, but it has a hidden side effect: the +modification of b is visible outside the transaction. Instead of modifying +pointers directly, prefer to operate on immutable values as much as possible. +Following this advice, we can rewrite the transaction to perform a copy: + +```go +stm.Atomically(func(tx *stm.Tx) { + b := tx.Get(p).([]byte) + c := make([]byte, len(b)) + copy(c, b) + c[0] = 7 + tx.Set(p, c) +}) +``` + +This is less efficient, but it preserves atomicity. + ## Benchmarks In synthetic benchmarks, STM seems to have a 1-5x performance penalty compared |