Skip to content

Commit 779d660

Browse files
larsrc-googlecopybara-github
authored andcommitted
Only allow worker async finishing when sandboxed.
All workers under dynamic execution are sandboxed, but on user interrupt non-sandboxed workers might keep working even after another invocation has begun, which could cause issues. Instead, we kill off interrupted workers that are neither sandboxed nor under dynamic execution. RELNOTES: None. PiperOrigin-RevId: 373141238
1 parent 05316ca commit 779d660

File tree

7 files changed

+91
-10
lines changed

7 files changed

+91
-10
lines changed

src/main/java/com/google/devtools/build/lib/worker/SandboxedWorker.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ final class SandboxedWorker extends SingleplexWorker {
3232
workerExecRoot = new WorkerExecRoot(workDir);
3333
}
3434

35+
@Override
36+
public boolean isSandboxed() {
37+
return true;
38+
}
39+
3540
@Override
3641
public void prepareExecution(
3742
SandboxInputs inputFiles, SandboxOutputs outputs, Set<PathFragment> workerFiles)

src/main/java/com/google/devtools/build/lib/worker/SingleplexWorker.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ Subprocess createProcess() throws IOException {
9292
return processBuilder.start();
9393
}
9494

95+
@Override
96+
public boolean isSandboxed() {
97+
return false;
98+
}
99+
95100
@Override
96101
public void prepareExecution(
97102
SandboxInputs inputFiles, SandboxOutputs outputs, Set<PathFragment> workerFiles)

src/main/java/com/google/devtools/build/lib/worker/Worker.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ SortedMap<PathFragment, HashCode> getWorkerFilesWithHashes() {
6666
return workerKey.getWorkerFilesWithHashes();
6767
}
6868

69+
/** Returns true if this worker is sandboxed. */
70+
public abstract boolean isSandboxed();
71+
6972
/**
7073
* Sets the reporter this {@code Worker} should report anomalous events to, or clears it. We
7174
* expect the reporter to be cleared at end of build.

src/main/java/com/google/devtools/build/lib/worker/WorkerFactory.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,9 @@ public PooledObject<Worker> wrap(Worker worker) {
9999
return new DefaultPooledObject<>(worker);
100100
}
101101

102-
/**
103-
* When a worker process is discarded, destroy its process, too.
104-
*/
102+
/** When a worker process is discarded, destroy its process, too. */
105103
@Override
106-
public void destroyObject(WorkerKey key, PooledObject<Worker> p) throws Exception {
104+
public void destroyObject(WorkerKey key, PooledObject<Worker> p) {
107105
if (workerOptions.workerVerbose) {
108106
int workerId = p.getObject().getWorkerId();
109107
reporter.handle(

src/main/java/com/google/devtools/build/lib/worker/WorkerProxy.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ final class WorkerProxy extends Worker {
5454
Runtime.getRuntime().addShutdownHook(shutdownHook);
5555
}
5656

57+
@Override
58+
public boolean isSandboxed() {
59+
return false;
60+
}
61+
5762
@Override
5863
void setReporter(EventHandler reporter) {
5964
// We might have created this multiplexer after setting the reporter for existing multiplexers

src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -457,12 +457,27 @@ WorkResponse execInWorker(
457457
try {
458458
response = worker.getResponse(request.getRequestId());
459459
} catch (InterruptedException e) {
460-
finishWorkAsync(
461-
key,
462-
worker,
463-
request,
464-
workerOptions.workerCancellation && Spawns.supportsWorkerCancellation(spawn));
465-
worker = null;
460+
if (worker.isSandboxed()) {
461+
// Sandboxed workers can safely finish their work async.
462+
finishWorkAsync(
463+
key,
464+
worker,
465+
request,
466+
workerOptions.workerCancellation && Spawns.supportsWorkerCancellation(spawn));
467+
worker = null;
468+
} else if (!key.isSpeculative()) {
469+
// Non-sandboxed workers interrupted outside of dynamic execution can only mean that
470+
// the user interrupted the build, and we don't want to delay finishing. Instead we
471+
// kill the worker.
472+
// Technically, workers are always sandboxed under dynamic execution, at least for now.
473+
try {
474+
workers.invalidateObject(key, worker);
475+
} catch (IOException e1) {
476+
// Nothing useful we can do here, in fact it may not be possible to get here.
477+
} finally {
478+
worker = null;
479+
}
480+
}
466481
throw e;
467482
} catch (IOException e) {
468483
restoreInterrupt(e);

src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,10 @@ public void testExecInWorker_sendsCancelMessageOnInterrupt()
179179
throws ExecException, InterruptedException, IOException {
180180
WorkerOptions workerOptions = new WorkerOptions();
181181
workerOptions.workerCancellation = true;
182+
workerOptions.workerSandboxing = true;
182183
when(spawn.getExecutionInfo())
183184
.thenReturn(ImmutableMap.of(ExecutionRequirements.SUPPORTS_WORKER_CANCELLATION, "1"));
185+
when(worker.isSandboxed()).thenReturn(true);
184186
WorkerSpawnRunner runner =
185187
new WorkerSpawnRunner(
186188
new SandboxHelpers(false),
@@ -196,6 +198,7 @@ public void testExecInWorker_sendsCancelMessageOnInterrupt()
196198
WorkerKey key = createWorkerKey(fs, "mnem", false);
197199
Path logFile = fs.getPath("/worker.log");
198200
Semaphore secondResponseRequested = new Semaphore(0);
201+
// Fake that the getting the regular response gets interrupted and we then answer the cancel.
199202
when(worker.getResponse(anyInt()))
200203
.thenThrow(new InterruptedException())
201204
.thenAnswer(
@@ -229,6 +232,53 @@ public void testExecInWorker_sendsCancelMessageOnInterrupt()
229232
.isEqualTo(WorkRequest.newBuilder().setRequestId(0).setCancel(true).build());
230233
}
231234

235+
@Test
236+
public void testExecInWorker_unsandboxedDiesOnInterrupt()
237+
throws InterruptedException, IOException {
238+
WorkerOptions workerOptions = new WorkerOptions();
239+
workerOptions.workerCancellation = true;
240+
workerOptions.workerSandboxing = false;
241+
when(spawn.getExecutionInfo())
242+
.thenReturn(ImmutableMap.of(ExecutionRequirements.SUPPORTS_WORKER_CANCELLATION, "1"));
243+
WorkerSpawnRunner runner =
244+
new WorkerSpawnRunner(
245+
new SandboxHelpers(false),
246+
fs.getPath("/execRoot"),
247+
createWorkerPool(),
248+
/* multiplex */ false,
249+
reporter,
250+
localEnvProvider,
251+
/* binTools */ null,
252+
resourceManager,
253+
/* runfilesTreeUpdater=*/ null,
254+
workerOptions);
255+
WorkerKey key = createWorkerKey(fs, "mnem", false);
256+
Path logFile = fs.getPath("/worker.log");
257+
when(worker.getResponse(anyInt())).thenThrow(new InterruptedException());
258+
259+
// Since this worker is not sandboxed, it will just get killed on interrupt.
260+
assertThrows(
261+
InterruptedException.class,
262+
() ->
263+
runner.execInWorker(
264+
spawn,
265+
key,
266+
context,
267+
new SandboxInputs(ImmutableMap.of(), ImmutableSet.of(), ImmutableMap.of()),
268+
SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()),
269+
ImmutableList.of(),
270+
inputFileCache,
271+
spawnMetrics));
272+
273+
assertThat(logFile.exists()).isFalse();
274+
verify(context, times(1)).report(ProgressStatus.EXECUTING, "worker");
275+
ArgumentCaptor<WorkRequest> argumentCaptor = ArgumentCaptor.forClass(WorkRequest.class);
276+
verify(worker, times(1)).putRequest(argumentCaptor.capture());
277+
assertThat(argumentCaptor.getAllValues().get(0))
278+
.isEqualTo(WorkRequest.newBuilder().setRequestId(0).build());
279+
verify(worker, times(1)).destroy();
280+
}
281+
232282
@Test
233283
public void testExecInWorker_noMultiplexWithDynamic()
234284
throws ExecException, InterruptedException, IOException {

0 commit comments

Comments
 (0)