Wait for process termination when the LocalSpawnRunner is interrupted.
The dynamic scheduler cancels the futures it uses to run spawns, which
causes the spawns executed via the LocalSpawnRunner to be interrupted.
Once interrupted, the spawn runner kills the subprocess... but it was
not waiting for the subprocess' termination.
This could lead to races because signal delivery is not synchronous and
the subprocess might continue running for a little bit while the dynamic
scheduler decides to do something else with the other (remote) spawn.
I haven't observed these races in the wild, but I'm seeing problems
while modifying the dynamic scheduler to forcibly cancel local spawns
once we have scored a cache hit.
To fix this problem, make the LocalSpawnRunner wait for process termination
after it forcibly destroys the subprocess on interrupt. And, while doing
so, homogenize all (?) the places where we try to forcibly terminate
subprocesses.
Addresses issue #7818.
RELNOTES: None.
PiperOrigin-RevId: 268724818
diff --git a/src/main/java/com/google/devtools/build/lib/shell/Subprocess.java b/src/main/java/com/google/devtools/build/lib/shell/Subprocess.java
index f058a9a..8cb0988 100644
--- a/src/main/java/com/google/devtools/build/lib/shell/Subprocess.java
+++ b/src/main/java/com/google/devtools/build/lib/shell/Subprocess.java
@@ -71,4 +71,29 @@
*/
@Override
void close();
+
+ /**
+ * Kills the subprocess and awaits for its termination so that we know it has released any
+ * resources it may have held.
+ */
+ default void destroyAndWait() {
+ destroy();
+
+ boolean wasInterrupted = false;
+ try {
+ while (true) {
+ try {
+ waitFor();
+ return;
+ } catch (InterruptedException ie) {
+ wasInterrupted = true;
+ }
+ }
+ } finally {
+ // Read this for detailed explanation: http://www.ibm.com/developerworks/library/j-jtp05236/
+ if (wasInterrupted) {
+ Thread.currentThread().interrupt(); // preserve interrupted status
+ }
+ }
+ }
}