voyeur

package
v0.0.0-...-9d78121 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 4, 2020 License: LGPL-3.0 Imports: 1 Imported by: 33

Documentation

Overview

Package voyeur implements a concurrency-safe value that can be watched for changes.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Value

type Value struct {
	// contains filtered or unexported fields
}

Value represents a shared value that can be watched for changes. Methods on a Value may be called concurrently. The zero Value is ok to use, and is equivalent to a NewValue result with a nil initial value.

func NewValue

func NewValue(initial interface{}) *Value

NewValue creates a new Value holding the given initial value. If initial is nil, any watchers will wait until a value is set.

func (*Value) Close

func (v *Value) Close() error

Close closes the Value, unblocking any outstanding watchers. Close always returns nil.

func (*Value) Closed

func (v *Value) Closed() bool

Closed reports whether the value has been closed.

func (*Value) Get

func (v *Value) Get() interface{}

Get returns the current value.

func (*Value) Set

func (v *Value) Set(val interface{})

Set sets the shared value to val.

func (*Value) Watch

func (v *Value) Watch() *Watcher

Watch returns a Watcher that can be used to watch for changes to the value.

type Watcher

type Watcher struct {
	// contains filtered or unexported fields
}

Watcher represents a single watcher of a shared value.

func (*Watcher) Close

func (w *Watcher) Close()

Close closes the Watcher without closing the underlying value. It may be called concurrently with Next.

func (*Watcher) Next

func (w *Watcher) Next() bool

Next blocks until there is a new value to be retrieved from the value that is being watched. It also unblocks when the value or the Watcher itself is closed. Next returns false if the value or the Watcher itself have been closed.

Example
package main

import (
	"fmt"

	"github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"
)

type suite struct {
	testing.IsolationSuite
}

var _ = gc.Suite(&suite{})

func main() {
	v := NewValue(nil)

	// The channel is not necessary for normal use of the watcher.
	// It just makes the test output predictable.
	ch := make(chan bool)

	go func() {
		for x := 0; x < 3; x++ {
			v.Set(fmt.Sprintf("value%d", x))
			ch <- true
		}
		v.Close()
	}()
	w := v.Watch()
	for w.Next() {
		fmt.Println(w.Value())
		<-ch
	}

}

func (s *suite) TestValueGetSet(c *gc.C) {
	v := NewValue(nil)
	expected := "12345"
	v.Set(expected)
	got := v.Get()
	c.Assert(got, gc.Equals, expected)
	c.Assert(v.Closed(), jc.IsFalse)
}

func (s *suite) TestValueInitial(c *gc.C) {
	expected := "12345"
	v := NewValue(expected)
	got := v.Get()
	c.Assert(got, gc.Equals, expected)
	c.Assert(v.Closed(), jc.IsFalse)
}

func (s *suite) TestValueClose(c *gc.C) {
	expected := "12345"
	v := NewValue(expected)
	c.Assert(v.Close(), gc.IsNil)

	isClosed := v.Closed()
	c.Assert(isClosed, jc.IsTrue)
	got := v.Get()
	c.Assert(got, gc.Equals, expected)

	// test that we can close multiple times without a problem
	c.Assert(v.Close(), gc.IsNil)
}

func (s *suite) TestWatcher(c *gc.C) {
	vals := []string{"one", "two", "three"}

	// blocking on the channel forces the scheduler to let the other goroutine
	// run for a bit, so we get predictable results.  This is not necessary for
	// normal use of the watcher.
	ch := make(chan bool)

	v := NewValue(nil)

	go func() {
		for _, s := range vals {
			v.Set(s)
			ch <- true
		}
		v.Close()
	}()

	w := v.Watch()
	c.Assert(w.Next(), jc.IsTrue)
	c.Assert(w.Value(), gc.Equals, vals[0])

	// test that we can get the same value multiple times
	c.Assert(w.Value(), gc.Equals, vals[0])
	<-ch

	// now try skipping a value by calling next without getting the value
	c.Assert(w.Next(), jc.IsTrue)
	<-ch

	c.Assert(w.Next(), jc.IsTrue)
	c.Assert(w.Value(), gc.Equals, vals[2])
	<-ch

	c.Assert(w.Next(), jc.IsFalse)
}

func (s *suite) TestDoubleSet(c *gc.C) {
	vals := []string{"one", "two", "three"}

	// blocking on the channel forces the scheduler to let the other goroutine
	// run for a bit, so we get predictable results.  This is not necessary for
	// normal use of the watcher.
	ch := make(chan bool)

	v := NewValue(nil)

	go func() {
		v.Set(vals[0])
		ch <- true
		v.Set(vals[1])
		v.Set(vals[2])
		ch <- true
		v.Close()
		ch <- true
	}()

	w := v.Watch()
	c.Assert(w.Next(), jc.IsTrue)
	c.Assert(w.Value(), gc.Equals, vals[0])
	<-ch

	// since we did two sets before sending on the channel,
	// we should just get vals[2] here and not get vals[1]
	c.Assert(w.Next(), jc.IsTrue)
	c.Assert(w.Value(), gc.Equals, vals[2])
}

func (s *suite) TestTwoReceivers(c *gc.C) {
	vals := []string{"one", "two", "three"}

	// blocking on the channel forces the scheduler to let the other goroutine
	// run for a bit, so we get predictable results.  This is not necessary for
	// normal use of the watcher.
	ch := make(chan bool)

	v := NewValue(nil)

	watcher := func() {
		w := v.Watch()
		x := 0
		for w.Next() {
			c.Assert(w.Value(), gc.Equals, vals[x])
			x++
			<-ch
		}
		c.Assert(x, gc.Equals, len(vals))
		<-ch
	}

	go watcher()
	go watcher()

	for _, val := range vals {
		v.Set(val)
		ch <- true
		ch <- true
	}

	v.Close()
	ch <- true
	ch <- true
}

func (s *suite) TestCloseWatcher(c *gc.C) {
	vals := []string{"one", "two", "three"}

	// blocking on the channel forces the scheduler to let the other goroutine
	// run for a bit, so we get predictable results.  This is not necessary for
	// normal use of the watcher.
	ch := make(chan bool)

	v := NewValue(nil)

	w := v.Watch()
	go func() {
		x := 0
		for w.Next() {
			c.Assert(w.Value(), gc.Equals, vals[x])
			x++
			<-ch
		}
		// the value will only get set once before the watcher is closed
		c.Assert(x, gc.Equals, 1)
		<-ch
	}()

	v.Set(vals[0])
	ch <- true
	w.Close()
	ch <- true

	// prove the value is not closed, even though the watcher is
	c.Assert(v.Closed(), jc.IsFalse)
}

func (s *suite) TestWatchZeroValue(c *gc.C) {
	var v Value
	ch := make(chan bool)
	go func() {
		w := v.Watch()
		ch <- true
		ch <- w.Next()
	}()
	<-ch
	v.Set(struct{}{})
	c.Assert(<-ch, jc.IsTrue)
}
Output:

value0
value1
value2

func (*Watcher) Value

func (w *Watcher) Value() interface{}

Value returns the last value that was retrieved from the watched Value by Next.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL