// Package watch provides an observable value: a [Value] that can be updated and // one or more [Observer] handles that observe its changes. // // It is the Go analog of iroh's n0_watcher (Watchable + Watcher). An [Observer] // exposes the current value, a one-shot wait for the next change, and an // iterator stream of values. The root package uses Observer for APIs such as // Endpoint.WatchAddr. // // The Go API is not stable before v1 and may change in any v0 release.
package watch import ( ) // Value is an observable container holding a T. It is safe for concurrent use. // Updates are published to all [Observer] handles created from it. // // The zero Value holds the zero T and is ready to use. type Value[ any] struct { mu sync.Mutex val set bool version uint64 notify chan struct{} // closed and replaced on each Set equal func(a, b ) bool } // NewValue returns a Value initialized to v. func [ any]( ) *Value[] { return &Value[]{val: , set: true} } // NewValueFunc returns a Value initialized to v. The equal function, if non-nil, // suppresses notifications for no-op updates. func [ any]( , func(, ) bool) *Value[] { return &Value[]{val: , set: true, equal: } } // Set updates the value and notifies all watchers if it changed. func ( *Value[]) ( ) { .mu.Lock() defer .mu.Unlock() if .set && .equal != nil && .equal(.val, ) { return } .val = .set = true .version++ if .notify != nil { close(.notify) .notify = nil } } // Current returns the current value. func ( *Value[]) () { .mu.Lock() defer .mu.Unlock() return .val } // Watch returns an [Observer] observing this value. func ( *Value[]) () Observer[] { return &watcher[]{src: } } // changed returns the current value, version, and a channel closed on the next // change after the given version. func ( *Value[]) ( uint64) (, uint64, <-chan struct{}) { .mu.Lock() defer .mu.Unlock() if .version != { return .val, .version, closedChan } if .notify == nil { .notify = make(chan struct{}) } return .val, .version, .notify } var closedChan = func() chan struct{} { := make(chan struct{}) close() return }() // Observer observes a [Value]. Multiple observers may observe the same value // independently. It is the Go analog of iroh's n0_watcher::Watcher. type Observer[ any] interface { // Current returns the current value. Current() // Updated blocks until the value changes after the watcher's last observed // version, returning the new value, or ctx.Err() if the context is done. // The first call returns the current value immediately. Updated(ctx context.Context) (, error) // Stream returns an iterator that yields each new value until ctx is done. // The current value is delivered first. Stream(ctx context.Context) iter.Seq[] } type watcher[ any] struct { src *Value[] seen uint64 once bool } func ( *watcher[]) () { return .src.Current() } func ( *watcher[]) ( context.Context) (, error) { if !.once { .once = true , , := .src.waitChan(^uint64(0)) // force "current" on first call .seen = return , nil } for { , , := .src.waitChan(.seen) if != .seen { .seen = return , nil } select { case <-: case <-.Done(): var return , .Err() } } } func ( *watcher[]) ( context.Context) iter.Seq[] { // Independent cursor so Stream doesn't disturb Updated on the same watcher. := &watcher[]{src: .src} return func( func() bool) { for { , := .Updated() if != nil { return } if !() { return } } } }