use of com.cloudbees.groovy.cps.Outcome in project workflow-cps-plugin by jenkinsci.
the class CpsFlowExecution method croak.
/**
* Report a fatal error in the VM.
*/
void croak(Throwable t) {
setResult(Result.FAILURE);
onProgramEnd(new Outcome(null, t));
cleanUpHeap();
}
use of com.cloudbees.groovy.cps.Outcome in project workflow-cps-plugin by jenkinsci.
the class CpsThreadGroup method run.
/**
* Run the CPS program a little bit.
*
* <p>
* The amount of execution needs to be small enough so as not to hog CPS VM thread
* In particular, time sensitive activities like the interruption wants to run on CPS VM thread.
*
* @return
* true if this program can still execute further. false if the program is suspended
* and requires some external event to become resumable again. The false return value
* is akin to a Unix thread waiting for I/O completion.
*/
@CpsVmThreadOnly("root")
private boolean run() {
boolean changed = false;
boolean ending = false;
boolean stillRunnable = false;
// TODO: maybe instead of running all the thread, run just one thread in round robin
for (CpsThread t : threads.values().toArray(new CpsThread[threads.size()])) {
if (t.isRunnable()) {
Outcome o = t.runNextChunk();
if (o.isFailure()) {
// failed thread is non-resumable
assert !t.isAlive();
// workflow produced an exception
Result result = Result.FAILURE;
Throwable error = o.getAbnormal();
if (error instanceof FlowInterruptedException) {
result = ((FlowInterruptedException) error).getResult();
}
execution.setResult(result);
FlowNode fn = t.head.get();
if (fn != null) {
t.head.get().addAction(new ErrorAction(error));
}
}
if (!t.isAlive()) {
LOGGER.fine("completed " + t);
// do this after ErrorAction is set above
t.fireCompletionHandlers(o);
threads.remove(t.id);
if (threads.isEmpty()) {
execution.onProgramEnd(o);
ending = true;
}
} else {
stillRunnable |= t.isRunnable();
}
changed = true;
}
}
if (changed && !stillRunnable) {
execution.persistedClean = null;
saveProgramIfPossible(false);
}
if (ending) {
execution.cleanUpHeap();
if (scripts != null) {
scripts.clear();
}
closures.clear();
try {
Util.deleteFile(execution.getProgramDataFile());
} catch (IOException x) {
LOGGER.log(Level.WARNING, "Failed to delete program.dat in " + execution, x);
}
}
return stillRunnable;
}
use of com.cloudbees.groovy.cps.Outcome in project workflow-cps-plugin by jenkinsci.
the class CpsBodyExecution method launch.
/**
* Starts evaluating the body.
*
* If the body is a synchronous closure, this method evaluates the closure synchronously.
* Otherwise, the body is asynchronous and the method schedules another thread to evaluate the body.
*
* @param currentThread
* The thread whose context the new thread will inherit.
*/
@CpsVmThreadOnly
/*package*/
void launch(CpsBodyInvoker params, CpsThread currentThread, FlowHead head) {
if (isLaunched())
throw new IllegalStateException("Already launched");
StepStartNode sn = addBodyStartFlowNode(head);
for (Action a : params.startNodeActions) {
if (a != null)
sn.addAction(a);
}
head.setNewHead(sn);
CpsFlowExecution.maybeAutoPersistNode(sn);
StepContext sc = new CpsBodySubContext(context, sn);
for (BodyExecutionCallback c : callbacks) {
c.onStart(sc);
}
try {
// TODO: handle arguments to closure
Object x = params.body.getBody(currentThread).call();
// pointless synchronization to make findbugs happy. This is already done, so there's no cancelling this anyway.
synchronized (this) {
this.thread = currentThread;
}
onSuccess.receive(x);
} catch (CpsCallableInvocation e) {
// execute this closure asynchronously
// TODO: does it make sense that the new thread shares the same head?
CpsThread t = currentThread.group.addThread(createContinuable(currentThread, e), head, ContextVariableSet.from(currentThread.getContextVariables(), params.contextOverrides));
// due to earlier cancellation
synchronized (this) {
t.resume(new Outcome(null, stopped));
assert this.thread == null;
this.thread = t;
}
} catch (Throwable t) {
// body has completed synchronously and abnormally
synchronized (this) {
this.thread = currentThread;
}
onFailure.receive(t);
}
}
use of com.cloudbees.groovy.cps.Outcome in project workflow-cps-plugin by jenkinsci.
the class CpsScriptTest method evaluateShallBeCpsTransformed.
/**
* The code getting evaluated must also get CPS transformation.
*/
@Ignore("TODO usually future == null, perhaps because CpsThread.resume is intended to be @CpsVmThreadOnly (so assumes that the promise is just set is not cleared by runNextChunk) yet we are calling it from the test thread; extremely dubious test design, should probably be using SemaphoreStep to be more realistic")
@Test
public void evaluateShallBeCpsTransformed() throws Exception {
CpsFlowDefinition flow = new CpsFlowDefinition("evaluate('1+com.cloudbees.groovy.cps.Continuable.suspend(2+3)')");
createExecution(flow);
exec.start();
exec.waitForSuspension();
// TODO: can't we assert that the suspend() ended with value 5?
// this should have paused at suspend, so we are going to resume it by having it return a value we control
assertFalse(dumpError(), exec.isComplete());
ListenableFuture<CpsThreadGroup> pp = exec.programPromise;
assertNotNull(pp);
Future<Object> future = pp.get().getThread(0).resume(new Outcome(7, null));
assertNotNull(future);
assertEquals(8, future.get());
exec.waitForSuspension();
assertTrue(dumpError(), exec.isComplete());
assertEquals(dumpError(), Result.SUCCESS, exec.getResult());
}
Aggregations