diff --git a/goose_std.go b/goose_std.go index 3af48f6..0737e36 100644 --- a/goose_std.go +++ b/goose_std.go @@ -3,6 +3,7 @@ package std import ( "math" "sync" + "time" "github.com/goose-lang/primitive" "github.com/goose-lang/std/std_core" @@ -164,3 +165,27 @@ func Multipar(num uint64, op func(uint64)) { // is a simple way to do so - the model always requires one step to reduce this // application to a value. func Skip() {} + +// WaitTimeout is like cond.Wait(), but waits for a maximum time of timeoutMs +// milliseconds. +// +// Not provided by sync.Cond, so we have to (inefficiently) implement this +// ourselves. +func WaitTimeout(cond *sync.Cond, timeoutMs uint64) { + done := make(chan struct{}) + go func() { + cond.Wait() + cond.L.Unlock() + close(done) + }() + select { + case <-time.After(time.Duration(timeoutMs) * time.Millisecond): + // timed out + cond.L.Lock() + return + case <-done: + // Wait returned + cond.L.Lock() + return + } +} diff --git a/goose_std_test.go b/goose_std_test.go index 3f0da52..b87967c 100644 --- a/goose_std_test.go +++ b/goose_std_test.go @@ -2,6 +2,7 @@ package std import ( "math" + "sync" "testing" "github.com/stretchr/testify/assert" @@ -87,3 +88,12 @@ func TestSkip(t *testing.T) { // nothing much to test, it does nothing Skip() } + +func TestWaitTimeout(t *testing.T) { + var m sync.Mutex + c := sync.NewCond(&m) + + m.Lock() + WaitTimeout(c, 10) + m.Unlock() +}