Graceful shutdown handling for Go applications. This package provides utilities for handling OS signals (SIGINT, SIGTERM) to enable clean shutdowns and hot reloading capabilities.
go get next.orly.dev/pkg/utils/interrupt
package main
import (
"context"
"log"
"time"
"next.orly.dev/pkg/utils/interrupt"
)
func main() {
// Create interrupt handler
handler := interrupt.New()
// Start your application
go func() {
for {
select {
case <-handler.Shutdown():
log.Println("Shutting down worker...")
return
default:
// Do work
time.Sleep(time.Second)
}
}
}()
// Wait for shutdown signal
<-handler.Done()
log.Println("Application stopped")
}
func worker(ctx context.Context) {
handler := interrupt.New()
// Create context that cancels on shutdown
workCtx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
<-handler.Shutdown()
cancel()
}()
// Use workCtx for all operations
for {
select {
case <-workCtx.Done():
return
default:
// Do work with context
}
}
}
handler := interrupt.New()
// Add cleanup callbacks
handler.OnShutdown(func() {
log.Println("Closing database connections...")
db.Close()
})
handler.OnShutdown(func() {
log.Println("Saving application state...")
saveState()
})
// Callbacks execute in reverse order when shutdown occurs
<-handler.Done()
handler := interrupt.New()
// Handle reload signals
go func() {
for {
select {
case <-handler.Reload():
log.Println("Reloading configuration...")
reloadConfig()
case <-handler.Shutdown():
return
}
}
}()
<-handler.Done()
The main interrupt handler type.
Methods:
New() *Handler - Create a new interrupt handlerShutdown() <-chan struct{} - Channel closed on shutdown signalsReload() <-chan struct{} - Channel closed on reload signals (SIGHUP)Done() <-chan struct{} - Channel closed when all cleanup is completeOnShutdown(func()) - Add a callback to run during shutdownWait() - Block until shutdown signal receivedIsShuttingDown() bool - Check if shutdown is in progressThe package handles these signals:
The interrupt package includes comprehensive tests:
# Run interrupt package tests
go test ./pkg/utils/interrupt
# Run with verbose output
go test -v ./pkg/utils/interrupt
# Run with race detection
go test -race ./pkg/utils/interrupt
Part of the full test suite:
# Run all tests including interrupt
./scripts/test.sh
# Run specific package tests
go test ./pkg/utils/...
Tests cover:
# Test signal handling
go test -v ./pkg/utils/interrupt -run TestSignalHandling
# Test callback execution
go test -v ./pkg/utils/interrupt -run TestShutdownCallbacks
package main
import (
"context"
"log"
"net/http"
"time"
"next.orly.dev/pkg/utils/interrupt"
)
func main() {
handler := interrupt.New()
server := &http.Server{
Addr: ":8080",
Handler: http.DefaultServeMux,
}
// Shutdown server gracefully
handler.OnShutdown(func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
server.Shutdown(ctx)
})
go server.ListenAndServe()
<-handler.Done()
log.Println("Server stopped")
}
func main() {
handler := interrupt.New()
// Start worker pool
pool := NewWorkerPool(10)
pool.Start()
// Clean shutdown
handler.OnShutdown(func() {
log.Println("Stopping worker pool...")
pool.Stop()
})
<-handler.Done()
}
go build ./pkg/utils/interrupt
This package integrates well with:
Part of the next.orly.dev project. See main LICENSE file.