DEV Community

loading...

syg: simply signal to callback mapping in Go

#go
nasa9084 profile image nasa9084 ・2 min read

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
    }
}

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
    }
}

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
    }
}

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

pic
Editor guide