diff --git a/core/pom.xml b/core/pom.xml
index 827d6e3c5ec7..cb7f3d52d03e 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -515,7 +515,7 @@ THE SOFTWARE.
org.jvnet.winp
winp
- 1.26
+ 1.27
org.jenkins-ci
diff --git a/core/src/main/java/hudson/util/ProcessTree.java b/core/src/main/java/hudson/util/ProcessTree.java
index 0ef4c2192e7d..843653f72da8 100644
--- a/core/src/main/java/hudson/util/ProcessTree.java
+++ b/core/src/main/java/hudson/util/ProcessTree.java
@@ -144,6 +144,8 @@ public final Iterator iterator() {
*/
public abstract void killAll(Map modelEnvVars) throws InterruptedException;
+ private final long softKillWaitSeconds = Integer.getInteger("SoftKillWaitSeconds", 2 * 60); // by default processes get at most 2 minutes to respond to SIGTERM (JENKINS-17116)
+
/**
* Convenience method that does {@link #killAll(Map)} and {@link OSProcess#killRecursively()}.
* This is necessary to reliably kill the process and its descendants, as some OS
@@ -501,6 +503,8 @@ public void killRecursively() throws InterruptedException {
return;
LOGGER.log(FINER, "Killing recursively {0}", getPid());
+ // Firstly try to kill the root process gracefully, then do a forcekill if it does not help (algorithm is described in JENKINS-17116)
+ killSoftly();
p.killRecursively();
killByKiller();
}
@@ -512,10 +516,39 @@ public void kill() throws InterruptedException {
}
LOGGER.log(FINER, "Killing {0}", getPid());
+ // Firstly try to kill it gracefully, then do a forcekill if it does not help (algorithm is described in JENKINS-17116)
+ killSoftly();
p.kill();
killByKiller();
}
+ private void killSoftly() throws InterruptedException {
+ // send Ctrl+C to the process
+ try {
+ if (!p.sendCtrlC()) {
+ return;
+ }
+ }
+ catch (WinpException e) {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE, "Failed to send CTRL+C to pid=" + getPid(), e);
+ }
+ return;
+ }
+
+ // after that wait for it to cease to exist
+ long deadline = System.nanoTime() + softKillWaitSeconds * 1000000000;
+ int sleepTime = 10; // initially we sleep briefly, then sleep up to 1sec
+ do {
+ if (!p.isRunning()) {
+ break;
+ }
+
+ Thread.sleep(sleepTime);
+ sleepTime = Math.min(sleepTime * 2, 1000);
+ } while (System.nanoTime() < deadline);
+ }
+
@Override
public synchronized List getArguments() {
if(args==null) {
@@ -718,12 +751,29 @@ protected final File getFile(String relativePath) {
* Tries to kill this process.
*/
public void kill() throws InterruptedException {
+ // after sending SIGTERM, wait for the process to cease to exist
+ long deadline = System.nanoTime() + softKillWaitSeconds * 1000000000;
+ kill(deadline);
+ }
+
+ private void kill(long deadline) throws InterruptedException {
if (getVeto() != null)
return;
try {
int pid = getPid();
LOGGER.fine("Killing pid="+pid);
UnixReflection.destroy(pid);
+ // after sending SIGTERM, wait for the process to cease to exist
+ int sleepTime = 10; // initially we sleep briefly, then sleep up to 1sec
+ File status = getFile("status");
+ do {
+ if (!status.exists()) {
+ break; // status is gone, process therefore as well
+ }
+
+ Thread.sleep(sleepTime);
+ sleepTime = Math.min(sleepTime * 2, 1000);
+ } while (System.nanoTime() < deadline);
} catch (IllegalAccessException e) {
// this is impossible
IllegalAccessError x = new IllegalAccessError();
@@ -740,11 +790,22 @@ public void kill() throws InterruptedException {
}
public void killRecursively() throws InterruptedException {
+ // after sending SIGTERM, wait for the processes to cease to exist until the deadline
+ long deadline = System.nanoTime() + softKillWaitSeconds * 1000000000;
+ killRecursively(deadline);
+ }
+
+ private void killRecursively(long deadline) throws InterruptedException {
// We kill individual processes of a tree, so handling vetoes inside #kill() is enough for UnixProcess es
LOGGER.fine("Recursively killing pid="+getPid());
- for (OSProcess p : getChildren())
- p.killRecursively();
- kill();
+ for (OSProcess p : getChildren()) {
+ if (p instanceof UnixProcess) {
+ ((UnixProcess)p).killRecursively(deadline);
+ } else {
+ p.killRecursively(); // should not happen, fallback to non-deadline version
+ }
+ }
+ kill(deadline);
}
/**