Prevent synchronous cleanupSandboxBase from blocking on interrupted commands When `shouldCleanupSandboxBase` is true or during abrupt command termination, `cleanupSandboxBase()` synchronously deletes directories. By having `SpawnRunner.cleanupSandboxBase()` declare `throws InterruptedException` and throw on `Thread.currentThread().isInterrupted()` during directory traversal, `SandboxModule.afterCommand()` catches the interruption and skips blocking synchronous directory tree deletions and `checkSandboxBaseTopOnlyContainsPersistentDirs()` validations on Ctrl-C. Part of https://github.com/bazelbuild/bazel/issues/19533 PiperOrigin-RevId: 971406665 Change-Id: I7328b1d5e8cac3088cdb8816e20515e972d66f3f
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java index e78a2a7..9ca0804 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java
@@ -409,8 +409,10 @@ * entries * @param treeDeleter scheduler for tree deletions * @throws IOException if there are problems deleting the entries + * @throws InterruptedException if the cleanup is interrupted */ - default void cleanupSandboxBase(Path sandboxBase, TreeDeleter treeDeleter) throws IOException {} + default void cleanupSandboxBase(Path sandboxBase, TreeDeleter treeDeleter) + throws IOException, InterruptedException {} /** * Returns a {@link SpawnResult.Builder} prepopulated with the runner name and the spawn digest.
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java index 7c0134a..c69456c 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
@@ -451,12 +451,17 @@ } @Override - public void cleanupSandboxBase(Path sandboxBase, TreeDeleter treeDeleter) throws IOException { + public void cleanupSandboxBase(Path sandboxBase, TreeDeleter treeDeleter) + throws IOException, InterruptedException { Path root = sandboxBase.getChild(getName()); if (root.exists()) { for (Path child : root.getDirectoryEntries()) { + if (Thread.currentThread().isInterrupted()) { + throw new InterruptedException(); + } treeDeleter.deleteTree(child); } + root.delete(); } } }
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index adbed03..43137a9 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
@@ -523,7 +523,8 @@ } @Override - public void cleanupSandboxBase(Path sandboxBase, TreeDeleter treeDeleter) throws IOException { + public void cleanupSandboxBase(Path sandboxBase, TreeDeleter treeDeleter) + throws IOException, InterruptedException { VirtualCgroup.deleteInstance(); // Delete the inaccessible files synchronously, bypassing the treeDeleter. They are only a // couple of files that can be deleted fast, and ensuring they are gone at the end of every
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java index 45acb85..bc7a5e4 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java
@@ -461,15 +461,15 @@ checkNotNull(sandboxBase, "shouldCleanupSandboxBase implies sandboxBase has been set"); for (SpawnRunner spawnRunner : spawnRunners) { spawnRunner.cleanupSandboxBase(sandboxBase, treeDeleter); - sandboxBase.getChild(spawnRunner.getName()).delete(); } + shouldCleanupSandboxBase = false; + checkSandboxBaseTopOnlyContainsPersistentDirs(sandboxBase); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } catch (IOException e) { env.getReporter() .handle(Event.warn("Failed to delete contents of sandbox " + sandboxBase + ": " + e)); } - shouldCleanupSandboxBase = false; - - checkSandboxBaseTopOnlyContainsPersistentDirs(sandboxBase); // We intentionally keep sandboxBase around, without resetting it to null, in case we have // asynchronous deletions going on. In that case, we'd still want to retry this during // shutdown.