diff options
author | EuAndreh <eu@euandre.org> | 2025-01-22 12:31:30 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2025-01-22 12:31:30 -0300 |
commit | 59d879ef4e654ce53c2450e000ffa435f06c2f0e (patch) | |
tree | 05ae996bf799b1e51f891a5586b3b72fa9bdfe3f /doc.go | |
parent | Setup Makefile build skeleton (diff) | |
download | stm-59d879ef4e654ce53c2450e000ffa435f06c2f0e.tar.gz stm-59d879ef4e654ce53c2450e000ffa435f06c2f0e.tar.xz |
Unify code into default repo format
Diffstat (limited to 'doc.go')
-rw-r--r-- | doc.go | 77 |
1 files changed, 0 insertions, 77 deletions
@@ -1,77 +0,0 @@ -/* -Package stm provides 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. - - x := stm.NewVar[int](3) - -You can then use the Atomically method to atomically read and/or write the the -data. This code atomically decrements x: - - stm.Atomically(func(tx *stm.Tx) { - cur := x.Get(tx) - x.Set(tx, 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: - - stm.Atomically(func(tx *stm.Tx) { - cur := x.Get(tx) - if cur == 0 { - tx.Retry() - } - x.Set(tx, 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. - - func dec(v *stm.Var[int]) { - return func(tx *stm.Tx) { - cur := v.Get(tx) - if cur == 0 { - tx.Retry() - } - v.Set(tx, cur-1) - } - } - - // Note that Select does not perform any work itself, but merely - // returns a transaction function. - stm.Atomically(stm.Select(dec(x), dec(y))) - -An important caveat: transactions must be idempotent (they should have the -same effect every time they are invoked). This is because a transaction may be -retried several times before successfully completing, meaning its 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 impure -operations 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 -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 |