[![GoDoc] (https://godoc.org/gist.github.com/fa3c0dd0a8c45d7a605387b9265cac98.git?status.svg)] (https://godoc.org/gist.github.com/fa3c0dd0a8c45d7a605387b9265cac98.git)
Last active
October 28, 2021 06:12
-
-
Save imjasonh/fa3c0dd0a8c45d7a605387b9265cac98 to your computer and use it in GitHub Desktop.
Value
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package val | |
import ( | |
"errors" | |
"time" | |
) | |
// ErrTimeout is returned by GetWithTimeout if no value is available before the | |
// specified timeout. | |
var ErrTimeout = errors.New("timed out waiting for value") | |
// Value holds a string value and provides it in a concurrency-safe manner. | |
type Value interface { | |
// Get returns the current value, blocking until a value is available. | |
// | |
// Get will never return an empty string. | |
Get() string | |
// GetWithTimeout returns the current value, or returns ErrTimeout if the | |
// specified timeout is exceeded. | |
GetWithTimeout(time.Duration) (string, error) | |
// Set sets the current value. | |
Set(string) | |
// Clear clears the current value. | |
Clear(string) | |
} | |
// NewValue returns a Value that holds a string value and provides it in a | |
// concurrency-safe manner. | |
func NewValue() Value { | |
v := value{ | |
newValCh: make(chan string), | |
getValCh: make(chan string), | |
clearCh: make(chan string), | |
} | |
go v.mux() | |
return v | |
} | |
type value struct { | |
newValCh chan string | |
getValCh chan string | |
clearCh chan string | |
} | |
func (v value) mux() { | |
var cur string | |
for { | |
if cur == "" { // don't send an empty string. | |
select { | |
case cur = <-v.newValCh: // got a new value. | |
case <-v.clearCh: // clearing should not block while cur == "" | |
} | |
} | |
select { | |
case cur = <-v.newValCh: // set the current value. | |
case v.getValCh <- cur: // send the current value. | |
case c := <-v.clearCh: // request to clear the value. | |
if c == cur { // only clear if it matches the current value. | |
cur = "" | |
} | |
} | |
} | |
} | |
func (v value) Get() string { | |
return <-v.getValCh // blocks until there is a value to send. | |
} | |
func (v value) GetWithTimeout(d time.Duration) (string, error) { | |
select { | |
case <-time.After(d): // wait d, then return error | |
return "", ErrTimeout | |
case cur := <-v.getValCh: | |
return cur, nil | |
} | |
} | |
func (v value) Set(s string) { | |
v.newValCh <- s // blocks until it can send. | |
} | |
func (v value) Clear(s string) { | |
v.clearCh <- s // blocks until it can send. | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package val | |
import ( | |
"testing" | |
"time" | |
) | |
func TestValue(t *testing.T) { | |
v := NewValue() | |
v.Set("foo") | |
if got, want := v.Get(), "foo"; got != want { | |
t.Errorf("Get() got %q, want %q", got, want) | |
} | |
v.Set("bar") | |
if got, want := v.Get(), "bar"; got != want { | |
t.Errorf("Get() got %q, want %q", got, want) | |
} | |
if got, want := v.Get(), "bar"; got != want { | |
t.Errorf("Get() got %q, want %q", got, want) | |
} | |
v.Clear("other") // has no effect, does not block. | |
if got, want := v.Get(), "bar"; got != want { | |
t.Errorf("Get() got %q, want %q", got, want) | |
} | |
v.Clear("bar") | |
// Attempt to Get while there is no value, should block indefinitely. | |
done := make(chan struct{}) | |
go func() { | |
defer close(done) | |
v.Get() | |
}() | |
select { | |
case <-time.After(100 * time.Millisecond): // expected. | |
case <-done: | |
t.Errorf("Get() returned, expected it to block") | |
} | |
if got, err := v.GetWithTimeout(100 * time.Millisecond); err != ErrTimeout { | |
t.Errorf("GetWithTimeout() got %q (err: %v), want timeout", got, err) | |
} | |
v.Clear("bar") // has no effect, does not block. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment