I am launching a simple Java application through Go, with the goal of proving that Go can send a signal like SIGQUIT or SIGTERM, and the Java can catch that and handle it appropriately (i.e. shutdown gracefully). When I run the Java program on the command line and send it a CTRL+C, the Java program correctly catches the signal.
However, when I launch the Java program through Go and attempt to send the process a signal, the Java process is neither terminated nor does it handle the signal. The only one that works is SIGKILL, which of course is not caught, and simply kills the process.
Here are the relevant parts of my Go code:
Start:
startChan := make(chan bool)
go func(startChan chan<- bool) {
a.Cmd = exec.Command("java", "-jar", "C:\\Code\\sigterm\\TestApp.jar")
a.Cmd.Stdout = os.Stdout
a.Cmd.Stderr = os.Stderr
launchErr := a.Cmd.Start()
if launchErr != nil {
fmt.Println("Unable to start app:" + launchErr.Error())
}
startChan <- true
}(startChan)
Stop:
func (a *App) Stop(timeout time.Duration) {
a.Cmd.Process.Signal(syscall.SIGQUIT) //Does not work
a.Cmd.Process.Signal(syscall.SIGTERM) //Does not work
a.Cmd.Process.Signal(syscall.SIGKILL) //Works
}
This is on Windows, by the way. I tried it by launching another program (notepad) and got the same results; that is, only SIGKILL worked. I have confirmed that Go is getting the correct PID and so forth, so the signal should be going to the right place.
Any ideas about how I can get this to work?
This does not seem to be supported as stated in the documentation:
https://godoc.org/os#Process.Signal
Sending Interrupt on Windows is not implemented.
Also, see this issue discussion to get more context on why it could not be done:
https://github.com/golang/go/issues/6720
syscall package is tailored to the OS (https://golang.org/pkg/syscall/#pkg-overview).
Although interrupt is not implemented on Windows, signaling is usable.
There is an example in the signal package itself for Windows.
An interactive version (go run) is available here.
Related
I'm referring to this article: Graceful Shutdowns on Cloud Run
The example outlines how to do this in Node.js.
How would one do this in Golang? Any issues with simply adding this to the func init() method?
func shutdownGracefully() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
// Do some cleanup stuff here...
os.Exit(0)
}()
}
We have sample Go code for proper signal handling at https://github.com/GoogleCloudPlatform/golang-samples/pull/1902. It's not merged yet but essentially highlights how to do this properly on Go.
Beware that when running locally, sending Ctrl+C to your application is a SIGINT, however on Cloud Run you will be getting SIGTERM. It is also important to pass cancellations properly and handling server shutdown gracefully while not dropping the ongoing requests (though, net/http.Server handles a decent portion of this for you as you’ll see in sample code).
How would one do this in Golang?
An idiomatic way to handle graceful shutdowns in go is having a select statement blocking the main goroutine listening for any signal. From there you can trigger all the proper shutdowns when necessary.
For instance:
select {
case err := <-anyOtherError:
// ...
case signal := <- c:
// Handle shutdown e.g. like http Server Shutdown
}
I like to set it inside a run function with other startup tasks like starting the server, configs, etc.
I'm writing a server application in Go, and using a wrapper to run it as a Windows service.
There's a need to gracefully shut down the server (to close resources and connections properly), and in UNIX it would be handled through the SIGTERM signal. No big deal.
Though on Windows things seem very different. I see on this guide that signals actually exist on windows (?), and the SIGTERM is defined, though other pages indicate they don't, or to use other mechanisms like WM_CLOSE.
What is the preferable way to tell a headless process to gracefully terminate? How should it be implemented in Go?
The server is designed to be multiplatform, so the most standard way of doing it is preferable.
The go way to initiate canceling a task/service, is to use the context.Context package.
So if you want a signal handler to trigger the closing of a task's context.Context:
func registerSigHandler() context.Context {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
rootCtx := context.Background()
taskCtx, cancelFn := context.WithCancel(rootCtx)
go func() {
sig := <-sigCh
log.Println("received signal:", sig)
// let sub-task know to wrap up: cancel taskCtx
cancelFn()
}()
return taskCtx
}
and then pass the returned taskCtx on to your worker task for it to listen on.
select {
case <-taskCtx.Done():
// context was canceled
default: // poll - rather than block in this example
}
Playground source-code.
Output:
2019/03/10 19:18:51 Worker PID: 33186
2019/03/10 19:18:51 Will terminate on these signals: interrupt terminated
2019/03/10 19:18:51 2019-03-10 19:18:51.018962 -0400 EDT m=+0.001727305
2019/03/10 19:18:52 2019-03-10 19:18:52.022782 -0400 EDT m=+1.005517010
2019/03/10 19:18:53 2019-03-10 19:18:53.019925 -0400 EDT m=+2.002630457
$ kill -INT 33186
2019/03/10 19:18:53 received signal: interrupt
2019/03/10 19:18:53 task context terminated reason: context canceled
2019/03/10 19:18:53 wrapping up task...
2019/03/10 19:18:53 workerTask() exited cleanly
2019/03/10 19:18:53 main exited cleanly
EDIT:
I tested this on Windows 10 and the clean-up is triggered when a Ctrl-C is issued from the same console.
Not sure how to send signals externally on Windows - which may be the OP's original issue. Using say killtask /F /PID 33186 would indeed kill the process without any signal handler being triggered.
Signaling is implemented on Windows but Unix signals are unavailable.
There is an example in the signal package of golang for Windows to send a Ctrl-Break. It is refactored for interactive use here.
So, after a while, I just wanted to share how I solved it. It's ugly, but the only real way I found. Windows golang programs do listen to a CTRL+C, which is not a signal or anything like it, but it does trigger a graceful shutdown in the go program using the same signal UNIX has.
Here's the relevant code:
// Create shutdown channel
exitChan := make(chan int)
interruptChan := make(chan os.Signal, 1)
signal.Notify(interruptChan, os.Interrupt)
select {
case exitCode := <-exitChan:
os.Exit(exitCode)
case <-interruptChan:
// This works in Windows if ctrl-c is invoked. It sucks though
svr.Stop()
os.Exit(-1)
}
To trigger the CTRL+C given the process, I use a small program called windows-kill.exe
// Windows is "special" and we need to use a library for killing processes gracefully
// Unfortunately setting up the C++ library with go is a hassle, so we resort to a CLI tool
ex, _ := os.Executable()
cmdPath := filepath.Join(filepath.Dir(ex), "windows-kill.exe")
cmd := exec.CommandContext(context.Background(), cmdPath, "-SIGINT", strconv.Itoa(l.proc.Pid))
err := cmd.Start()
if err != nil {
panic(err)
}
Here's the repo of the library and tool: https://github.com/alirdn/windows-kill
The idea of using *nix constructs and functions to Windows is generally bad and prone to weird tricks that may or may not work.
The proper way to cleanup on shutdown a GUI application is the handling of WM_QUERYENDSESSION and WM_ENDSESSION.
Also, you have flexibility on generic shut-down mechanism, check here.
The proper way to get notified if you are a service is the service handler of your console application (SERVICE_STOPPED). For more, see Writing a ServiceMain.
If you are running some form of web-services, may be better to use the web-service itself to initiate a shutdown, as tracking PIDS for signals can get messy.
To stop a http web-service, simply add a route like this:
http.HandleFunc("/shutdown",
func(w http.ResponseWriter, r *http.Request) {
// stops server - may want to add admin credentials check here!
srv.Shutdown(context.Background())
})
Playground source-code example.
2019/03/10 16:58:15 Listening on: :8080
$ curl 'localhost:8080/shutdown'
2019/03/10 17:04:17 Listening on: :8080
2019/03/10 17:04:19 exited cleanly
I've got a server-like program in Windows 10 where the main program waits for the requests with http.Handle(dst, handlerStruct), and for every query does the corresponding operations and returns a result or just prints something through the screen.
It usually works fine, but sometimes, and just sometimes, when I leave the program running "waiting" for requests to come in for a long time (3 minutes up) and then I send a request, it just gets "stucked" until I press CTRL+C, and then it returns the values inmediately. So it looks like the program runs well, but the returning thread is having a break or something when I leave it a long time without requests.
This happens ever since I started developing the program, and not only when using functions with goroutines.
For me it looks like it has more to do with Windows 10 than with Golang itself, but it's just very annoying, and of course I don't want this to happen with my server...
Hope you can see what's going wrong here...
Thanks.
EDIT
This is the main program waiting for the requests:
func main() {
runtime.GOMAXPROCS(10000000)
http.Handle("/sameDayCombinations", CBR.SameDayCombinationsHandler{})
http.ListenAndServe(":8080", nil)
}
Now this is the code of ServeHTTP for the handler:
func (h SameDayCombinationsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t1 := time.Now()
q := common.DecodeRequest(r)
json := common.EncodeAnswer((&h).RetrieveSpecificSolutions(q.Year, q.Month, q.Day, q.DepID, q.ArrID, q.Adults, q.Children, q.Infants), true)
fmt.Fprintf(w, json)
t2 := time.Now()
fmt.Println(t2.Sub(t1))
}
EDIT2
Here I add the stack of a program execution after the appearance of this weird behavior. So, after doing CTRL+C I got this; looks pretty normal to me.
http://pastebin.com/DduvtNZr
Though, now, by empirical demonstration, I'm sure it has to do with the console Prints (probably large prints?). That's a good new, cause it'll only happen while debugging, but stills a weird behavior...
If you look at Nginx it calls "nginx reload" to reload itself. Is there any way to send a signal from the command line to a running process? Even if the main process starts child processes how can I send commands to the main to notify its children?
ex:
myapp start -debug // starts a server
myapp reload -gracefull // stops the app gracefully
Now i need to send os signals to notify my server to perform a graceful shutdown
kill -QUIT pid
kill -USR2 pid
I hope my question is clear enough
Thnx
Receive signals
Take a look at the os/signal package.
Package signal implements access to incoming signals.
There is even an example in the documentation :
// Set up channel on which to send signal notifications.
// We must use a buffered channel or risk missing the signal
// if we're not ready to receive when the signal is sent.
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
// Block until a signal is received.
s := <-c
fmt.Println("Got signal:", s)
Send signals
To see how to send signals take a look at signal_test.go, it uses syscall. For example :
// Send this process a SIGHUP
t.Logf("sighup...")
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
waitSig(t, c, syscall.SIGHUP)
I figured out that in go i we can pass the environment to syscall.Exec
err := syscall.Exec(argv0. os.Args. os.Environ())
simply copies the current env to the child process.
Take the following code snippet:
func main() {
ch := make(chan int)
quit := make(chan int)
go func() {
for {
ch <- querySomePeriodicThing()
}
}()
// ...
loop:
for {
select {
case <-ch: handlePeriodicThing()
case <-quit: break loop
}
}
}
The goroutine should run for the duration of execution. When the select statement receives something from the quit channel, it breaks out of the loop and the program ends, without any attempt to stop the goroutine.
My question: will this have any intermittent adverse effects that are not obvious from running it once or twice? I know that in other languages threads should be cleaned up (i.e., exited) before the program ends, but is go different? Assume that querySomePeriodicThing() does not open file descriptors or sockets or anything that would be bad to leave open.
As mentioned in the spec, your program will exit when the main function completes:
Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.
So the fact you have other goroutines still running is not a problem from a language point of view. It may still be a problem depending on what your program is doing.
If the goroutine has created some resources that should be cleaned up before program exit, then having execution stop mid-way through might be a problem: in this case, you should make your main function wait for them to complete first. There is no equivalent to pthread_join, so you will need to code this yourself (e.g. by using a channel or sync.WaitGroup).
Note that for some resources are automatically cleaned up by the operating system on process exit (e.g. open files, file locks, etc), so in some cases no special clean up will be necessary
Goroutines aren't threads, they are very lightweight and the runtime automatically cleans them up when they are no longer running, or if the program exits.