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.
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 ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var Version string
Functions ¶
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 NewSignalContext ¶
NewSignalContext creates a context that cancels on SIGTERM but captures SIGINT. 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
Types ¶
This section is empty.