Documentation
¶
Overview ¶
Package sync provides small concurrency helpers.
This module is github.com/alexfalkowski/go-sync and exposes package sync.
Overview ¶
The package contains:
- Convenience aliases for common synchronization primitives and atomics.
- Hook-based helpers for running an operation with centralized error handling.
- Wait and Timeout helpers for coordinating an operation with a timeout.
- Worker: a bounded scheduler for running operations concurrently.
- Group helpers built on errgroup, errors.Join, and singleflight.
- Typed wrappers around sync.Pool, sync.Map, and sync/atomic.Value.
- BufferPool: a convenience pool for bytes.Buffer.
The package is intentionally small. Most types are either thin wrappers over the standard library or type aliases for widely used synchronization helpers.
Aliases ¶
Once, Mutex, RWMutex, and WaitGroup are aliases for their counterparts in the standard library sync package.
Int32, Int64, Uint32, Uint64, Uintptr, Bool, and Pointer[T] are aliases for atomic types from sync/atomic.
ErrorGroup is an alias for errgroup.Group. ErrorsGroup runs functions concurrently and returns all non-nil errors joined with errors.Join.
Hooks ¶
Many APIs accept a Hook. Hook.OnRun is the operation to execute and is required. Hook.OnError is optional and centralizes error handling; when set, it is invoked only when OnRun returns a non-nil error. If OnError is nil, errors are returned (or ignored) as described by the calling API.
Wait and Timeout return the result of hook.Error when the operation finishes before their own deadline logic wins the race. Worker never returns handler errors from Schedule; it only invokes hook.Error for side effects.
Timeouts ¶
There are two timeout-related helpers with different semantics:
Wait runs hook.OnRun and waits up to the provided timeout for it to complete. If the timeout expires (or the provided context is canceled) first, Wait returns nil without waiting for OnRun to finish. This makes Wait a “best effort” coordination helper rather than a cancellation mechanism. A non-positive timeout behaves the same way and returns nil without invoking Hook.OnRun.
Timeout runs hook.OnRun using a derived context with the provided timeout. If the context’s deadline expires (or it is canceled) first, Timeout returns the derived context's cancellation cause (typically ErrTimeout, context.Canceled, or a parent-provided cause). A non-positive timeout produces an already-expired derived context, so Timeout returns ErrTimeout without invoking Hook.OnRun.
In both helpers, returning from Wait or Timeout does not forcibly stop the goroutine running Hook.OnRun. If OnRun ignores context cancellation, it may continue running in the background even after the helper has returned.
In both cases, if Hook.OnRun is nil, the functions return ErrNoOnRunProvided.
Worker ¶
Worker schedules hook.OnRun to run asynchronously while bounding concurrency. Schedule blocks until the handler is scheduled or the provided timeout (via context.WithTimeoutCause) expires. Errors returned by OnRun are routed to hook.OnError (if set) and are not returned by Schedule. Use Worker.Wait to wait for all scheduled handlers to finish.
The zero value of Worker is not ready for use; construct one with NewWorker.
Groups ¶
ErrorsGroup runs functions concurrently and waits for all of them to finish. Wait returns all non-nil errors joined with errors.Join in the order the functions were passed to Go.
SingleFlightGroup[T] is a generic wrapper around singleflight.Group. Its zero value is ready for use. Do returns a typed result and preserves singleflight's shared-result behavior. When T is an interface type and the function returns a nil interface value, Do exposes that result as the zero value of T.
Typed wrappers ¶
Pool[T] is a typed wrapper around sync.Pool. Its zero value is not ready for use; construct one with NewPool[T].
BufferPool is a convenience wrapper over Pool[bytes.Buffer]. Its zero value is also not ready for use; construct one with NewBufferPool.
Value[T] is a typed wrapper around atomic.Value. Its zero value is ready for use. Load and Swap return the zero value of T if no value has been stored yet. When T is an interface type, storing a nil interface value has the same behavior as atomic.Value.Store(nil) and panics.
Map[K,V] is a typed wrapper around sync.Map. Its zero value is ready for use. If K is an interface type and a nil interface key is stored, Range exposes that entry's key as the zero value of K. If V is an interface type and a nil interface value is stored, methods that return values expose that entry as the zero value of V. Range follows the same semantics as sync.Map.Range and does not provide a consistent snapshot.
Index ¶
- Variables
- func IsTimeoutError(err error) bool
- func Timeout(ctx context.Context, timeout time.Duration, hook Hook) error
- func Wait(ctx context.Context, timeout time.Duration, hook Hook) error
- type Bool
- type BufferPool
- type ErrorGroup
- type ErrorHandler
- type ErrorsGroup
- type Handler
- type Hook
- type Int32
- type Int64
- type Map
- func (m *Map[K, V]) Clear()
- func (m *Map[K, V]) CompareAndDelete(key K, old V) bool
- func (m *Map[K, V]) CompareAndSwap(key K, old, new V) bool
- func (m *Map[K, V]) Delete(key K)
- func (m *Map[K, V]) Load(key K) (V, bool)
- func (m *Map[K, V]) LoadAndDelete(key K) (V, bool)
- func (m *Map[K, V]) LoadOrStore(key K, value V) (V, bool)
- func (m *Map[K, V]) Range(f func(key K, value V) bool)
- func (m *Map[K, V]) Store(key K, value V)
- func (m *Map[K, V]) Swap(key K, value V) (V, bool)
- type Mutex
- type Once
- type Pointer
- type Pool
- type RWMutex
- type SingleFlightGroup
- type Uint32
- type Uint64
- type Uintptr
- type Value
- type WaitGroup
- type Worker
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoOnRunProvided = errors.New("no OnRun handler provided")
ErrNoOnRunProvided is returned when [Hook.OnRun] is nil.
var ErrTimeout = fmt.Errorf("timeout: %w", context.DeadlineExceeded)
ErrTimeout is the timeout cause used by derived contexts in this package.
It wraps context.DeadlineExceeded, so errors.Is matches both values.
Functions ¶
func IsTimeoutError ¶
IsTimeoutError reports whether err matches ErrTimeout or context.DeadlineExceeded.
It uses errors.Is, so wrapped deadline-exceeded errors also report true.
func Timeout ¶
Timeout runs hook.OnRun with a derived context that has the given timeout.
Timeout differs from Wait in two key ways:
- Cancellation is propagated to OnRun by passing a derived context created by context.WithTimeoutCause. Well-behaved OnRun implementations should observe ctx.Done() and exit promptly.
- Timeout reports timeout/cancellation by returning context.Cause when the derived context becomes done before OnRun completes.
If OnRun completes before the derived context is done, Timeout returns hook.Error(ctx, hook.OnRun(ctx)) where ctx is the derived context.
Even after receiving the OnRun result, Timeout re-checks the derived context. If it is done at that point, Timeout returns context.Cause.
If the input ctx is already done on entry, Timeout returns its cancellation cause without invoking OnRun. If timeout <= 0, Timeout returns ErrTimeout without invoking OnRun.
As with Wait, returning from Timeout does not forcibly stop the goroutine running OnRun. If OnRun ignores ctx.Done(), it may continue running in the background. Hook.OnError may still run there, but Timeout discards its return value once the derived context has already ended.
If hook.OnRun is nil, Timeout returns ErrNoOnRunProvided.
Example ¶
err := sync.Timeout(context.Background(), 10*time.Millisecond, sync.Hook{
OnRun: func(ctx context.Context) error {
<-ctx.Done()
return context.Cause(ctx)
},
})
fmt.Println(sync.IsTimeoutError(err))
Output: true
func Wait ¶
Wait runs hook.OnRun and waits up to timeout for it to complete.
Wait is a “best effort” waiting helper. It does not cancel the work started by OnRun. Instead, it starts OnRun asynchronously and then waits for whichever happens first:
- OnRun completes: Wait returns hook.Error(ctx, hook.OnRun(ctx)).
- The timeout elapses: Wait returns nil immediately.
- ctx is done: Wait returns nil immediately.
Even after receiving the OnRun result, Wait re-checks timeout/context state before returning. If timeout has elapsed or ctx is done at that point, Wait returns nil.
Important: if the timeout elapses or ctx becomes done, Wait returns without waiting for OnRun to finish. The OnRun goroutine may continue running in the background. If OnRun later returns an error, Hook.OnError may still run in that goroutine, but Wait discards the final return value.
If ctx is already done on entry (or timeout <= 0), Wait returns nil without invoking OnRun.
If hook.OnRun is nil, Wait returns ErrNoOnRunProvided.
Example ¶
err := sync.Wait(context.Background(), time.Second, sync.Hook{
OnRun: func(context.Context) error {
return nil
},
})
fmt.Println(err == nil)
Output: true
Types ¶
type Bool ¶ added in v1.15.0
Bool is an alias for atomic.Bool.
It is provided for convenience so users of this package can refer to a typed atomic boolean without importing sync/atomic directly.
type BufferPool ¶
type BufferPool struct {
// contains filtered or unexported fields
}
BufferPool provides pooled bytes.Buffer values.
Buffers returned by BufferPool.Get should be considered temporarily borrowed by the caller. Return them to the pool via BufferPool.Put when finished to enable reuse and reduce allocations.
The zero value is not ready for use.
Example ¶
pool := sync.NewBufferPool()
buffer := pool.Get()
defer pool.Put(buffer)
buffer.WriteString("hello")
copy := pool.Copy(buffer)
fmt.Println(string(copy))
Output: hello
func NewBufferPool ¶
func NewBufferPool() *BufferPool
NewBufferPool returns a pointer to an initialized BufferPool.
The returned pool is ready for use and is backed by a generic Pool of bytes.Buffer values.
The zero value of BufferPool is not ready for use; construct one with NewBufferPool.
func (*BufferPool) Copy ¶ added in v1.1.0
func (p *BufferPool) Copy(buffer *bytes.Buffer) []byte
Copy returns a copy of the buffer contents as a new byte slice.
The returned slice does not alias the buffer's underlying array, so it is safe to keep after the buffer is returned to the pool.
If buffer is nil, Copy returns nil.
Example (Nil) ¶
pool := sync.NewBufferPool() fmt.Println(pool.Copy(nil) == nil)
Output: true
func (*BufferPool) Get ¶
func (p *BufferPool) Get() *bytes.Buffer
Get returns a buffer from the pool.
The returned buffer is empty. New buffers start zeroed, and BufferPool.Put resets buffers before returning them to the pool.
Example ¶
pool := sync.NewBufferPool()
buffer := pool.Get()
defer pool.Put(buffer)
buffer.WriteString("aaa")
fmt.Println(buffer.String())
Output: aaa
func (*BufferPool) Put ¶
func (p *BufferPool) Put(buffer *bytes.Buffer)
Put resets buffer and puts it back into the pool.
If buffer is nil, Put is a no-op.
type ErrorGroup ¶ added in v1.9.0
ErrorGroup is an alias for errgroup.Group.
It is provided for convenience so users of this package can refer to an errgroup without importing `golang.org/x/sync/errgroup` directly.
Note: this is a type alias, not a wrapper. All behavior, including how errors are captured and how `Wait` behaves, is defined by `errgroup.Group`.
Example ¶
var g sync.ErrorGroup
g.Go(func() error { return nil })
g.Go(func() error { return context.Canceled })
fmt.Println(g.Wait() != nil)
Output: true
type ErrorHandler ¶
ErrorHandler is the signature for [Hook.OnError].
It is invoked only when a non-nil error is returned from [Hook.OnRun]. If the ErrorHandler returns a different error, Hook.Error returns that error. Whether that value reaches the API caller depends on the helper invoking the hook.
type ErrorsGroup ¶ added in v1.24.0
type ErrorsGroup struct {
// contains filtered or unexported fields
}
ErrorsGroup runs functions concurrently and joins all returned errors.
Unlike ErrorGroup, which returns the first non-nil error reported by an errgroup, ErrorsGroup records every non-nil error and returns them from ErrorsGroup.Wait using errors.Join.
The zero value of ErrorsGroup is ready for use.
Example ¶
var g sync.ErrorsGroup
first := errors.New("first")
second := errors.New("second")
g.Go(func() error { return first })
g.Go(func() error { return second })
err := g.Wait()
fmt.Println(errors.Is(err, first), errors.Is(err, second))
Output: true true
func (*ErrorsGroup) Go ¶ added in v1.24.0
func (g *ErrorsGroup) Go(f func() error)
Go calls the given function in a new goroutine.
The first call to ErrorsGroup.Wait blocks until all functions started by Go have returned. Non-nil errors are joined in the order the functions were passed to Go, not the order they complete.
func (*ErrorsGroup) Wait ¶ added in v1.24.0
func (g *ErrorsGroup) Wait() error
Wait blocks until all functions started by ErrorsGroup.Go have returned, then returns all non-nil errors joined with errors.Join.
type Handler ¶
Handler is the signature for [Hook.OnRun].
The provided context.Context is the context used by the operation invoking the hook (for example, the original ctx passed to Wait, or the derived timeout context created by Timeout).
type Hook ¶
type Hook struct {
OnRun Handler
OnError ErrorHandler
}
Hook bundles handlers used by helpers in this package.
The helpers in this package call [Hook.OnRun] to perform work and then pass the returned error to Hook.Error, which applies [Hook.OnError] if configured.
[Hook.OnRun] must be non-nil; otherwise operations return ErrNoOnRunProvided.
Whether the value returned from Hook.Error is observed depends on the calling helper:
- Wait returns it only if OnRun finishes before timeout/cancellation wins.
- Timeout returns it only if OnRun finishes before the derived context ends.
- Worker.Schedule never returns it; handler errors are only observed via [Hook.OnError] side effects.
type Int32 ¶ added in v1.13.0
Int32 is an alias for atomic.Int32.
It is provided for convenience so users of this package can refer to a typed atomic integer without importing sync/atomic directly.
type Int64 ¶ added in v1.19.0
Int64 is an alias for atomic.Int64.
It is provided for convenience so users of this package can refer to a typed atomic integer without importing sync/atomic directly.
type Map ¶
type Map[K comparable, V any] struct { // contains filtered or unexported fields }
Map is a typed wrapper around sync.Map.
It provides a generic API while preserving sync.Map’s concurrency properties. This includes sync.Map's iteration semantics: Map.Range does not provide a consistent snapshot when concurrent stores and deletes are happening.
Zero value ¶
The zero value is ready for use.
Missing keys vs stored zero values ¶
Methods such as Map.Load, Map.LoadAndDelete, and Map.Swap return the zero value of V when a key is not present. Use the returned boolean to distinguish “not present” from a stored zero value.
Nil interface keys and values ¶
If K is an interface type, storing a nil interface key results in an untyped nil key being stored.
Map.Range exposes such keys as the zero value of K.
Internally, sync.Map stores values as `any`. This wrapper type-asserts stored values back to V for some operations. If V is an interface type, storing a nil interface value (for example, `var r io.Reader = nil`) results in an untyped nil being stored.
Methods that return values from the map (such as Map.Load, Map.LoadOrStore, Map.LoadAndDelete, Map.Swap, and Map.Range) treat this as the zero value of V. Use the returned booleans where available to distinguish stored zero values from absent keys.
Example ¶
var m sync.Map[string, int]
m.Store("one", 1)
v, ok := m.Load("one")
fmt.Println(v, ok)
Output: 1 true
Example (NilInterfaceValue) ¶
var m sync.Map[string, io.Reader]
var r io.Reader
m.Store("reader", r)
m.Range(func(_ string, value io.Reader) bool {
fmt.Println(value == nil)
return true
})
Output: true
func NewMap ¶
func NewMap[K comparable, V any]() *Map[K, V]
NewMap returns a pointer to a Map ready for use.
The zero value of Map is also ready for use; NewMap is purely optional.
func (*Map[K, V]) CompareAndDelete ¶
CompareAndDelete executes the compare-and-delete operation.
It follows sync.Map.CompareAndDelete semantics. If old's dynamic type is not comparable, CompareAndDelete panics.
func (*Map[K, V]) CompareAndSwap ¶
CompareAndSwap executes the compare-and-swap operation.
It follows sync.Map.CompareAndSwap semantics. If old's dynamic type is not comparable, CompareAndSwap panics.
func (*Map[K, V]) Load ¶
Load returns the value stored in the map for key.
It returns the zero value of V when the key is not present; ok reports whether the key was present.
func (*Map[K, V]) LoadAndDelete ¶
LoadAndDelete deletes the value for key, returning the previous value if any.
It returns the zero value of V when the key is not present; loaded reports whether the key was present.
func (*Map[K, V]) LoadOrStore ¶
LoadOrStore returns the existing value for key if present.
Otherwise, it stores and returns the given value.
The returned loaded result reports whether the value was already present.
If the stored value is nil (for example, when V is an interface type and a nil value was stored), it returns the zero value of V.
func (*Map[K, V]) Range ¶
Range calls f sequentially for each key and value present in the map.
It follows sync.Map.Range semantics. In particular, Range does not necessarily correspond to any consistent snapshot of the map's contents.
If f returns false, Range stops the iteration.
If a stored key is nil (for example, when K is an interface type and a nil interface key was stored), Range passes the zero value of K to f.
If a stored value is nil (for example, when V is an interface type and a nil value was stored), Range passes the zero value of V to f.
type Mutex ¶ added in v1.9.0
Mutex is an alias of sync.Mutex.
It is provided for convenience so users of this package can refer to a mutex without importing the standard library sync package directly.
type Once ¶ added in v1.19.0
Once is an alias of sync.Once.
It is provided for convenience so users of this package can refer to a once value without importing the standard library sync package directly.
type Pointer ¶ added in v1.18.0
Pointer is an alias for atomic.Pointer.
It is provided for convenience so users of this package can refer to a typed atomic pointer without importing sync/atomic directly.
type Pool ¶
type Pool[T any] struct { // contains filtered or unexported fields }
Pool is a typed wrapper around sync.Pool.
It stores and returns pointers to T (*T) to avoid copying large values. Pool does not reset values automatically on Put.
Zero value ¶
The zero value is not ready for use. Construct a Pool with NewPool.
Semantics ¶
Pool has the same semantics as sync.Pool:
- Items may be dropped at any time by the runtime.
- Items are meant to be reused to reduce allocations, not to manage resource lifetimes.
- Values taken from the pool should be considered ephemeral and should not be assumed to be unique or to remain in the pool.
Callers are responsible for resetting any state on values before reusing them, if needed.
Example ¶
type item struct {
id int
}
pool := sync.NewPool[item]()
v := pool.Get()
v.id = 10
pool.Put(v)
v2 := pool.Get()
fmt.Println(v2 != nil)
pool.Put(v2)
Output: true
func NewPool ¶
NewPool returns a pointer to an initialized Pool for values of type T.
The pool creates new values on demand by allocating `new(T)` when empty.
Note: the returned Pool stores *T values. Callers should treat values obtained from Pool.Get as temporarily borrowed and return them to the pool with Pool.Put when finished.
type RWMutex ¶ added in v1.9.0
RWMutex is an alias of sync.RWMutex.
It is provided for convenience so users of this package can refer to a read/write mutex without importing the standard library sync package directly.
type SingleFlightGroup ¶ added in v1.9.0
type SingleFlightGroup[T any] struct { // contains filtered or unexported fields }
SingleFlightGroup suppresses duplicate executions of functions associated with the same key.
It is a thin, generic wrapper around singleflight.Group that provides type-safe results (via the type parameter T) while preserving singleflight semantics.
For a given key, the first caller executes the provided function and concurrent callers for the same key wait for that execution to complete and receive the same result.
The zero value of SingleFlightGroup is ready for use.
The type parameter T describes the value returned from SingleFlightGroup.Do. If the function returns a non-nil error, Do returns the zero value of T along with that error.
Implementation detail: the underlying singleflight implementation stores and returns values as `any`, so this wrapper performs a type assertion back to T. As long as the function passed to Do returns a value of type T, the assertion will succeed.
When T is an interface type and fn returns a nil interface value, Do exposes that result as the zero value of T.
Example ¶
var g sync.SingleFlightGroup[int]
v, err, shared := g.Do("key", func() (int, error) {
return 42, nil
})
fmt.Println(v, err == nil, shared)
Output: 42 true false
func NewSingleFlightGroup ¶ added in v1.9.0
func NewSingleFlightGroup[T any]() *SingleFlightGroup[T]
NewSingleFlightGroup creates a pointer to a new SingleFlightGroup instance.
A SingleFlightGroup is a generic wrapper around singleflight.Group that provides type-safe results (via the type parameter T) while preserving singleflight semantics.
The zero value of SingleFlightGroup is already ready for use, so calling NewSingleFlightGroup is optional.
func (*SingleFlightGroup[T]) Do ¶ added in v1.9.0
func (g *SingleFlightGroup[T]) Do(key string, fn func() (T, error)) (T, error, bool)
Do executes fn for the given key, making sure that only one execution is in flight at a time for that key.
If another execution for the same key is already running, Do waits for it and returns the same results.
It returns (value, err, shared):
- value is the successful result of fn (type T), or the zero value of T if err != nil.
- err is the error returned by fn.
- shared reports whether the result was given to multiple callers.
If fn returns a nil interface value and T is an interface type, value is the zero value of T.
func (*SingleFlightGroup[T]) Forget ¶ added in v1.9.0
func (g *SingleFlightGroup[T]) Forget(key string)
Forget forgets an in-flight call for key.
Future calls to SingleFlightGroup.Do with the same key will invoke their function rather than waiting for the earlier call to complete.
type Uint32 ¶ added in v1.19.0
Uint32 is an alias for atomic.Uint32.
It is provided for convenience so users of this package can refer to a typed atomic integer without importing sync/atomic directly.
type Uint64 ¶ added in v1.19.0
Uint64 is an alias for atomic.Uint64.
It is provided for convenience so users of this package can refer to a typed atomic integer without importing sync/atomic directly.
type Uintptr ¶ added in v1.19.0
Uintptr is an alias for atomic.Uintptr.
It is provided for convenience so users of this package can refer to a typed atomic integer without importing sync/atomic directly.
type Value ¶
type Value[T any] struct { // contains filtered or unexported fields }
Value is a typed wrapper around atomic.Value.
It provides a generic API while preserving the semantics and constraints of atomic.Value.
Zero value ¶
The zero value is ready for use.
Unset values ¶
If no value has been stored yet, Value.Load and Value.Swap return the zero value of T.
Type safety and panics ¶
Internally, atomic.Value stores values as `any`. This wrapper type-asserts the stored value back to T on Load/Swap. The assertion will succeed as long as you only store values of type T in this Value.
Storing values of different concrete types in the same underlying atomic.Value has the same constraints as atomic.Value itself and may panic.
When T is an interface type, storing a nil interface value has the same behavior as atomic.Value.Store(nil) and panics.
Example ¶
var value sync.Value[int] fmt.Println(value.Load()) value.Store(1) fmt.Println(value.Swap(2))
Output: 0 1
func NewValue ¶ added in v0.25.0
NewValue returns a pointer to a new Value wrapper.
The returned pointer is ready for use.
func (*Value[T]) CompareAndSwap ¶
CompareAndSwap executes the atomic compare-and-swap operation.
It follows atomic.Value.CompareAndSwap semantics. If old's dynamic type is not comparable, CompareAndSwap panics. As with Value.Store, interface-typed values must also satisfy atomic.Value's concrete-type rules.
func (*Value[T]) Load ¶
func (v *Value[T]) Load() T
Load returns the stored value.
If no value has been stored yet, it returns the zero value of T.
func (*Value[T]) Store ¶
func (v *Value[T]) Store(value T)
Store atomically stores value.
It follows atomic.Value.Store semantics. In particular, storing a nil interface value panics, and later stores must remain compatible with the concrete type established by the first store.
type WaitGroup ¶ added in v1.13.0
WaitGroup is an alias for sync.WaitGroup.
It is provided for convenience so users of this package can refer to a WaitGroup without importing `sync` directly.
type Worker ¶
type Worker struct {
// contains filtered or unexported fields
}
Worker schedules handlers with a bounded level of concurrency.
Work is scheduled via Worker.Schedule and completion is observed via Worker.Wait. Scheduled handlers run asynchronously in their own goroutines.
The zero value is not ready for use.
Example ¶
worker := sync.NewWorker(2)
var count sync.Int32
for range 3 {
_ = worker.Schedule(context.Background(), time.Second, sync.Hook{
OnRun: func(context.Context) error {
count.Add(1)
return nil
},
})
}
worker.Wait()
fmt.Println(count.Load())
Output: 3
func NewWorker ¶
NewWorker returns a pointer to a Worker that bounds concurrent execution to count.
The worker uses a buffered channel of size count as a semaphore. A call to Worker.Schedule acquires one slot before starting work and releases it when the work completes.
If count is 0, scheduling will always block until the provided context times out or is canceled (because the semaphore has no capacity).
The zero value of Worker is not ready for use; construct one with NewWorker.
func (*Worker) Schedule ¶
Schedule attempts to schedule hook.OnRun to run asynchronously, subject to the worker's concurrency limit.
Schedule blocks until one of the following occurs:
- A concurrency slot is acquired before the deadline: Schedule starts OnRun in a goroutine and returns nil.
- The derived timeout context is done first: Schedule returns context.Cause from that derived context.
The context passed to OnRun is a derived context created by context.WithTimeoutCause using the provided timeout. The timeout budget starts when Schedule is called, so time spent waiting for a concurrency slot and time spent running OnRun share the same deadline. This context is also passed to hook.OnError (via hook.Error) if OnRun returns a non-nil error. If OnRun ignores that context and continues running after the deadline, the goroutine may outlive the caller of Schedule until the handler eventually returns.
Error handling semantics:
- If hook.OnRun is nil, Schedule returns ErrNoOnRunProvided.
- If the input context is already done on entry, Schedule returns its cancellation cause without scheduling OnRun.
- If timeout <= 0, Schedule returns ErrTimeout without scheduling OnRun.
- Errors returned from OnRun are routed to hook.OnError (if set) and are not returned from Schedule. Schedule only reports errors related to scheduling (timeout/cancellation before a slot is acquired).
- Once a handler has been scheduled successfully, Schedule returns nil even if the derived context later expires while the handler is still running.
To wait for all scheduled handlers to complete, call Worker.Wait.
func (*Worker) Wait ¶
func (w *Worker) Wait()
Wait blocks until all handlers that have been successfully scheduled have completed.
It does not cancel running handlers. Cancellation is controlled by the contexts provided to Worker.Schedule and observed by the handlers themselves. Wait can be called multiple times; each call waits for the currently scheduled work to finish.