Reputation: 621
I'm quite new to Go and in a quite a rut trying to figure out how to handle events that need to return back to the main loop.
In C, I can just return the function back but it seems I can't do that here, at least with that method.
I can handle signals that need to be processed within sigHandler() i.e. SIGINT or SIGTERM but I need to return calls back to main when handling SIGHUP or SIGUSR. In my code below, the process just stuck in wait mode once I sent the hangup signal.
I appreciate if someone can help me to guide on how I can properly design the signal handling to handle calls that need to return to main code which is in the first goroutine.
I'm now handling channel messages in the main goroutine within select{} but the code below quits when I'm sending the HUP signal. The objective here is to reload the config and continue execution as normal.
In the main line code, I have this:
go sigHandler()
cs := make(chan bool, 1)
go sigHandler(cs)
// setup the http server
err := setupServer(addr, port)
if err != nil {
fatal("Error setting up listening sockets")
os.Exit(1)
}
select {
case quit := <-cs:
if quit {
logEvent(loginfo, sys, "Terminating..")
closeLog()
os.Exit(0)
} else {
logEvent(loginfo, sys, "Reloading configuration..")
}
}
The function sigHandler()
func sigHandler(cs chan bool) {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
signal := <-c
logEvent(lognotice, sys, "Signal received: "+signal.String())
switch signal {
case syscall.SIGINT, syscall.SIGTERM:
cs <- true
case syscall.SIGHUP:
cs <- false
}
}
Upvotes: 2
Views: 5610
Reputation: 121492
You could do something like this:
package main
import (
"os"
"os/signal"
"syscall"
)
// We make sigHandler receive a channel on which we will report the value of var quit
func sigHandler(q chan bool) {
var quit bool
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
// foreach signal received
for signal := range c {
// logEvent(lognotice, sys, "Signal received: "+signal.String())
switch signal {
case syscall.SIGINT, syscall.SIGTERM:
quit = true
case syscall.SIGHUP:
quit = false
}
if quit {
quit = false
// closeDb()
// logEvent(loginfo, sys, "Terminating..")
// closeLog()
os.Exit(0)
}
// report the value of quit via the channel
q <- quit
}
}
func main() {
// init two channels, one for the signals, one for the main loop
sig := make(chan bool)
loop := make(chan error)
// start the signal monitoring routine
go sigHandler(sig)
// while vat quit is false, we keep going
for quit := false; !quit; {
// we start the main loop code in a goroutine
go func() {
// Main loop code here
// we can report the error via the chan (here, nil)
loop <- nil
}()
// We block until either a signal is received or the main code finished
select {
// if signal, we affect quit and continue with the loop
case quit = <-sig:
// if no signal, we simply continue with the loop
case <-loop:
}
}
}
However, note that the signal will cause the main loop to continue, but it will not stop the execution on the first goroutine.
Upvotes: 2
Reputation: 6670
Here's one way of structuring things to do what you want, separating concerns so that the signal-handling code and the main code are separate and easily tested independently.
How you implement Quit and ReloadConfig is entirely up to your program - ReloadConfig may send a "please reload" value on a channel to a running goroutine; it may lock a mutex and change some shared configuration data; or some other possibility.
package main
import (
"log"
"os"
"os/signal"
"syscall"
)
func main() {
obj := &myObject{}
go handleSignals(obj)
select {}
}
type myObject struct {
}
func (obj *myObject) Quit() {
log.Printf("quitting")
os.Exit(0)
}
func (obj *myObject) ReloadConfig() {
log.Printf("reloading configuration")
}
type MainObject interface {
ReloadConfig()
Quit()
}
func handleSignals(main MainObject) {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
for sig := range c {
switch sig {
case syscall.SIGINT, syscall.SIGTERM:
main.Quit()
return
case syscall.SIGHUP:
main.ReloadConfig()
}
}
}
Upvotes: 2