worker

package
v0.1.26 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2026 License: MIT Imports: 2 Imported by: 0

README

worker

Generic goroutine-lifecycle primitive for the SDK kernel. Stdlib-only.

import "github.com/kitsunium/sdk/internal/kernel/worker"

// Spawn a background loop. It MUST return promptly once stop is closed.
d := worker.Start(func(stop <-chan struct{}) {
	for {
		select {
		case <-stop:
			return
		default:
			// ... do a unit of work ...
		}
	}
})

// Stop is idempotent and joins: it returns only after the loop has returned.
d.Stop()

LoopDaemon wraps a single goroutine with an idempotent Stop (close-once + join) and a Done channel closed when the loop returns. Every layers a ticker loop on top:

d := worker.Every(time.Second, func() { /* fires each interval */ })
d.Stop() // stops the ticker and joins the goroutine

It collapses the stop/stopOnce/done/doneOnce scaffold the async drainer and the s3/cloudwatch batching sinks each hand-rolled. Emits no errors.

See ADR 0014 §D6 for the design rationale.

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.

Jump to

Keyboard shortcuts

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