aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlukechampine <luke.champine@gmail.com>2016-04-03 13:54:52 -0400
committerlukechampine <luke.champine@gmail.com>2016-04-03 13:54:52 -0400
commit680211145fb0a720505eead0b9334451b8466916 (patch)
treef5190c512d87060738cc47702b8caaaa1c763827
parentgo fmt (diff)
downloadstm-680211145fb0a720505eead0b9334451b8466916.tar.gz
stm-680211145fb0a720505eead0b9334451b8466916.tar.xz
better pointer documentation
-rw-r--r--README.md37
-rw-r--r--stm.go1
2 files changed, 38 insertions, 0 deletions
diff --git a/README.md b/README.md
index b968c31..d6adcf6 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/stm.go b/stm.go
index cb745a1..fc4a125 100644
--- a/stm.go
+++ b/stm.go
@@ -74,6 +74,7 @@ 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.
+Remember: modifying a pointer is a side effect!
*/
package stm