/** Checks for progress or completion of the external task. */ @Override public void run() { task = null; try (WithThreadName naming = new WithThreadName(": checking " + remote + " on " + node)) { check(); } catch (Exception x) { // TODO use ErrorLoggingScheduledThreadPoolExecutor from core if it becomes public LOGGER.log(Level.WARNING, null, x); } finally { if (recurrencePeriod > 0) { task = THREAD_POOL.schedule(this, recurrencePeriod, TimeUnit.MILLISECONDS); } } }
Futures.addCallback(programPromise, new FutureCallback<CpsThreadGroup>() { final Exception source = new Exception(); // call stack of this object captures who called this. useful during debugging. @Override
/** * Schedules the execution of this thread from the last {@linkplain Continuable#suspend(Object)} point. * * @return * Future that promises the completion of the next {@link #runNextChunk()}. */ public Future<Object> resume(Outcome v) { if (resumeValue != null) { return Futures.immediateFailedFuture(new IllegalStateException("Already resumed with " + resumeValue)); } resumeValue = v; promise = SettableFuture.create(); group.scheduleRun(); return promise; }
@Override public String getStatus() { StringBuilder b = new StringBuilder(); try (Timeout timeout = Timeout.limit(2, TimeUnit.SECONDS)) { // CpsThreadDump applies a 3s timeout anyway FilePath workspace = getWorkspace(); if (workspace != null) { b.append(controller.getDiagnostics(workspace, launcher())); } else { b.append("waiting to reconnect to ").append(remote).append(" on ").append(node); } } catch (IOException | InterruptedException x) { b.append("failed to look up workspace ").append(remote).append(" on ").append(node).append(": ").append(x); } b.append("; recurrence period: ").append(recurrencePeriod).append("ms"); ScheduledFuture<?> t = task; if (t != null) { b.append("; check task scheduled; cancelled? ").append(t.isCancelled()).append(" done? ").append(t.isDone()); } t = stopTask; if (t != null) { b.append("; stop task scheduled; cancelled? ").append(t.isCancelled()).append(" done? ").append(t.isDone()); } return b.toString(); }
@Override public ListenableFuture<List<StepExecution>> getCurrentExecutions(final boolean innerMostOnly) { if (programPromise == null || isComplete()) { return Futures.immediateFuture(Collections.<StepExecution>emptyList());
private @CheckForNull FilePath getWorkspace() throws AbortException { if (ws == null) { ws = FilePathUtils.find(node, remote); if (ws == null) { LOGGER.log(Level.FINE, "Jenkins is not running, no such node {0}, or it is offline", node); return null; } } boolean directory; try (Timeout timeout = Timeout.limit(10, TimeUnit.SECONDS)) { directory = ws.isDirectory(); } catch (Exception x) { // RequestAbortedException, ChannelClosedException, EOFException, wrappers thereof; InterruptedException if it just takes too long. LOGGER.log(Level.FINE, node + " is evidently offline now", x); ws = null; if (!printedCannotContactMessage) { logger().println("Cannot contact " + node + ": " + x); printedCannotContactMessage = true; } return null; } if (!directory) { throw new AbortException("missing workspace " + remote + " on " + node); } return ws; }
Futures.addCallback(programPromise, new FutureCallback<CpsThreadGroup>() { @Override public void onSuccess(CpsThreadGroup g) { if (v) {
@Override public ListenableFuture<Void> saveState() { try { final SettableFuture<Void> f = SettableFuture.create(); CpsFlowExecution exec = getFlowExecution(); if (!exec.getDurabilityHint().isPersistWithEveryStep()) { f.set(null); return f; } exec.runInCpsVmThread(new FutureCallback<CpsThreadGroup>() { @Override public void onSuccess(CpsThreadGroup result) { try { // TODO keep track of whether the program was saved anyway after saveState was called but before now, and do not bother resaving it in that case if (result.getExecution().getDurabilityHint().isPersistWithEveryStep()) { result.getExecution().getStorage().flush(); result.saveProgram(); } f.set(null); } catch (Exception x) { f.setException(x); } } @Override public void onFailure(Throwable t) { f.setException(t); } }); return f; } catch (IOException x) { return Futures.immediateFailedFuture(x); } }
@Restricted(DoNotUse.class) @Terminator public static void suspendAll() { CpsFlowExecution exec = null; try (Timeout t = Timeout.limit(3, TimeUnit.MINUTES)) { // TODO some complicated sequence of calls to Futures could allow all of them to run in parallel LOGGER.fine("starting to suspend all executions"); for (FlowExecution execution : FlowExecutionList.get()) {
Futures.addCallback( r.restorePickles(pickleFutures = new ArrayList<>()),
programPromise = Futures.immediateFailedFuture(ex); throw new IOException("Failed to even create placeholder nodes for execution", ex); } finally { if (programPromise == null) { programPromise = Futures.immediateFailedFuture(new IllegalStateException("completed or broken execution"));
@Nonnull private List<String> categoriesForPipeline(Task task) { if (task instanceof PlaceholderTask) { PlaceholderTask placeholderTask = (PlaceholderTask)task; Run<?, ?> r = placeholderTask.run(); if (r != null) { Map<String, List<String>> categoriesByFlowNode = ThrottleJobProperty.getCategoriesForRunByFlowNode(r); if (!categoriesByFlowNode.isEmpty()) { try (Timeout t = Timeout.limit(100, TimeUnit.MILLISECONDS)) { FlowNode firstThrottle = firstThrottleStartNode(placeholderTask.getNode()); if (firstThrottle != null) { List<String> categories = categoriesByFlowNode.get(firstThrottle.getId()); if (categories != null) { return categories; } } } catch (IOException | InterruptedException e) { LOGGER.log(Level.WARNING, "Error getting categories for pipeline {0}: {1}", new Object[] {task.getDisplayName(), e}); return new ArrayList<>(); } } } } return new ArrayList<>(); }
owner.getListener().getLogger().println("Failing build: shutting down master and build is marked to not resume"); final Throwable x = new FlowInterruptedException(Result.ABORTED); Futures.addCallback(this.getCurrentExecutions(/* cf. JENKINS-26148 */true), new FutureCallback<List<StepExecution>>() { @Override public void onSuccess(List<StepExecution> l) { for (StepExecution e : Iterators.reverse(l)) {
programPromise = Futures.immediateFailedFuture(new IllegalStateException("Failed loading heads", failureReason)); LOGGER.log(Level.INFO, "Creating placeholder flownodes for execution: " + this); if (this.owner != null) {
@Restricted(NoExternalUse.class) // for Jelly public @CheckForNull String getEnclosingLabel() { if (!context.isReady()) { return null; } FlowNode executorStepNode; try (Timeout t = Timeout.limit(100, TimeUnit.MILLISECONDS)) { executorStepNode = context.get(FlowNode.class); } catch (Exception x) { LOGGER.log(Level.FINE, null, x); return null; } if (executorStepNode == null) { return null; } List<FlowNode> heads = executorStepNode.getExecution().getCurrentHeads(); int headsHash = heads.hashCode(); // deterministic based on IDs of those heads if (headsHash == lastCheckedHashCode) { return lastEnclosingLabel; } else { lastCheckedHashCode = headsHash; return lastEnclosingLabel = computeEnclosingLabel(executorStepNode, heads); } } private String computeEnclosingLabel(FlowNode executorStepNode, List<FlowNode> heads) {
/** Sends {@link StepContext#onFailure} to all running (leaf) steps. */ @RequirePOST public void doTerm() { checkPermission(Item.CANCEL); if (!isInProgress() || /* redundant, but make FindBugs happy */ execution == null) { return; } final Throwable x = new FlowInterruptedException(Result.ABORTED); Futures.addCallback(execution.getCurrentExecutions(/* cf. JENKINS-26148 */true), new FutureCallback<List<StepExecution>>() { @Override public void onSuccess(List<StepExecution> l) { for (StepExecution e : Iterators.reverse(l)) { StepContext context = e.getContext(); context.onFailure(x); try { FlowNode n = context.get(FlowNode.class); if (n != null) { listener.getLogger().println("Terminating " + n.getDisplayFunctionName()); } } catch (Exception x) { LOGGER.log(Level.FINE, null, x); } } } @Override public void onFailure(Throwable t) {} }); printLater(StopState.KILL, "Click here to forcibly kill entire build"); }
CURRENT.set(this); try (Timeout timeout = Timeout.limit(5, TimeUnit.MINUTES)) { LOGGER.log(FINE, "runNextChunk on {0}", resumeValue); final Outcome o = resumeValue;
/** * Stops the execution of this thread. If it's paused to wait for the completion of {@link StepExecution}, * call {@link StepExecution#stop(Throwable)} to give it a chance to clean up. * * <p> * If the execution is not inside a step (meaning it's paused in a safe point), then have the CPS thread * throw a given {@link Throwable} to break asap. */ @CpsVmThreadOnly public void stop(Throwable t) { StepExecution s = getStep(); // this is the part that should run in CpsVmThread if (s == null) { // if it's not running inside a StepExecution, we need to set an interrupt flag // and interrupt at an earliest convenience Outcome o = new Outcome(null, t); if (resumeValue==null) { resume(o); } else { // this thread was already resumed, so just overwrite the value with a Throwable resumeValue = o; } return; } try (Timeout timeout = Timeout.limit(30, TimeUnit.SECONDS)) { s.stop(t); } catch (Exception e) { t.addSuppressed(e); s.getContext().onFailure(t); } }
return; // slave not yet ready, wait for another day try (Timeout timeout = Timeout.limit(10, TimeUnit.SECONDS)) { if (controller.writeLog(workspace, logger())) { getContext().saveState();