From 3eb552afdec6ff59144d370bbc74921c04453b0b Mon Sep 17 00:00:00 2001 From: lukechampine Date: Tue, 29 Mar 2016 23:42:32 -0400 Subject: replace OrElse with Select --- README.md | 21 +++++++++++---------- stm.go | 37 ++++++++++++++++++++++++------------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index f470283..53fdc66 100644 --- a/README.md +++ b/README.md @@ -46,26 +46,27 @@ 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. +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 dec(v *stm.Var) func(*stm.Tx) { return func(tx *stm.Tx) { cur := tx.Get(v).(int) if cur == 0 { - panic(stm.Retry) + tx.Retry() } - tx.Set(x, cur-1) + tx.Set(v, cur-1) } } - // Note that OrElse does not perform any work itself, but merely + // Note that Select does not perform any work itself, but merely // returns a new transaction. - stm.Atomically(stm.OrElse(dec(x), dec(y)) + stm.Atomically(stm.Select(dec(x), dec(y))) ``` An important caveat: transactions must not have side effects! This is because a diff --git a/stm.go b/stm.go index 5dfbb7d..e3c965e 100644 --- a/stm.go +++ b/stm.go @@ -40,25 +40,26 @@ 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 with 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. +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) { return func(tx *stm.Tx) { cur := tx.Get(v).(int) if cur == 0 { - panic(stm.Retry) + tx.Retry() } - tx.Set(x, cur-1) + tx.Set(v, cur-1) } } - // Note that OrElse does not perform any work itself, but merely + // Note that Select does not perform any work itself, but merely // returns a transaction function. - stm.Atomically(stm.OrElse(dec(x), dec(y)) + 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 @@ -169,11 +170,21 @@ func catchRetry(fn func(*Tx), tx *Tx) (retry bool) { return } -// OrElse runs fn1, and runs fn2 if fn1 calls Retry. -func OrElse(fn1, fn2 func(*Tx)) func(*Tx) { +// Select runs the supplied functions in order. Execution stops when a +// function succeeds without calling Retry. If no functions succeed, the +// entire selection will be retried. +func Select(fns ...func(*Tx)) func(*Tx) { return func(tx *Tx) { - if catchRetry(fn1, tx) { - fn2(tx) + switch len(fns) { + case 0: + // empty Select blocks forever + tx.Retry() + case 1: + fns[0](tx) + default: + if catchRetry(fns[0], tx) { + Select(fns[1:]...)(tx) + } } } } -- cgit v1.2.3