microsoft / vscode-spring-boot-dashboard

Spring Boot Dashboard for VS Code
https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-spring-boot-dashboard
Other
56 stars 31 forks source link

Seek for better way to shutdown the app gracefully. #19

Open Eskibear opened 6 years ago

Eskibear commented 6 years ago

Code here: https://github.com/Microsoft/vscode-spring-boot-dashboard/blob/master/src/Controller.ts#L57-L65

In MacOSX, after clicking "Stop", sending "SIGINT" to kill, the value of app.process.killed is true, but actually the process is not terminated.

Eskibear commented 6 years ago

By changing the signal to "SIGTERM", it works in MacOSX, and moreover, the app shutdown more 'nicely'.

2018-08-02 13:02:09.175  INFO 36752 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 80 ms
2018-08-02 13:02:18.902  INFO 36752 --- [       Thread-3] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@25b485ba: startup date [Thu Aug 02 13:01:55 CST 2018]; root of context hierarchy
2018-08-02 13:02:18.904  INFO 36752 --- [       Thread-3] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
boot-dashboard-demo is inactive now.
kdvolder commented 6 years ago

Interesting. I beleave that from OS point of view 'SIGKILL' is a 'stronger' signal than 'SIGTERM'.

Although SIGKILL is defined in the same signal header file as SIGTERM, it cannot be ignored by the process. In fact, the process isn't even made aware of the SIGKILL signal since the signal goes straight to the kernel

Quoted from: https://major.io/2010/03/18/sigterm-vs-sigkill/

So I assume this is a OS X specific thing. Perhaps OS X doesn't allow process to use SIGKILL on other processes.

I also wonder how this translates to Windows environment?

BTW: From my experience implementing process termination can be quite complex. There are a number of mechanisms that can be used.

1) using JMX to ask a boot app to terminate 2) sending OS level signals of different strength (like SIGKILL, SIGTERM)

This can become complex because

a) The JMX mechanism is the only one that really works independent of OS but

b) OS signals don't work exactly the same on all OS's.

Generally, what we have implemented is to start by using JMX to 'ask nicely' and after some timeout become more aggressive at terminating process more directly.

See: https://docs.oracle.com/javase/8/docs/api/java/lang/Process.html

We use the destroy() and destroyForcibly() methods which I assumed correspond to SIGTERM and SIGKILL on linux systems and might be something different on other OS but implementing the same 'intent'.

Eskibear commented 6 years ago

So I assume this is a OS X specific thing. Perhaps OS X doesn't allow process to use SIGKILL on other processes.

I was talking about "SIGINT" instead of "SIGKILL". SIGKILL is the "strongest" signal as far as I know. SIGTERM is the default signal for the command kill in MacOSX. Both can terminate the process.

Following is a list of signals defined in MacOSX. (And I think it's compatible with x86 Linux)

     No    Name         Default Action       Description
     1     SIGHUP       terminate process    terminal line hangup
 *   2     SIGINT       terminate process    interrupt program
     3     SIGQUIT      create core image    quit program
     4     SIGILL       create core image    illegal instruction
     5     SIGTRAP      create core image    trace trap
     6     SIGABRT      create core image    abort program (formerly SIGIOT)
     7     SIGEMT       create core image    emulate instruction executed
     8     SIGFPE       create core image    floating-point exception
 *   9     SIGKILL      terminate process    kill program
     10    SIGBUS       create core image    bus error
     11    SIGSEGV      create core image    segmentation violation
     12    SIGSYS       create core image    non-existent system call invoked
     13    SIGPIPE      terminate process    write on a pipe with no reader
     14    SIGALRM      terminate process    real-time timer expired
 *   15    SIGTERM      terminate process    software termination signal
     16    SIGURG       discard signal       urgent condition present on socket
     17    SIGSTOP      stop process         stop (cannot be caught or ignored)
     18    SIGTSTP      stop process         stop signal generated from keyboard
     19    SIGCONT      discard signal       continue after stop
     20    SIGCHLD      discard signal       child status has changed
     21    SIGTTIN      stop process         background read attempted from control terminal
     22    SIGTTOU      stop process         background write attempted to control terminal
     23    SIGIO        discard signal       I/O is possible on a descriptor (see fcntl(2))
     24    SIGXCPU      terminate process    cpu time limit exceeded (see setrlimit(2))
     25    SIGXFSZ      terminate process    file size limit exceeded (see setrlimit(2))
     26    SIGVTALRM    terminate process    virtual time alarm (see setitimer(2))
     27    SIGPROF      terminate process    profiling timer alarm (see setitimer(2))
     28    SIGWINCH     discard signal       Window size change
     29    SIGINFO      discard signal       status request from keyboard
     30    SIGUSR1      terminate process    User defined signal 1
     31    SIGUSR2      terminate process    User defined signal 2

I also wonder how this translates to Windows environment?

"Windows does not support sending signals, but Node.js offers some emulation with process.kill(), and subprocess.kill(). Sending signal 0 can be used to test for the existence of a process. Sending SIGINT, SIGTERM, and SIGKILL cause the unconditional termination of the target process." Ref: https://nodejs.org/api/process.html#process_signal_events

And that is exactly the reason for the "known issue" in PR https://github.com/Microsoft/vscode-spring-boot-dashboard/pull/18 In windows, it simply "causes the unconditional termination".

kdvolder commented 6 years ago

Thanks for the extra information. I see somehow I confused SIGKILL and SIGINT. But yes, I think SIGTERM and SIGKILL are the signals we should be using on Linux and OS X. I suggest a strategy that sends increasingly stronger signals, trying to be nice first and then becoming increasingly aggressive if the nice way fails. So in this order JMX -> SIGTERM -> SIGKILL.

kdvolder commented 6 years ago

BTW: SIGTERM and the JMX approach are similar in that they are a 'nice request' sent to the process. This means that not every process is guaranteed to obey it. Buggy or stuck apps can disobey SIGTERM (an example might be a buggy shutdownhook in the process that deadlocks). So its a good idea to keep using SIGKILL as fallback when a process doesn't terminate within some reasonable time after receiving SIGTERM.

Eskibear commented 6 years ago

Agree with using JMX -> SIGTERM -> SIGKILL eventually to get better experience. For the first step, let me change SIGINT to SIGTERM to unblock MacOSX.