Node node = computer.getNode();
if (node == null) {
throw new IllegalStateException("running computer lacks a node");
}
TaskListener listener = context.get(TaskListener.class);
Launcher launcher = node.createLauncher(listener);
Run<?,?> r = context.get(Run.class);
if (cookie == null) {
// First time around.
cookie = UUID.randomUUID().toString();
// Switches the label to a self-label, so if the executable is killed and restarted via ExecutorPickle, it will run on the same node:
label = computer.getName();
EnvVars env = computer.buildEnvironment(listener);
env.put(COOKIE_VAR, cookie);
synchronized (runningTasks) {
runningTasks.put(cookie, context);
}
// For convenience, automatically allocate a workspace, like WorkspaceStep would:
Job<?,?> j = r.getParent();
if (!(j instanceof TopLevelItem)) {
throw new Exception(j + " must be a top-level job");
}
FilePath p = node.getWorkspaceFor((TopLevelItem) j);
if (p == null) {
throw new IllegalStateException(node + " is offline");
}
WorkspaceList.Lease lease = computer.getWorkspaceList().allocate(p);
FilePath workspace = lease.path;
FlowNode flowNode = context.get(FlowNode.class);
flowNode.addAction(new WorkspaceActionImpl(workspace, flowNode));
listener.getLogger().println("Running on " + computer.getDisplayName() + " in " + workspace); // TODO hyperlink
context.invokeBodyLater(exec, computer, env, workspace).addCallback(new Callback(cookie, lease));
LOGGER.log(Level.FINE, "started {0}", cookie);
} else {
// just rescheduled after a restart; wait for task to complete
LOGGER.log(Level.FINE, "resuming {0}", cookie);
}
try {
// wait until the invokeBodyLater call above completes and notifies our Callback object
synchronized (runningTasks) {
while (runningTasks.containsKey(cookie)) {
LOGGER.log(Level.FINE, "waiting on {0}", cookie);
try {
runningTasks.wait();
} catch (InterruptedException x) {
// Jenkins is shutting down or this task was interrupted (Executor.doStop)
// TODO if the latter, we would like an API to StepExecution.stop the tip of our body
exec.recordCauseOfInterruption(r, listener);
}
}
}
} finally {
try {
launcher.kill(Collections.singletonMap(COOKIE_VAR, cookie));
} catch (ChannelClosedException x) {
// fine, Jenkins was shutting down
} catch (RequestAbortedException x) {
// slave was exiting; too late to kill subprocesses
}