From 5765cfe010fab2e87cdd95e822102dbbf9e0231c Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 8 Nov 2019 12:33:51 +0000 Subject: [PATCH] Allow 5 seconds for child to handle SIGINT before destroying it Previously, when RunProcess handled a SIGINT it would immediately attempt to destroy the process that it had run. This created a race condition between the SIGINT being handled by the child process and RunProcess destroying the child. The exact behavior of destroy is implementation dependent and it may result in forcible termination of the process where shutdown hooks are not called. This is what happens on Windows. The exit code in such a case is 1 which prevents anything from waiting for the process to complete from detecting that it ended as a result of a SIGINT, leaving it with no choice but to report an error. This is what happens with mvn spring-boot:run with a forked process on Windows and results in the build failing. This commit updates RunProcess to allow the child process to handle the SIGINT itself, waiting for up to five seconds for that to happen before the process is then destroyed. Given this time, the child process exits with 130 which RunMojo already handles correctly as indicating that the process died due to SIGINT and the build completes with success as a result. Fixes gh-18936 --- .../boot/loader/tools/RunProcess.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/RunProcess.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/RunProcess.java index c4438a06f89e..1c35c7555926 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/RunProcess.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/RunProcess.java @@ -113,13 +113,33 @@ public Process getRunningProcess() { * @return {@code true} if stopped */ public boolean handleSigInt() { - // if the process has just ended, probably due to this SIGINT, consider handled. - if (hasJustEnded()) { + if (allowChildToHandleSigInt()) { return true; } return doKill(); } + private boolean allowChildToHandleSigInt() { + Process process = this.process; + if (process == null) { + return true; + } + long end = System.currentTimeMillis() + 5000; + while (System.currentTimeMillis() < end) { + if (!process.isAlive()) { + return true; + } + try { + Thread.sleep(500); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + return false; + } + } + return false; + } + /** * Kill this process. */