This question already has an answer here:
- Sending a POSIX signal from the JVM 2 answers
- how can I kill a Linux process in java with SIGKILL Process.destroy() does SIGTERM 4 answers
A Script For Testing
The script below just hangs until you press Ctrl+C, then it takes two seconds to shut down. I wrote it to investigate Java's process.destroy()
#! /usr/bin/env bash
# dieslowly.sh
bail() {
echo exiting...
sleep 2
echo ...exited
trap - SIGINT SIGTERM
kill -- -$$
}
trap bail SIGTERM SIGINT
echo "running..."
sleep infinity
I think it works because:
- If I run
./dieSlowly.sh
and press Ctrl+C, it waits two seconds and then exits - If run
./dieSlowly.sh
and then in a separate terminal runkill -2 -<pid>
, it waits two seconds then exits
A Process-Handling Test
This (Java) test calls the bash script, and tries to allow it to shut down slowly
void kill_dummy()
{
// Run the test script
Process process = new ProcessBuilder("./dieSlowly.sh").start();
// terminate the process, wait for it to stop, then kill it
DateTime before = new DateTime();
process.destroy(); // this doesn't seem to do anything
// Runtime rt = Runtime.getRuntime();
// rt.exec('kill -9 ' + process.pid); // this kills it too fast
// rt.exec('kill -2 -' + process.pid); // this doesn't kill it at all
// rt.exec('kill -2 ' + process.pid); // neither does this
process.waitFor(4, TimeUnit.SECONDS);
process.destroyForcibly();
DateTime after = new DateTime();
// expect it to have taken less than four secongs
assert (after.getMillis() - before.getMillis() < 4000);
}
I expect the test to take about two seconds--which is how long it takes the script to shut down on its own.
Instead it takes about four seconds. Using the debugger I have verified that the destroy()
call has no effect. This causes waitFor
to timeout. destroyForcibly()
then kills the process with extreme prejudice--which I want to avoid.
The Process Tree
If I put a breakpoint on destroyForcibly()
the command ps axjf
displays this tree:
\_ -zsh
| \_ /bin/sh /opt/idea-IC-172.4343.14/bin/idea.sh /path/to/my/project
| \_ idea
| \_ idea
| \_ bash ./dieSlowly.sh
| \_ sleep infinity
\_ -zsh
| \_ -zsh
| \_ bash ./dieSlowly.sh
| \_ sleep infinity
My Question
What modifications can I make so that the test triggers the slow-shutdown pathway on the test?
Edit
I understand that signals are an OS-specific thing, and the JVM is limited because of its intention to be os-independent. However, I do successfully kill the process when I run kill -9 <pid>
from the JVM, so it seems that it is possible to relay commands to the OS and have it send signals.
Assuming that I can't use process.destroy()
, then the question becomes:
Why can I call
kill -9
from the JVM, but notkill -2
?