shutdown

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2026 License: MIT Imports: 4 Imported by: 2

README

Shutdown

shutdown is a small Go library for explicit, instance-based shutdown management.

The core package provides three strategies:

  • Lifo for last-in, first-out shutdown
  • Fifo for first-in, first-out shutdown
  • Group for concurrent shutdown

Requirements

  • Go 1.25+

Design

The root package is instance-first: create a shutdown manager, append closers, then close it explicitly.

manager := shutdown.NewLIFO()
manager.Append(db)
manager.Append(server)

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if err := manager.CloseContext(ctx); err != nil {
	log.Printf("shutdown failed: %v", err)
}

For code that prefers package-level wiring, shutdown/compat provides a shared manager behind package-level functions. New code should still prefer explicit manager instances from the root package when possible.

Context semantics

CloseContext behaves as follows:

  • if a closer implements CloseContext(context.Context) error, that method is used;
  • otherwise the library falls back to Close() error;
  • Lifo and Fifo stop scheduling new closers after context cancellation;
  • Group closes resources concurrently and waits for all started closers to finish;
  • errors are aggregated with errors.Join.

CloseContext requires a non-nil context. Passing nil is treated as a caller bug and may panic.

Core API

type ContextCloser interface {
	CloseContext(context.Context) error
}

Constructors:

  • shutdown.NewLIFO()
  • shutdown.NewFIFO()
  • shutdown.NewGroup()

Helpers:

  • shutdown.Fn
  • shutdown.ContextFn
  • shutdown.QuietFn

Package-level API

For package-level convenience, use shutdown/compat:

compat.Set(shutdown.NewLIFO())
compat.Append(server)
compat.Append(db)

waitCtx := context.Background()

shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if err := compat.CloseOnSignal(waitCtx, shutdownCtx, os.Interrupt, syscall.SIGTERM); err != nil {
	log.Printf("compat shutdown failed: %v", err)
}

shutdown/compat provides a package-level API backed by a shared manager. compat.CloseOnSignal accepts separate contexts for waiting and shutdown so cancellation of the wait phase does not corrupt shutdown execution. compat.WaitForSignal requires at least one explicit signal. All compat functions that accept context.Context also require a non-nil context.

Notes

  • repeated Close calls are idempotent and return the result of the first shutdown;
  • appending after shutdown starts is invalid and will panic;
  • errors.Is works with joined shutdown errors.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Closer

type Closer = io.Closer

Closer is the minimal contract accepted by shutdown managers.

It is declared as an alias of io.Closer so existing resources that already implement Close() error can be registered without adaptation.

A manager treats each registered Closer as a single shutdown step. Depending on the concrete manager implementation, those steps may be executed sequentially or concurrently when shutdown starts.

type ContextCloser added in v0.4.0

type ContextCloser interface {
	CloseContext(ctx context.Context) error
}

ContextCloser is implemented by resources that support context-aware shutdown.

When a registered resource implements ContextCloser, managers prefer CloseContext over Close during CloseContext execution. This allows the resource to observe cancellation, deadlines, and other context-derived shutdown policies.

If a resource does not implement ContextCloser, managers fall back to the regular Close method defined by Closer.

type ContextFn added in v0.4.0

type ContextFn func(ctx context.Context) error

ContextFn adapts a context-aware function to both Closer and ContextCloser.

When a manager performs context-aware shutdown, it calls CloseContext and the wrapped function receives the caller-provided context. When a caller uses the plain Close method, ContextFn falls back to a background context.

This adapter is useful for resources whose shutdown path needs deadline or cancellation information but does not warrant a custom type.

func (ContextFn) Close added in v0.4.0

func (f ContextFn) Close() error

Close invokes the wrapped function with context.Background().

This preserves compatibility with the Closer interface when no explicit context is available.

func (ContextFn) CloseContext added in v0.4.0

func (f ContextFn) CloseContext(ctx context.Context) error

CloseContext invokes the wrapped function with the provided context.

type Fifo

type Fifo struct {
	// contains filtered or unexported fields
}

Fifo closes registered resources in first-in, first-out order.

The first resource appended to the manager is the first resource closed during shutdown. This strategy is useful when resources were acquired in the same order they should be released, or when shutdown ordering should mirror the application startup sequence rather than unwind it.

Fifo is safe for concurrent Append and Close calls. Shutdown itself is idempotent: the first call to Close or CloseContext performs the work, and subsequent calls return the previously computed result.

func NewFIFO added in v0.4.0

func NewFIFO() *Fifo

NewFIFO constructs an empty FIFO shutdown manager.

The returned value is ready for immediate use and does not require further initialization.

func (*Fifo) Append

func (f *Fifo) Append(closer Closer)

Append registers a closer to be executed during shutdown.

Nil closers are ignored. If shutdown has already started, Append panics, because adding resources after the shutdown sequence has been fixed is treated as a programming error in the current API.

func (*Fifo) Close

func (f *Fifo) Close() error

Close starts FIFO shutdown with context.Background().

It is equivalent to calling CloseContext(context.Background()).

func (*Fifo) CloseContext

func (f *Fifo) CloseContext(ctx context.Context) error

CloseContext starts FIFO shutdown using the supplied context.

Each registered closer is executed in append order. Before running a new closer, the manager checks ctx.Err(); if the context has already been canceled, shutdown stops scheduling additional closers and the context error is joined into the returned error.

If a closer implements ContextCloser, CloseContext forwards ctx to that method. Otherwise the manager falls back to Close.

The first call performs shutdown and caches the result. Later calls return the cached result without re-running any closers.

ctx must be non-nil. Passing nil is considered a caller bug and may panic.

type Fn

type Fn func() error

Fn adapts a plain function to the Closer interface.

Fn is useful when shutdown logic does not naturally live on a struct that implements io.Closer. It allows callers to register inline or delegated cleanup logic without creating a dedicated type.

Example:

manager.Append(shutdown.Fn(func() error {
	return server.Close()
}))

func (Fn) Close

func (f Fn) Close() error

Close calls the wrapped function and returns its result.

type Group

type Group struct {
	// contains filtered or unexported fields
}

Group closes registered resources concurrently.

Unlike Fifo and Lifo, Group does not impose an ordering relationship between registered closers. All closers are started in parallel during shutdown, and the manager waits for every started closer to finish before returning.

Group is useful when shutdown steps are independent and serial execution would only increase total shutdown latency.

func NewGroup added in v0.4.0

func NewGroup() *Group

NewGroup constructs an empty concurrent shutdown manager.

func (*Group) Append

func (g *Group) Append(closer Closer)

Append registers a closer to be executed during shutdown.

Nil closers are ignored. Appending after shutdown has started panics.

func (*Group) Close

func (g *Group) Close() error

Close starts concurrent shutdown with context.Background().

func (*Group) CloseContext

func (g *Group) CloseContext(ctx context.Context) error

CloseContext starts concurrent shutdown using the supplied context.

All registered closers are launched concurrently. If a closer implements ContextCloser, it receives the supplied context; otherwise Close is used.

Group differs intentionally from the sequential managers: it waits for all started closers to finish even if ctx becomes done while shutdown is running. After all goroutines complete, any observed closer errors are joined together, and the context error is appended if present.

Shutdown is performed only once. Later calls return the cached result.

ctx must be non-nil. Passing nil is considered a caller bug and may panic.

type Lifo

type Lifo struct {
	// contains filtered or unexported fields
}

Lifo closes registered resources in last-in, first-out order.

The most recently appended resource is closed first. This is the classic stack-like shutdown strategy and is often a good fit when resources should be unwound in reverse acquisition order.

Lifo is safe for concurrent use and performs shutdown at most once.

func NewLIFO added in v0.4.0

func NewLIFO() *Lifo

NewLIFO constructs an empty LIFO shutdown manager.

func (*Lifo) Append

func (l *Lifo) Append(closer Closer)

Append registers a closer to be executed during shutdown.

Nil closers are ignored. Appending after shutdown has started panics.

func (*Lifo) Close

func (l *Lifo) Close() error

Close starts LIFO shutdown with context.Background().

func (*Lifo) CloseContext

func (l *Lifo) CloseContext(ctx context.Context) error

CloseContext starts LIFO shutdown using the supplied context.

Registered closers are executed in reverse append order. The manager checks ctx.Err() before scheduling the next closer; if the context has been canceled, remaining closers are skipped and the context error is joined into the final result.

Context-aware closers receive the supplied context through CloseContext. Plain closers are executed through Close.

Shutdown is idempotent: only the first call executes closers.

ctx must be non-nil. Passing nil is considered a caller bug and may panic.

type Manager added in v0.4.0

type Manager interface {
	Append(closer Closer)
	Close() error
	CloseContext(ctx context.Context) error
}

Manager is the common contract implemented by all shutdown strategies.

A Manager owns a collection of registered closers and is responsible for invoking them exactly once when shutdown begins. Implementations differ in the order and concurrency model used during shutdown, but they share the same high-level lifecycle:

  1. callers register resources with Append;
  2. callers trigger shutdown with Close or CloseContext;
  3. subsequent Close calls are idempotent and return the result of the first shutdown attempt.

Append is intentionally simple and does not return an error. In the current implementation, attempting to append after shutdown has begun is considered a programming error and panics.

CloseContext requires a non-nil context. Passing nil is considered a caller bug and may panic inside a concrete manager implementation.

type QuietCloser

type QuietCloser interface {
	Close()
}

QuietCloser is a convenience contract for resources whose shutdown operation cannot fail.

The compat layer can wrap a QuietCloser into a regular Closer by adapting its Close method to return a nil error.

type QuietFn

type QuietFn func()

QuietFn adapts a no-error function to the Closer interface.

It is the functional counterpart to QuietCloser and is useful for fire-and-forget cleanup logic that cannot fail.

func (QuietFn) Close

func (f QuietFn) Close() error

Close invokes the wrapped function and always returns nil.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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