From d2fb630df81f1c7c49430915c204ffa54fcd6687 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 4 Oct 2016 20:14:45 -0700 Subject: [PATCH] helper/shadow: Value.Close --- helper/shadow/value.go | 28 ++++++++++++++++++++++++++++ helper/shadow/value_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/helper/shadow/value.go b/helper/shadow/value.go index bf30c12a8b..2413335b80 100644 --- a/helper/shadow/value.go +++ b/helper/shadow/value.go @@ -1,12 +1,24 @@ package shadow import ( + "errors" "sync" ) +// ErrClosed is returned by any closed values. +// +// A "closed value" is when the shadow has been notified that the real +// side is complete and any blocking values will _never_ be satisfied +// in the future. In this case, this error is returned. If a value is already +// available, that is still returned. +var ErrClosed = errors.New("shadow closed") + // Value is a struct that coordinates a value between two // parallel routines. It is similar to atomic.Value except that when // Value is called if it isn't set it will wait for it. +// +// The Value can be closed with Close, which will cause any future +// blocking operations to return immediately with ErrClosed. type Value struct { lock sync.Mutex cond *sync.Cond @@ -14,6 +26,22 @@ type Value struct { valueSet bool } +// Close closes the value. This can never fail. For a definition of +// "close" see the struct docs. +func (w *Value) Close() error { + w.lock.Lock() + set := w.valueSet + w.lock.Unlock() + + // If we haven't set the value, set it + if !set { + w.SetValue(ErrClosed) + } + + // Done + return nil +} + // Value returns the value that was set. func (w *Value) Value() interface{} { w.lock.Lock() diff --git a/helper/shadow/value_test.go b/helper/shadow/value_test.go index 069b308525..ad8a64aa9b 100644 --- a/helper/shadow/value_test.go +++ b/helper/shadow/value_test.go @@ -41,3 +41,32 @@ func TestValue(t *testing.T) { t.Fatalf("bad: %#v", val) } } + +func TestValueClose(t *testing.T) { + var v Value + + // Close + v.Close() + + // Verify + val := v.Value() + if val != ErrClosed { + t.Fatalf("bad: %#v", val) + } +} + +func TestValueClose_existing(t *testing.T) { + var v Value + + // Set the value + v.SetValue(42) + + // Close + v.Close() + + // Verify + val := v.Value() + if val != 42 { + t.Fatalf("bad: %#v", val) + } +}