diff options
-rw-r--r-- | retry.go | 16 | ||||
-rw-r--r-- | stm_test.go | 2 | ||||
-rw-r--r-- | tx.go | 42 |
3 files changed, 42 insertions, 18 deletions
@@ -1,14 +1,20 @@ package stm -// Retry is a sentinel value. When thrown via panic, it indicates that a +import ( + "runtime/pprof" +) + +var retries = pprof.NewProfile("stmRetries") + +// retry is a sentinel value. When thrown via panic, it indicates that a // transaction should be retried. -const Retry = "retry" +var retry = &struct{}{} // catchRetry returns true if fn calls tx.Retry. -func catchRetry(fn Operation, tx *Tx) (result interface{}, retry bool) { +func catchRetry(fn Operation, tx *Tx) (result interface{}, gotRetry bool) { defer func() { - if r := recover(); r == Retry { - retry = true + if r := recover(); r == retry { + gotRetry = true } else if r != nil { panic(r) } diff --git a/stm_test.go b/stm_test.go index d6e4475..5c0ae31 100644 --- a/stm_test.go +++ b/stm_test.go @@ -223,7 +223,7 @@ func testPingPong(t testing.TB, n int, afterHit func(string)) { tx.Set(ready, false) return false } - panic(Retry) + return tx.Retry() }).(bool) { afterHit(noise) AtomicSet(ready, true) @@ -9,15 +9,16 @@ import ( // A Tx represents an atomic transaction. type Tx struct { - reads map[*Var]VarValue - writes map[*Var]interface{} - watching map[*Var]struct{} - locks txLocks - mu sync.Mutex - cond sync.Cond - waiting bool - completed bool - tries int + reads map[*Var]VarValue + writes map[*Var]interface{} + watching map[*Var]struct{} + locks txLocks + mu sync.Mutex + cond sync.Cond + waiting bool + completed bool + tries int + numRetryValues int } // Check that none of the logged values have changed since the transaction began. @@ -97,9 +98,18 @@ func (tx *Tx) Set(v *Var, val interface{}) { tx.writes[v] = val } -// Retry aborts the transaction and retries it when a Var changes. -func (tx *Tx) Retry() { - panic(Retry) +type txProfileValue struct { + *Tx + int +} + +// Retry aborts the transaction and retries it when a Var changes. You can return from this method +// to satisfy return values, but it should never actually return anything as it panics internally. +func (tx *Tx) Retry() interface{} { + retries.Add(txProfileValue{tx, tx.numRetryValues}, 0) + tx.numRetryValues++ + panic(retry) + panic("unreachable") } // Assert is a helper function that retries a transaction if the condition is @@ -117,9 +127,17 @@ func (tx *Tx) reset() { for k := range tx.writes { delete(tx.writes, k) } + tx.removeRetryProfiles() tx.resetLocks() } +func (tx *Tx) removeRetryProfiles() { + for tx.numRetryValues > 0 { + tx.numRetryValues-- + retries.Remove(txProfileValue{tx, tx.numRetryValues}) + } +} + func (tx *Tx) recycle() { for v := range tx.watching { delete(tx.watching, v) |