DEV Community

nasa9084
nasa9084

Posted on

syg: simply signal to callback mapping in Go

#go

When you want to catch system signals in Go, you can use os package, os/signal package, and syscall package.

Here's an example of catching a system signal then shutdown the HTTP server gracefully:

func main() {
    sigCh := make(chan os.Signal, 1)
    signal.Notify(sigCh, syscall.SIGINT)

    s := &http.Server{
        // configure server properties
    }

    go func() {
        <-sigCh
        s.Shutdown(context.Background())
    }()

    if err := s.ListenAndServe(); err != http.ErrServerClosed {
        // error handling
    }
}
Enter fullscreen mode Exit fullscreen mode

Yes, this is a very simple pattern.
If server downed with some error before signal is sent, the goroutine which listening the signal will leak.
So, we use context and for-select loop.

func main() {
    sigCh := make(chan os.Signal, 1)
    signal.Notify(sigCh, syscall.SIGINT)

    s := &http.Server{
        // configure server properties
    }
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    go func() {
        for {
            select {
                case <-sigCh:
                    s.Shutdown(context.Background())
                case <-ctx.Done():
                    return
            }
        }
    }()

    if err := s.ListenAndServe(); err != http.ErrServerClosed {
        // error handling
    }
}
Enter fullscreen mode Exit fullscreen mode

Hmmm.... This is also a very simple pattern, there's no error handling code, no server configure, or no flag parsing.
However, the code is so complex. I cannot remember this signal catch idiom.

So I created github.com/nasa9084/syg package for listen signals and call a callback function.

github.com/nasa9084/syg

Rethink the example code.
I rewrite the example code with syg package:

func main() {
    s := &http.Server{
        // configure server properties
    }

    cancel := syg.Listen(func(os.Signal) {
        s.Shutdown(context.Background())
    }, syscall.SIGINT)
    defer cancel()

    if err := s.ListenAndServe(); err != http.ErrServerClosed {
        // error handling
    }
}
Enter fullscreen mode Exit fullscreen mode

Wow, very shortened.

The syg package has only two function, Listen() and ListenContext().
As you thought, these are essentially same function.
ListenContext() takes context.Context, but Listen() is not.
Listen() calls context.Background() inside the function, and calls ListenContext() with that context.

These functions calls signal.Notify() and generates goroutine which waits the signal, and returns the cancel function.

syg package is very small package, but it is very useful.
Please use this package and make issues or PR if you find problems or enhancements!

Discussion (0)