clock

package
v0.1.0-develop.2026060... Latest Latest
Warning

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

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package clock is GoCell's platform-level Clock abstraction.

All production code that depends on the current time, elapsed durations, timer firing, ticker delivery, callback scheduling, or context-aware blocking sleep must accept a Clock through its constructor and call into the injected instance. Two complementary archtest gates enforce this contract (tools/archtest, both type-aware so import aliases, dot-imports, function-value references, and struct-field assignments are uniformly covered):

  • PROD-CLOCK-INJECTION-01 forbids direct stdlib time entry points in production code (time.Now / time.Since / time.Until / time.NewTimer / time.NewTicker / time.After / time.AfterFunc / time.Tick / time.Sleep). Whitelist: kernel/clock and kernel/clock/clockmock; pkg/securecookie keeps a thin local Clock interface that the higher layer satisfies structurally with a kernel/clock.Clock instance, since pkg/ is constrained by LAYER-01 to stdlib-only imports and cannot reach kernel/clock.

  • KERNEL-CLOCK-LEAF-FALLBACK-01 forbids leaf-level construction via kernel/clock.Real() outside the composition root. Whitelist: the Real() factory definition itself (kernel/clock/clock.go), the main and example composition roots (cmd/corebundle/, gocell.go, examples/*/main.go, examples/ssobff/app.go), and the e2e suite's own composition root (tests/e2e/internal/clients/clients.go). _test.go files are out of scope — test-side cleanup is tracked separately as G12-TEST-CLOCK-REAL-CLEANUP.

Test code should use github.com/ghbvf/gocell/kernel/clock/clockmock which provides a deterministic Clock implementation whose progress is controlled explicitly via Advance and Set.

Composition root convention: a single Real instance is constructed at process start (cmd/corebundle/bundle.go and gocell.go are the only legitimate callers) and threaded through to every consumer. Constructors must declare clock as a required parameter — no default fallback, no Option-style optional injection — and validate at the boundary via the public helper MustHaveClock. The root clock is passed as the first positional parameter to assembly.New(clk, cfg) and bootstrap.New(clk, opts...) so that the assembly auto-propagates it to every cell's Init.

Absolute-time vs relative-time timer API:

  • NewTimerAt + ResetAt form the absolute-time API. Callers supply a time.Time deadline computed once; the timer is armed atomically without any need to read Now() at arm time. This eliminates the read-then-act race (capture deadline → goroutine preempted → arm timer using stale Now()) that the relative-duration API inherits from stdlib.

  • NewTimer (via NewTimerAt(c.Now().Add(d))) + Reset(d) form the relative-time API. These mirror stdlib time.NewTimer / time.Timer.Reset ergonomics for callers that only need "fire after d". Prod code must still use them through an injected Clock, not stdlib directly.

ref: docs/architecture/202605021500-adr-kernel-clock-injection.md (ADR + 2026-05-02 closure) ref: jonboulle/clockwork — caller-required, no-default Clock parity ref: k8s.io/client-go SharedIndexInformer — single root threaded down

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MustHaveClock

func MustHaveClock(c Clock, ctx string)

MustHaveClock panics when c is nil or when c is an interface value wrapping a nil pointer (typed-nil). Use at construction boundaries to fail fast on missing Clock wiring; the panic is wrapped with panicregister.Approved per PANIC-REGISTERED-01.

ctx names the call site (e.g. "assembly.New") so the panic message identifies which constructor is missing wiring.

ref: docs/architecture/202604270030-architectural-panic-whitelist.md §4 We share a single helper across kernel/assembly, runtime/bootstrap, runtime/http/health, etc. so the panic message stays uniform and the gate's auditing has exactly one location.

func MustHavePositiveInterval

func MustHavePositiveInterval(d time.Duration, ctx string)

MustHavePositiveInterval panics when d is non-positive. Mirrors stdlib time.NewTicker / time.Ticker.Reset semantics (both panic on d <= 0). ctx names the call site so callers can identify the misuse from the panic.

Types

type Clock

type Clock interface {
	// Now returns the current time.
	Now() time.Time

	// Since returns the time elapsed since t. It is shorthand for Now().Sub(t).
	Since(t time.Time) time.Duration

	// Until returns the duration until t. It is shorthand for t.Sub(Now()).
	Until(t time.Time) time.Duration

	// NewTimerAt creates a [Timer] that fires when the clock reaches deadline.
	//
	// The absolute-deadline shape eliminates the read-then-act gap that a
	// duration-based API would require. Callers wanting "fire after d" should
	// write c.NewTimerAt(c.Now().Add(d)).
	NewTimerAt(deadline time.Time) Timer

	// NewTicker creates a [Ticker] that fires every interval.
	//
	// First fire is at Now()+interval, matching stdlib time.NewTicker
	// semantics. interval must be > 0; non-positive values panic with the
	// same message as time.NewTicker.
	//
	// Callers must call Ticker.Stop when done; the fake implementation also
	// requires Stop to release internal references.
	NewTicker(interval time.Duration) Ticker

	// AfterFunc schedules fn to run when the clock reaches deadline. The
	// returned [Timer] can be used to cancel the callback (Stop returns true
	// if cancelation prevented the run) or to re-arm it (Reset).
	//
	// fn runs on its own goroutine and must not block the clock. Past
	// deadlines schedule fn for immediate execution.
	AfterFunc(deadline time.Time, fn func()) Timer

	// Sleep blocks until the clock reaches until or ctx is canceled,
	// whichever comes first. Returns ctx.Err() on cancelation, nil
	// otherwise. A non-positive remaining duration returns immediately
	// (ctx.Err() if ctx is already done, nil otherwise).
	Sleep(ctx context.Context, until time.Time) error
}

Clock is the canonical time source for GoCell production code.

All methods must be safe for concurrent use.

func Real

func Real() Clock

Real returns a Clock backed by the standard library's wall-clock time source. The returned value is safe for concurrent use and is the only production Clock implementation; all production wiring routes through it.

type Ticker

type Ticker interface {
	// C returns the channel that receives a value on each tick. The channel
	// is buffered to capacity 1; a slow receiver causes ticks to be
	// coalesced (matches stdlib time.Ticker semantics).
	C() <-chan time.Time

	// Stop halts the ticker. It must be called to release internal
	// references; failing to call Stop leaks the ticker indefinitely. Stop
	// does not close C.
	Stop()

	// Reset changes the ticker's interval. If interval is non-positive,
	// Reset panics with the same message as stdlib time.Ticker.Reset.
	Reset(interval time.Duration)
}

Ticker fires repeatedly at a fixed interval, mirroring stdlib time.Ticker.

type Timer

type Timer interface {
	// C returns the channel that receives a single value when the timer fires.
	//
	// AfterFunc-created timers do not deliver on C; their callback is
	// invoked on a separate goroutine. C is still safe to read but will
	// never receive a value.
	C() <-chan time.Time

	// Stop prevents the timer from firing. It returns true if the call stops
	// the timer, false if the timer has already expired or been stopped.
	Stop() bool

	// Reset re-arms the timer to fire after d. It returns true if the timer
	// was active when Reset was called, false if it had already expired or
	// been stopped.
	Reset(d time.Duration) bool

	// ResetAt re-arms the timer to fire at the given absolute deadline.
	// Use this instead of Reset(clk.Until(deadline)) to eliminate the
	// read-then-act race between reading Now() and arming the timer.
	// It returns true if the timer was active when ResetAt was called.
	ResetAt(deadline time.Time) bool
}

Timer is a single-fire timer created by a Clock.

Directories

Path Synopsis
Package clockmock provides a deterministic clock.Clock implementation driven by explicit FakeClock.Advance and FakeClock.Set calls.
Package clockmock provides a deterministic clock.Clock implementation driven by explicit FakeClock.Advance and FakeClock.Set calls.

Jump to

Keyboard shortcuts

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