Documentation
¶
Overview ¶
Package lifecycle provides a centralized library for managing application lifecycles and interactive I/O.
Dual Signal Context ¶
Standard Go `signal.NotifyContext` cancels on the first signal. `lifecycle` distinguishes between:
- SIGINT (Ctrl+C): "Soft" interrupt. Captures the signal but keeps the Context active. Allows the application to decide whether to pause, confirm exit, or ignore.
- SIGTERM: "Hard" stop. Cancels the Context immediately, triggering graceful shutdown.
Interruptible I/O ¶
On many systems (especially Windows), reading from `os.Stdin` blocks the goroutine indefinitely, preventing clean cancellation. Furthermore, on Windows, receiving a signal can close the standard input handle, causing an unexpected EOF. `lifecycle` provides `OpenTerminal` (using `CONIN$`) and `NewInterruptibleReader` to ensure I/O operations respect `context.Context` cancellation and signals are handled gracefully without premature termination.
Shutdown Timeouts ¶
Graceful shutdown often involves waiting for background goroutines to finish (e.g., closing database connections, flushing logs). To prevent the application from hanging indefinitely if a cleanup operation stalls, `lifecycle` provides `BlockWithTimeout`. This ensures the process exits deterministically even if some components are stuck.
Usage ¶
ctx := lifecycle.NewSignalContext(context.Background()) defer ctx.Cancel() // Safe terminal reading term, _ := lifecycle.OpenTerminal() reader := lifecycle.NewInterruptibleReader(term, ctx.Done())
See examples/demo for a full interactive application.
Index ¶
- Variables
- func BlockWithTimeout(done <-chan struct{}, timeout time.Duration) error
- func IsInterrupted(err error) bool
- func NewInterruptibleReader(base io.Reader, cancel <-chan struct{}) *termio.InterruptibleReader
- func NewLogMetricsProvider() metrics.Provider
- func NewSignalContext(parent context.Context, opts ...signal.Option) *signal.Context
- func OpenTerminal() (io.ReadCloser, error)
- func SetLogger(l *slog.Logger)
- func SetMetricsProvider(p metrics.Provider)
- func SetStrictMode(strict bool)
- func StartProcess(cmd *exec.Cmd) error
- func UpgradeTerminal(r io.Reader) (io.Reader, error)
- func WithForceExit(threshold int) signal.Option
- func WithInterrupt(cancel bool) signal.Option
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var Version string
Functions ¶
func BlockWithTimeout ¶ added in v1.1.0
BlockWithTimeout blocks until the done channel is closed or the timeout expires. Alias for pkg/runtime.BlockWithTimeout.
Example ¶
ExampleBlockWithTimeout demonstrates how to enforce a deadline on shutdown cleanup.
package main
import (
"fmt"
"time"
"github.com/aretw0/lifecycle"
)
func main() {
done := make(chan struct{})
// Simulate a cleanup task
go func() {
defer close(done)
// Simulate fast cleanup
time.Sleep(10 * time.Millisecond)
}()
// Wait for cleanup, but give up after 1 second
err := lifecycle.BlockWithTimeout(done, 1*time.Second)
if err != nil {
fmt.Println("Cleanup timed out!")
} else {
fmt.Println("Cleanup finished successfully")
}
}
Output: Cleanup finished successfully
func IsInterrupted ¶
IsInterrupted checks if an error indicates an interruption (Context Canceled, EOF, etc.). Alias for pkg/termio.IsInterrupted.
func NewInterruptibleReader ¶
func NewInterruptibleReader(base io.Reader, cancel <-chan struct{}) *termio.InterruptibleReader
NewInterruptibleReader returns a reader that checks the cancel channel before/after blocking reads. Alias for pkg/termio.NewInterruptibleReader.
func NewLogMetricsProvider ¶ added in v1.1.0
NewLogMetricsProvider returns a metrics provider that logs to the current logger. Useful for development and local verification. Alias for pkg/metrics.LogProvider.
func NewSignalContext ¶
NewSignalContext creates a context that cancels on SIGTERM/SIGINT. On the first signal, context is cancelled. On the second, it force exits. Behavior can be customized via functional options. Alias for pkg/signal.NewContext.
Example ¶
ExampleNewSignalContext demonstrates how to use the Dual Signal context. Note: This example is illustrative; in a real run, it waits for SIGINT/SIGTERM.
package main
import (
"context"
"fmt"
"time"
"github.com/aretw0/lifecycle"
)
func main() {
// Create a context that listens for signals.
ctx := lifecycle.NewSignalContext(context.Background())
// For checking output deterministically in this example, we cancel manually
// after a short delay, allowing "work" to happen first.
go func() {
time.Sleep(50 * time.Millisecond)
ctx.Cancel()
}()
// Simulate work
select {
case <-ctx.Done():
fmt.Println("Context cancelled too early")
case <-time.After(10 * time.Millisecond):
fmt.Println("Doing work...")
}
}
Output: Doing work...
func OpenTerminal ¶
func OpenTerminal() (io.ReadCloser, error)
OpenTerminal checks for text input capability and returns a Reader. On Windows, it tries to open CONIN$. Alias for pkg/termio.Open.
Example ¶
ExampleOpenTerminal demonstrates how to open the terminal safely.
package main
import (
"fmt"
"github.com/aretw0/lifecycle"
)
func main() {
// OpenTerminal handles OS-specific logic (like CONIN$ on Windows)
reader, err := lifecycle.OpenTerminal()
if err != nil {
fmt.Printf("Error opening terminal: %v\n", err)
return
}
defer reader.Close()
fmt.Println("Terminal opened successfully")
// Wrap with InterruptibleReader to respect context cancellation
// r := lifecycle.NewInterruptibleReader(reader, ctx.Done())
}
Output: Terminal opened successfully
func SetLogger ¶ added in v1.1.0
SetLogger overrides the global logger used by the library. Alias for pkg/log.SetLogger.
func SetMetricsProvider ¶ added in v1.1.0
SetMetricsProvider overrides the global metrics provider. This allowing bridging library metrics to Prometheus, OTEL, etc. Alias for pkg/metrics.SetProvider.
func SetStrictMode ¶ added in v1.1.0
func SetStrictMode(strict bool)
SetStrictMode sets whether to block on unsupported platforms for process hygiene. Alias for pkg/proc.StrictMode.
func StartProcess ¶ added in v1.1.0
StartProcess starts the specified command with process hygiene (auto-kill on parent exit). Alias for pkg/proc.Start.
func UpgradeTerminal ¶ added in v0.1.1
UpgradeTerminal checks if the provided reader is a terminal and returns a safe reader (e.g. CONIN$ on Windows). If not a terminal, returns the original reader.
func WithForceExit ¶ added in v1.1.0
WithForceExit configures the threshold of signals required to trigger an immediate os.Exit(1). Set to 0 to disable forced exit. Alias for pkg/signal.WithForceExit.
func WithInterrupt ¶ added in v1.1.0
WithInterrupt configures whether SIGINT (Ctrl+C) should cancel the context. Alias for pkg/signal.WithInterrupt.
Types ¶
This section is empty.
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
demo
command
|
|
|
hooks
command
|
|
|
observability
command
|
|
|
zombie
command
|
|
|
pkg
|
|
|
log
Package log provides a lightweight, structured logging interface for the lifecycle library.
|
Package log provides a lightweight, structured logging interface for the lifecycle library. |
|
metrics
Package metrics provides a decoupled interface for collecting library metrics.
|
Package metrics provides a decoupled interface for collecting library metrics. |
|
proc
Package proc provides primitives for managing process lifecycle and hygiene.
|
Package proc provides primitives for managing process lifecycle and hygiene. |
|
runtime
Package runtime provides utilities for deterministic process management.
|
Package runtime provides utilities for deterministic process management. |
|
signal
Package signal provides a stateful signal context for interactive CLI applications.
|
Package signal provides a stateful signal context for interactive CLI applications. |
|
termio
Package termio provides interruptible I/O primitives and terminal handling.
|
Package termio provides interruptible I/O primitives and terminal handling. |