Documentation
¶
Overview ¶
Package worker — Every layers a ticker loop on top of the LoopDaemon lifecycle. Held in its own file so worker.go stays focused on the LoopDaemon surface.
Package worker provides LoopDaemon, a generic goroutine lifecycle primitive. A LoopDaemon spawns a single background goroutine running a Loop, exposes an idempotent Stop that signals the loop to exit and joins it, and a Done channel closed once the loop has returned. Stdlib-only and domain-neutral: any background drainer, batcher, ticker, or watch loop can reuse it.
Three real consumers collapse onto this primitive — the async middleware drainer and the s3/cloudwatch batching sinks — each previously carried a byte-identical stop/stopOnce/done/doneOnce scaffold. The mechanism lives here; the loop body stays with the consumer (ADR 0014 §D6).
The "Daemon" role suffix (the linter requires a recognized role suffix on every exported struct, and the bare noun is rejected); Start / Every are the idiomatic constructors, with NewLoopDaemon as the New-prefixed alias the struct-constructor lint expects.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Loop ¶
type Loop func(stop <-chan struct{})
Loop is the goroutine body run by a LoopDaemon. It receives a stop channel that is closed when Stop is called and MUST return promptly once stop is closed — a Loop that ignores stop deadlocks Stop, which blocks on the join. A Loop MUST NOT panic: it runs in a bare goroutine, so a panic crashes the process. Callers that run risky work recover inside their own Loop.
type LoopDaemon ¶
type LoopDaemon struct {
// contains filtered or unexported fields
}
LoopDaemon is a running background goroutine with an idempotent Stop and a join. It is a concrete struct (not an interface) by deliberate choice, mirroring recycler.Pool / snapshot.Value — a single-impl interface would be over-abstraction. Always used behind a pointer returned by Start: the embedded sync.Once and channels must not be copied.
func Every ¶
func Every(interval time.Duration, tick func()) *LoopDaemon
Every starts a LoopDaemon whose loop fires tick on each interval until Stop is called. The daemon owns the time.Ticker and stops it when the loop exits, so Stop both ends the ticking and joins the goroutine. A non-positive interval panics: time.NewTicker itself panics on a non-positive duration, so Every surfaces the same contract at its own call site rather than deep in the loop.
func NewLoopDaemon ¶
func NewLoopDaemon(loop Loop) *LoopDaemon
NewLoopDaemon is the New-prefixed constructor alias mandated by the struct-constructor lint; it delegates to Start, which is the idiomatic verb.
func Start ¶
func Start(loop Loop) *LoopDaemon
Start spawns loop in a new goroutine and returns the LoopDaemon controlling it. The returned daemon's Done channel closes when loop returns; Stop signals loop to exit and blocks until it has. A nil loop is a programmer error and panics at construction rather than spawning a goroutine that does nothing.
func (*LoopDaemon) Done ¶
func (d *LoopDaemon) Done() <-chan struct{}
Done returns the channel closed when the loop has returned. Callers that want to react to loop exit without joining (which Stop does) select on it.
func (*LoopDaemon) Stop ¶
func (d *LoopDaemon) Stop()
Stop signals the loop to exit and blocks until it has returned. It is idempotent and safe to call concurrently: the sync.Once guards close(stop), and the join on done is a receive on a closed channel after the first call.