Skip to content

Lightweight Go package for clean, channel-based signal handling. Provides interrupt, terminate, hangup, user-defined signals, and stdin-based triggers. Supports composite shutdown channels for graceful server stops, CLI tools, daemons, and Kubernetes workloads with zero dependencies.

License

Notifications You must be signed in to change notification settings

nyxstack/signals

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Signals

A simple Go package for handling system signals and user input in a clean, channel-based way.

Overview

The signals package provides easy-to-use functions that return channels which close when specific signals are received. This allows for clean signal handling in Go applications using select statements.

Installation

go get github.com/nyxstack/signals

Functions

Signal Handlers

Interrupt() <-chan struct{}

Returns a channel that closes when SIGINT (Ctrl+C) is received.

Terminate() <-chan struct{}

Returns a channel that closes when SIGTERM is received (commonly used by Kubernetes and process managers).

Hangup() <-chan struct{}

Returns a channel that closes when SIGHUP is received (terminal hangup, reload, or session end).

Sigusr1() <-chan struct{} (Unix only)

Returns a channel that closes when SIGUSR1 is received (user-defined signal 1).

Sigusr2() <-chan struct{} (Unix only)

Returns a channel that closes when SIGUSR2 is received (user-defined signal 2).

User Input

Quit() <-chan struct{}

Returns a channel that closes when the user types 'q' followed by Enter on stdin.

Enter() <-chan struct{}

Returns a channel that closes when the user presses Enter.

Any() <-chan struct{}

Returns a channel that closes on any key press.

Composite Signals

AnyShutdown() <-chan struct{}

Returns a channel that closes on SIGINT, SIGTERM, or user 'q'.

GracefulShutdown() <-chan struct{}

Returns a channel that closes on SIGTERM or SIGHUP.

Usage Examples

Basic Signal Handling

package main

import (
    "fmt"
    "time"
    
    "github.com/nyxstack/signals"
)

func main() {
    fmt.Println("Application started. Press Ctrl+C to exit.")
    
    // Wait for interrupt signal
    <-signals.Interrupt()
    
    fmt.Println("Received interrupt signal, shutting down...")
}

Multiple Signal Handling

package main

import (
    "fmt"
    "time"
    
    "github.com/nyxstack/signals"
)

func main() {
    fmt.Println("Server starting...")
    
    // Simulate some work
    go func() {
        for {
            fmt.Println("Working...")
            time.Sleep(2 * time.Second)
        }
    }()
    
    // Wait for any shutdown signal
    select {
    case <-signals.Interrupt():
        fmt.Println("Received SIGINT (Ctrl+C)")
    case <-signals.Terminate():
        fmt.Println("Received SIGTERM")
    case <-signals.Hangup():
        fmt.Println("Received SIGHUP")
    case <-signals.Quit():
        fmt.Println("User typed 'q'")
    }
    
    fmt.Println("Gracefully shutting down...")
}

With Context for Graceful Shutdown

package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/nyxstack/signals"
)

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    
    // Start background work
    go worker(ctx)
    
    // Wait for shutdown signal
    select {
    case <-signals.Interrupt():
    case <-signals.Terminate():
    }
    
    fmt.Println("Shutdown signal received, stopping...")
    cancel()
    
    // Give time for graceful shutdown
    time.Sleep(1 * time.Second)
    fmt.Println("Application stopped")
}

func worker(ctx context.Context) {
    ticker := time.NewTicker(1 * time.Second)
    defer ticker.Stop()
    
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Worker stopping...")
            return
        case <-ticker.C:
            fmt.Println("Working...")
        }
    }
}

Use Cases

  • Web Servers: Graceful shutdown on SIGTERM/SIGINT
  • CLI Tools: Clean exit on Ctrl+C or user input
  • Kubernetes Applications: Proper handling of pod termination signals
  • Daemon Processes: Configuration reload on SIGHUP
  • Interactive Applications: User-controlled exit with 'q' key

Goroutine Lifecycle

Each signal function spawns a goroutine to monitor for signals or input. Understanding the lifecycle is important:

Signal Functions (Interrupt, Terminate, Hangup, etc.)

  • Goroutine runs until the signal is received
  • After signal receipt, the goroutine cleans up automatically using defer signal.Stop()
  • These are lightweight and designed for one-time use per call

Input Functions (Quit, Enter, Any)

  • Goroutines block on stdin reads until input is received or stdin closes
  • Designed for typical CLI usage where the program exits after receiving input
  • For long-running applications with multiple input reads, call the function each time rather than reusing the channel

Composite Functions (AnyShutdown, GracefulShutdown)

  • Spawn multiple signal monitoring goroutines internally
  • When any monitored signal fires, the returned channel closes
  • Unused signal goroutines clean up when their channels are garbage collected

Best Practices

  1. One-time use: These functions are designed for blocking until a signal/input is received, then the program typically exits
  2. Don't abandon channels: If you stop listening to a returned channel, call the function again when needed rather than keeping old channels around
  3. Long-running apps: For applications that need repeated signal handling, each new operation should call the function again
  4. Testing: In tests, close stdin or send signals to ensure goroutines terminate properly

Requirements

  • Go 1.24.2 or later

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests. When contributing:

  1. Follow Go conventions and best practices
  2. Include tests for new signal handlers
  3. Update documentation for any new functions
  4. Ensure backward compatibility

License

MIT License - see LICENSE file for details.

About

Lightweight Go package for clean, channel-based signal handling. Provides interrupt, terminate, hangup, user-defined signals, and stdin-based triggers. Supports composite shutdown channels for graceful server stops, CLI tools, daemons, and Kubernetes workloads with zero dependencies.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages