aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--funcs.go23
-rw-r--r--retry.go2
-rw-r--r--stm_test.go15
-rw-r--r--tx.go2
4 files changed, 21 insertions, 21 deletions
diff --git a/funcs.go b/funcs.go
index 3336c71..54576ea 100644
--- a/funcs.go
+++ b/funcs.go
@@ -39,7 +39,7 @@ func newTx() *Tx {
return tx
}
-func WouldBlock(fn Operation) (block bool) {
+func WouldBlock[R any](fn Operation[R]) (block bool) {
tx := newTx()
tx.reset()
_, block = catchRetry(fn, tx)
@@ -51,7 +51,7 @@ func WouldBlock(fn Operation) (block bool) {
}
// Atomically executes the atomic function fn.
-func Atomically(op Operation) interface{} {
+func Atomically[R any](op Operation[R]) R {
expvars.Add("atomically", 1)
// run the transaction
tx := newTx()
@@ -116,20 +116,19 @@ func AtomicSet[T any](v *Var[T], val interface{}) {
// Compose is a helper function that composes multiple transactions into a
// single transaction.
-func Compose(fns ...Operation) Operation {
- return func(tx *Tx) interface{} {
+func Compose[R any](fns ...Operation[R]) Operation[struct{}] {
+ return VoidOperation(func(tx *Tx) {
for _, f := range fns {
f(tx)
}
- return nil
- }
+ })
}
// 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 ...Operation) Operation {
- return func(tx *Tx) interface{} {
+func Select[R any](fns ...Operation[R]) Operation[R] {
+ return func(tx *Tx) R {
switch len(fns) {
case 0:
// empty Select blocks forever
@@ -154,12 +153,12 @@ func Select(fns ...Operation) Operation {
}
}
-type Operation func(*Tx) interface{}
+type Operation[R any] func(*Tx) R
-func VoidOperation(f func(*Tx)) Operation {
- return func(tx *Tx) interface{} {
+func VoidOperation(f func(*Tx)) Operation[struct{}] {
+ return func(tx *Tx) struct{} {
f(tx)
- return nil
+ return struct{}{}
}
}
diff --git a/retry.go b/retry.go
index 1997b18..1adcfd0 100644
--- a/retry.go
+++ b/retry.go
@@ -11,7 +11,7 @@ var retries = pprof.NewProfile("stmRetries")
var retry = &struct{}{}
// catchRetry returns true if fn calls tx.Retry.
-func catchRetry(fn Operation, tx *Tx) (result interface{}, gotRetry bool) {
+func catchRetry[R any](fn Operation[R], tx *Tx) (result R, gotRetry bool) {
defer func() {
if r := recover(); r == retry {
gotRetry = true
diff --git a/stm_test.go b/stm_test.go
index fdf7af2..4103563 100644
--- a/stm_test.go
+++ b/stm_test.go
@@ -125,7 +125,7 @@ func TestVerify(t *testing.T) {
func TestSelect(t *testing.T) {
// empty Select should panic
- require.Panics(t, func() { Atomically(Select()) })
+ require.Panics(t, func() { Atomically(Select[struct{}]()) })
// with one arg, Select adds no effect
x := NewVar[int](2)
@@ -135,15 +135,16 @@ func TestSelect(t *testing.T) {
picked := Atomically(Select(
// always blocks; should never be selected
- VoidOperation(func(tx *Tx) {
+ func(tx *Tx)int {
tx.Retry()
- }),
+ panic("unreachable")
+ },
// always succeeds; should always be selected
- func(tx *Tx) interface{} {
+ func(tx *Tx) int {
return 2
},
// always succeeds; should never be selected
- func(tx *Tx) interface{} {
+ func(tx *Tx) int {
return 3
},
))
@@ -152,9 +153,9 @@ func TestSelect(t *testing.T) {
func TestCompose(t *testing.T) {
nums := make([]int, 100)
- fns := make([]Operation, 100)
+ fns := make([]Operation[struct{}], 100)
for i := range fns {
- fns[i] = func(x int) Operation {
+ fns[i] = func(x int) Operation[struct{}] {
return VoidOperation(func(*Tx) { nums[x] = x })
}(i) // capture loop var
}
diff --git a/tx.go b/tx.go
index 0d69498..2020308 100644
--- a/tx.go
+++ b/tx.go
@@ -114,7 +114,7 @@ type txProfileValue struct {
// 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{} {
+func (tx *Tx) Retry() struct{} {
retries.Add(txProfileValue{tx, tx.numRetryValues}, 1)
tx.numRetryValues++
panic(retry)