I’m implementing a single-threaded event-driven server in Go using an OS-specific multiplexer abstraction (epoll on Linux, kqueue on macOS).
My main loop looks roughly like this:
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)
for {
select {
case <-sigs:
return //I have defer function for gracefully shutdown
default:
readyEvents, err := mp.Wait() // epoll_wait / kevent
if err != nil {
continue
}
for _, ev := range readyEvents {
// accept/read/write logic
}
}
}
Using select with sigs doesn’t help because the blocking happens in the default case. The iteration can’t complete to check the context again.
So my questions are:
What is the idiomatic way to unblock
epoll_wait/keventso a graceful shutdown can occur?How do production servers usually handle graceful shutdown in an event-loop model like this?