Documentation
¶
Overview ¶
Package completion is the one-shot "this background operation has finished" signal that the viewer grew nine hand-rolled copies of, and that internal/ui/exifwin grew a tenth for the Location-section map prefetch: a channel replaced at the start of each request and closed when that request finishes, which the test suite waits on instead of polling widget state a producer goroutine may still be writing.
The rule it makes unbreakable is the one those nine copies could only state in prose: a request that has been superseded must still close its own channel, without touching the field a newer request now owns. Begin hands back a func closed over this generation's channel, so a stale producer cannot reach the newer one even by accident.
It is deliberately viewer-independent: no Fyne types, no fyne.Do, no UI marshaling. The caller decides what counts as stale and what finishing means; Signal answers only "has the generation I am looking at finished yet".
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Handle ¶
type Handle struct {
// contains filtered or unexported fields
}
Handle names one generation of a Signal. It keeps naming that generation for good, which is what separates it from Signal.Wait: the Signal moves on, a Handle does not. The zero Handle has nothing to wait for.
type Signal ¶
type Signal struct {
// contains filtered or unexported fields
}
Signal is a replaceable one-shot completion signal: begun by one generation at a time, finished when that generation's work is done. The zero Signal is ready to use and reports Begun() == false.
Safe for concurrent use. That matters more than it looks: the fields this type replaces were written by background goroutines and read by the test goroutine with nothing synchronizing the two, which is the hazard openfiles.go's runFileChooser was split out to dodge.
func (*Signal) Begin ¶
func (s *Signal) Begin() (done func())
Begin supersedes any generation already in flight and returns the function that finishes *this* one. Call it exactly where the old code did `defer close(done)`.
The returned func is idempotent: calling it twice is a no-op rather than the panic a repeated close(chan) would be, so a retry chain that can reach its finish along two paths stays correct.
Deliberately no way to get the channel itself: the whole point is that a superseded producer holds a closer over its own generation and nothing else.
func (*Signal) Begun ¶
Begun reports whether Begin has ever been called - "did this operation ever start", not "is it still running". It replaces the `!= nil` checks tests used to make against the raw channel fields, and is monotonic for the same reason those were: nothing ever puts a Signal back to its zero state.
func (*Signal) Current ¶
Current returns a Handle naming the generation in flight right now, so a caller can wait out *that* request even after a newer one has superseded it. Taken from a Signal that has never begun, the zero Handle waits for nothing.
This exists for one real case: a test that starts a request, starts a second one that replaces it, and then wants to prove the first one's goroutine actually exited. Waiting on the Signal itself would wait on the second generation and prove nothing.
func (*Signal) Wait ¶
Wait blocks until the generation current *at the moment Wait is called* has finished, or until ctx is done, whichever happens first. It returns ctx.Err() only in the latter case.
A Signal that has never begun has nothing to wait for and returns nil immediately - the "a viewer that never scanned has nothing to drain" case, which callers would otherwise each have to special-case.
The channel is snapshotted under the lock and waited on outside it, so a waiter never blocks a producer's Begin. A generation that starts after this snapshot is not waited for, exactly as reading the old channel field once and selecting on it behaved. A caller that needs to keep waiting on a specific generation across a supersession takes a Handle instead.
The application never needs this; tests do.