aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--retry.go16
-rw-r--r--stm_test.go2
-rw-r--r--tx.go42
3 files changed, 42 insertions, 18 deletions
diff --git a/retry.go b/retry.go
index 92efb9e..1997b18 100644
--- a/retry.go
+++ b/retry.go
@@ -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)
diff --git a/tx.go b/tx.go
index 065cb6c..3a1506c 100644
--- a/tx.go
+++ b/tx.go
@@ -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)