Documentation
¶
Overview ¶
Package contextutil provides context lifecycle helpers shared by transports and middleware.
Index ¶
- func CarrierLookup(c Carrier, key any) (any, bool)
- func EffectiveError(ctx context.Context) error
- func PinnedValue(ctx context.Context, key any) (any, bool)
- func TryPinValue(ctx context.Context, key, value any) bool
- func TrySetValueProvider(ctx context.Context, provider ValueProvider) bool
- func TryUpdatePinnedValueLocked[T comparable](ctx context.Context, key any, old T, update func() T) (T, bool)
- func UpdatePinnedTransitionLocked(ctx context.Context, key any, t PinTransition) (any, bool)
- type Carrier
- type LazyDeadline
- func (c *LazyDeadline) Cancel()
- func (c *LazyDeadline) Deadline() (time.Time, bool)
- func (c *LazyDeadline) Done() <-chan struct{}
- func (c *LazyDeadline) Err() error
- func (c *LazyDeadline) Pinned(key any) (any, bool)
- func (c *LazyDeadline) TryPin(key, value any) bool
- func (c *LazyDeadline) TrySetProvider(provider ValueProvider) bool
- func (c *LazyDeadline) UpdatePinLocked(key any, update func(current any) (next any, ok bool)) (any, bool)
- func (c *LazyDeadline) UpdatePinTransitionLocked(key any, t PinTransition) (any, bool)
- func (c *LazyDeadline) Value(key any) any
- type PinTransition
- type ValueProvider
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CarrierLookup ¶ added in v1.8.0
CarrierLookup answers the carrier-discovery key on behalf of an implementation: a Carrier's Value method delegates to it before its own lookups, so the package-level helpers can find the carrier through any number of derived contexts.
func EffectiveError ¶
EffectiveError reports a context cancellation even at the narrow deadline boundary where the wall clock has reached Deadline but the context timer goroutine has not yet published Err. This matters after a socket operation: the kernel deadline can return first, and treating that I/O timeout as upstream evidence would leak a request-local failure into shared state.
func PinnedValue ¶
PinnedValue returns the exact value stored in the carrier's request-lifetime slot for key. Unlike ctx.Value, it never falls through to a value provider or parent context.
func TryPinValue ¶
TryPinValue stores one request-lifetime control value directly on the carrier reachable through ctx. The pin survives pooled metadata reuse without allocating a context.WithValue node. It is deliberately not exposed through Context.Value: callers must use PinnedValue, keeping the ordinary context value chain immutable. Re-pinning a key that is already pinned fails, as does pinning into a full table.
func TrySetValueProvider ¶
func TrySetValueProvider(ctx context.Context, provider ValueProvider) bool
TrySetValueProvider installs provider on the carrier reachable through ctx. It returns false for ordinary contexts or when a provider was already installed.
func TryUpdatePinnedValueLocked ¶
func TryUpdatePinnedValueLocked[T comparable]( ctx context.Context, key any, old T, update func() T, ) (T, bool)
TryUpdatePinnedValueLocked computes and installs a replacement while holding the carrier's request-lifetime lock. It is intended for transitions whose initialization must complete before a concurrent request owner can close and recycle adjacent pooled state.
update must be bounded and must not call the pin mutation APIs recursively on ctx (or a context derived from it): the pin lock is deliberately non-reentrant. Cancellation methods use separate locks, so update may safely inspect Done, Err or context.AfterFunc. The Locked suffix exposes the callback contract at each call site.
func UpdatePinnedTransitionLocked ¶ added in v1.8.0
UpdatePinnedTransitionLocked runs t against the carrier reachable through ctx. It exists for hot transitions: the closure-based TryUpdatePinnedValueLocked allocates its adapter and captures on every call, which a live profile priced at two objects per materialized request.
Types ¶
type Carrier ¶ added in v1.8.0
type Carrier interface {
context.Context
TryPin(key, value any) bool
Pinned(key any) (any, bool)
UpdatePinLocked(key any, update func(current any) (next any, ok bool)) (any, bool)
// UpdatePinTransitionLocked is UpdatePinLocked with the transition
// carried as an interface value instead of a closure. A closure
// forces its captures to the heap at every call site; a transition
// implemented on state the caller already owns rides its existing
// pointer and costs nothing. Same lock, same reentrancy and non-nil
// rules.
UpdatePinTransitionLocked(key any, t PinTransition) (any, bool)
TrySetProvider(provider ValueProvider) bool
}
Carrier is the request-lifetime anchor a context exposes: the pin table and the value provider that request-scoped machinery (response metadata, recursion-work accounting, the resolution-attempt guard, caches' memo state) rides on without deriving value contexts.
Two implementations exist by design: *LazyDeadline, the ordinary request context, and the server's job carrier, which serves the allocation-free fast path and is recycled between requests. Everything that pins or reads request-lifetime values goes through the package-level helpers (TryPinValue, PinnedValue, TryUpdatePinnedValueLocked, TrySetValueProvider), which resolve the carrier from the context and work identically on either implementation.
Contract for implementers:
- TryPin stores value under key exactly once; re-pinning a key that is already pinned fails, as does pinning into a full table. key and value are never nil interfaces.
- Pinned returns the exact stored value. It must be safe under the implementation's declared concurrency (LazyDeadline: lock-free readers; the job carrier: single-owner with a cheap lock).
- UpdatePinLocked calls update with the current value while holding the carrier's pin lock and stores the returned value when ok. update must be bounded and must not call the pin APIs recursively on the same carrier; the lock is deliberately non-reentrant. The stored value must not be a nil interface. When the slot is missing it returns (nil, false); when update declines it returns (current, false).
- TrySetProvider installs the value provider once; a second install fails.
func CarrierFrom ¶ added in v1.8.0
CarrierFrom returns the carrier reachable through ctx, or nil.
type LazyDeadline ¶
type LazyDeadline struct {
// contains filtered or unexported fields
}
func WithLazyDeadline ¶
func WithLazyDeadline(parent context.Context, deadline time.Time) *LazyDeadline
WithLazyDeadline returns a LazyDeadline with the earlier of deadline and the parent's deadline. Call Cancel when the request finishes.
func WithLazyTimeout ¶
func WithLazyTimeout(parent context.Context, timeout time.Duration) *LazyDeadline
WithLazyTimeout returns a LazyDeadline whose deadline is no later than the parent's. Call Cancel when the request finishes.
func (*LazyDeadline) Cancel ¶
func (c *LazyDeadline) Cancel()
Cancel releases an armed timer/parent registration, or records a terminal cause without arming one when the request completed on the fast path.
func (*LazyDeadline) Deadline ¶
func (c *LazyDeadline) Deadline() (time.Time, bool)
Deadline implements context.Context.
func (*LazyDeadline) Done ¶
func (c *LazyDeadline) Done() <-chan struct{}
Done implements context.Context. Its first live call materializes the standard-library deadline context so timer and parent cancellation semantics remain identical on paths that actually block.
func (*LazyDeadline) Err ¶
func (c *LazyDeadline) Err() error
Err implements context.Context. A parent cancellation or elapsed deadline materializes the standard context to pin the first terminal cause.
func (*LazyDeadline) Pinned ¶ added in v1.8.0
func (c *LazyDeadline) Pinned(key any) (any, bool)
Pinned implements Carrier: it returns the exact value stored in the request-lifetime slot for key, lock-free.
func (*LazyDeadline) TryPin ¶ added in v1.8.0
func (c *LazyDeadline) TryPin(key, value any) bool
TryPin implements Carrier. Re-pinning a key that is already pinned fails, as does pinning into a full table.
func (*LazyDeadline) TrySetProvider ¶ added in v1.8.0
func (c *LazyDeadline) TrySetProvider(provider ValueProvider) bool
TrySetProvider implements Carrier. A second install fails.
func (*LazyDeadline) UpdatePinLocked ¶ added in v1.8.0
func (c *LazyDeadline) UpdatePinLocked( key any, update func(current any) (next any, ok bool), ) (any, bool)
UpdatePinLocked implements Carrier: update runs with the current slot value while pinMu is held, and its return value is stored when ok. See the Carrier contract for the reentrancy and non-nil rules.
func (*LazyDeadline) UpdatePinTransitionLocked ¶ added in v1.8.0
func (c *LazyDeadline) UpdatePinTransitionLocked(key any, t PinTransition) (any, bool)
UpdatePinTransitionLocked implements Carrier; see the interface contract. The body mirrors UpdatePinLocked with the transition on an interface instead of a closure.
func (*LazyDeadline) Value ¶
func (c *LazyDeadline) Value(key any) any
Value implements context.Context. Once materialized, values are deliberately routed through the standard child context. Besides preserving values, this lets context.WithCancel/WithDeadline recognize its private parent fast path instead of spawning a propagation goroutine.
type PinTransition ¶ added in v1.8.0
PinTransition computes a pinned value's replacement while the pin lock is held. NextLocked follows UpdatePinLocked's callback contract: it must be bounded, must not touch the pin APIs reentrantly, and a false return leaves the current value in place.
type ValueProvider ¶
ValueProvider supplies request-local context values without adding another context.WithValue wrapper to a LazyDeadline. Providers are installed before the context is published to middleware and must be safe for concurrent use.