diff options
-rw-r--r-- | funcs.go | 23 | ||||
-rw-r--r-- | retry.go | 2 | ||||
-rw-r--r-- | stm_test.go | 15 | ||||
-rw-r--r-- | tx.go | 2 |
4 files changed, 21 insertions, 21 deletions
@@ -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{}{} } } @@ -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 } @@ -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) |