Class CpsFlowExecution

java.lang.Object
org.jenkinsci.plugins.workflow.flow.FlowExecution
org.jenkinsci.plugins.workflow.cps.CpsFlowExecution
All Implemented Interfaces:
org.jenkinsci.plugins.workflow.flow.BlockableResume, org.jenkinsci.plugins.workflow.graph.FlowActionStorage, org.jenkinsci.plugins.workflow.graph.GraphLookupView

public class CpsFlowExecution extends org.jenkinsci.plugins.workflow.flow.FlowExecution implements org.jenkinsci.plugins.workflow.flow.BlockableResume
FlowExecution implemented with Groovy CPS.

State Transition

CpsFlowExecution goes through the following states:


                                    +----------------------+
                                    |                      |
                                    v                      |
 PERSISTED --> PREPARING --> SUSPENDED --> RUNNABLE --> RUNNING --> COMPLETE
                                             ^
                                             |
                                           INITIAL
 
INITIAL
When a new CpsFlowExecution is created, it starts from here. When start() method is called, we get one thread scheduled, and we arrive at RUNNABLE state.
PERSISTED
CpsFlowExecution is on disk with its owner, for example in build.xml of the workflow run. Nothing exists in memory. For example, Jenkins is not running. Transition from this into PREPARING is triggered outside our control by XStream using CpsFlowExecution.ConverterImpl to unmarshal CpsFlowExecution. FlowExecution.onLoad() is called at the end, and we arrive at the PREPARING state.
PREPARING
CpsFlowExecution is in memory, but CpsThreadGroup isn't. We are trying to restore all the ephemeral pickles that are necessary to get workflow going again. programPromise represents a promise of completing this state. PickleResolver keeps track of this, and when it's all done, we arrive at SUSPENDED state.
SUSPENDED
CpsThreadGroup is in memory, but all CpsThreads are not runnable, which means they are waiting for some conditions to trigger (such as a completion of a shell script that's executing, human approval, etc). CpsFlowExecution and CpsThreadGroup are safe to persist. When a condition is met, CpsThread.resume(Outcome) is called, and that thread becomes runnable, and we move to the RUNNABLE state.
RUNNABLE
Some of CpsThreads are runnable, but we aren't actually running. The conditions that triggered CpsThread is captured in CpsThread.resumeValue. As we get into this state, CpsThreadGroup.scheduleRun() should be called to schedule the execution. CpsFlowExecution and CpsThreadGroup are safe to persist in this state, just like in the SUSPENDED state. When CpsThreadGroup.runner allocated a real Java thread to the execution, we move to the RUNNING state.
RUNNING
A thread is inside CpsThreadGroup.run() and is actively mutating the object graph inside the script. This state continues until no threads are runnable any more. Only one thread executes CpsThreadGroup.run(). In this state, CpsFlowExecution still need to be persistable (because generally we don't get to control when it is persisted), but CpsThreadGroup isn't safe to persist. When the Java thread leaves CpsThreadGroup.run(), we move to the SUSPENDED state.
COMPLETE
All the CpsThreads have terminated and there's nothing more to execute, and there's no more events to wait. The result is finalized and there's no further state change.
Author:
Kohsuke Kawaguchi
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static final class 
     
    static class 
     
    static class 
     

    Nested classes/interfaces inherited from interface org.jenkinsci.plugins.workflow.graph.GraphLookupView

    org.jenkinsci.plugins.workflow.graph.GraphLookupView.EnclosingBlocksIterable
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static boolean
    If true, then when the execution completes, we migrate the flow node storage from SimpleXStreamFlowNodeStorage to BulkFlowNodeStorage.
    com.google.common.util.concurrent.ListenableFuture<CpsThreadGroup>
    Loading of the program is asynchronous because it requires us to re-obtain stateful objects.

    Fields inherited from class org.jenkinsci.plugins.workflow.flow.FlowExecution

    durabilityHint, internalGraphLookup
  • Constructor Summary

    Constructors
    Constructor
    Description
    CpsFlowExecution(String script, boolean sandbox, org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner)
     
    CpsFlowExecution(String script, boolean sandbox, org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner, org.jenkinsci.plugins.workflow.flow.FlowDurabilityHint durabilityHint)
     
    CpsFlowExecution(String script, org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner)
    Deprecated.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    addListener(org.jenkinsci.plugins.workflow.flow.GraphListener listener)
     
    protected void
     
    boolean
    See JENKINS-22941 for why this exists.
    boolean
    If true, we are allowed to resume the build because resume is enabled AND we shut down cleanly.
     
    com.google.common.util.concurrent.ListenableFuture<List<org.jenkinsci.plugins.workflow.steps.StepExecution>>
    getCurrentExecutions(boolean innerMostOnly)
     
    List<org.jenkinsci.plugins.workflow.graph.FlowNode>
     
    org.jenkinsci.plugins.workflow.cps.FlowHead
    getFlowHead(int id)
     
     
    Finds the expected next loaded script name, like Script1.
    org.jenkinsci.plugins.workflow.graph.FlowNode
     
    org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner
     
     
     
    groovy.lang.GroovyShell
    Returns a groovy compiler used to load the script.
    org.jenkinsci.plugins.workflow.support.storage.FlowNodeStorage
     
    Directory where workflow stores its state.
    Synchronously obtain the current state of the workflow program.
    groovy.lang.GroovyShell
    Returns a groovy compiler used to load the trusted script.
    protected void
     
    void
    interrupt(Result result, CauseOfInterruption... causes)
     
    int
     
    Assigns a new ID.
    boolean
     
    boolean
    isCurrentHead(org.jenkinsci.plugins.workflow.graph.FlowNode n)
     
    boolean
    Has the execution been marked done - note that legacy builds may not have that flag persisted, in which case we look for a single FlowEndNode head (see: isComplete() and FlowExecution.isComplete())
    boolean
     
    boolean
    If true, pipeline is forbidden to resume even if it can.
    boolean
    True if executing with groovy-sandbox, false if executing with approval.
    loadActions(org.jenkinsci.plugins.workflow.graph.FlowNode node)
     
    void
    loadProgramAsync(File programDataFile)
    Deserializes CpsThreadGroup from getProgramDataFile() if necessary.
    static void
    maybeAutoPersistNode(org.jenkinsci.plugins.workflow.graph.FlowNode node)
    Invoke me to toggle autopersist back on for steps that delay it.
    void
    onLoad(org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner)
     
    void
    pause(boolean v)
    Pause or unpause the execution.
    void
    removeListener(org.jenkinsci.plugins.workflow.flow.GraphListener listener)
     
    void
    saveActions(org.jenkinsci.plugins.workflow.graph.FlowNode node, List<Action> actions)
     
    void
     
    void
    setResumeBlocked(boolean resumeBlocked)
     
    void
     
    static void
     
     
    void
    Deprecated.
    Use some other idiom, like SemaphoreStep.

    Methods inherited from class org.jenkinsci.plugins.workflow.flow.FlowExecution

    findAllEnclosingBlockStarts, findEnclosingBlockStart, getCauseOfFailure, getCurrentExecutions, getDurabilityHint, getEndNode, getInternalGraphLookup, getUrl, isActive, iterateEnclosingBlocks, notifyShutdown, onLoad

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
  • Field Details

    • OPTIMIZE_STORAGE_UPON_COMPLETION

      public static boolean OPTIMIZE_STORAGE_UPON_COMPLETION
      If true, then when the execution completes, we migrate the flow node storage from SimpleXStreamFlowNodeStorage to BulkFlowNodeStorage.
    • programPromise

      public transient volatile com.google.common.util.concurrent.ListenableFuture<CpsThreadGroup> programPromise
      Loading of the program is asynchronous because it requires us to re-obtain stateful objects. This object represents a Future for filling in CpsThreadGroup. TODO: provide a mechanism to diagnose how far along this process is.
      See Also:
      • runInCpsVmThread(FutureCallback)
  • Constructor Details

    • CpsFlowExecution

      @Deprecated public CpsFlowExecution(String script, org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner) throws IOException
      Deprecated.
      Throws:
      IOException
    • CpsFlowExecution

      public CpsFlowExecution(@NonNull String script, boolean sandbox, @NonNull org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner, @CheckForNull org.jenkinsci.plugins.workflow.flow.FlowDurabilityHint durabilityHint) throws IOException
      Throws:
      IOException
    • CpsFlowExecution

      public CpsFlowExecution(String script, boolean sandbox, org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner) throws IOException
      Throws:
      IOException
  • Method Details

    • isResumeBlocked

      public boolean isResumeBlocked()
      If true, pipeline is forbidden to resume even if it can.
      Specified by:
      isResumeBlocked in interface org.jenkinsci.plugins.workflow.flow.BlockableResume
    • setResumeBlocked

      public void setResumeBlocked(boolean resumeBlocked)
      Specified by:
      setResumeBlocked in interface org.jenkinsci.plugins.workflow.flow.BlockableResume
    • getShell

      public groovy.lang.GroovyShell getShell()
      Returns a groovy compiler used to load the script.
      See Also:
      • "doc/classloader.md"
      • GroovyShell.getClassLoader()
    • getTrustedShell

      public groovy.lang.GroovyShell getTrustedShell()
      Returns a groovy compiler used to load the trusted script.
      See Also:
      • "doc/classloader.md"
    • getStorage

      public org.jenkinsci.plugins.workflow.support.storage.FlowNodeStorage getStorage()
    • getScript

      public String getScript()
    • getLoadedScripts

      public Map<String,String> getLoadedScripts()
    • isSandbox

      public boolean isSandbox()
      True if executing with groovy-sandbox, false if executing with approval.
    • getOwner

      public org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner getOwner()
      Specified by:
      getOwner in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • getStorageDir

      public File getStorageDir() throws IOException
      Directory where workflow stores its state.
      Throws:
      IOException
    • start

      public void start() throws IOException
      Specified by:
      start in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      Throws:
      IOException
    • iotaStr

      @Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class) public String iotaStr()
      Assigns a new ID.
    • iota

      @Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class) public int iota()
    • initializeStorage

      protected void initializeStorage() throws IOException
      Throws:
      IOException
    • canResume

      public boolean canResume()
      If true, we are allowed to resume the build because resume is enabled AND we shut down cleanly.
    • onLoad

      public void onLoad(org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner) throws IOException
      Overrides:
      onLoad in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      Throws:
      IOException
    • loadProgramAsync

      public void loadProgramAsync(File programDataFile)
      Deserializes CpsThreadGroup from getProgramDataFile() if necessary. This moves us into the PREPARING state.
      Parameters:
      programDataFile -
    • afterStepExecutionsResumed

      protected void afterStepExecutionsResumed()
      Overrides:
      afterStepExecutionsResumed in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • blocksRestart

      public boolean blocksRestart()
      See JENKINS-22941 for why this exists.
      Overrides:
      blocksRestart in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • waitForSuspension

      @Deprecated public void waitForSuspension() throws InterruptedException, ExecutionException
      Deprecated.
      Use some other idiom, like SemaphoreStep.
      Waits for the workflow to move into the SUSPENDED state.
      Throws:
      InterruptedException
      ExecutionException
    • getFlowHead

      @CheckForNull public org.jenkinsci.plugins.workflow.cps.FlowHead getFlowHead(int id)
    • getCurrentHeads

      public List<org.jenkinsci.plugins.workflow.graph.FlowNode> getCurrentHeads()
      Specified by:
      getCurrentHeads in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • getCurrentExecutions

      public com.google.common.util.concurrent.ListenableFuture<List<org.jenkinsci.plugins.workflow.steps.StepExecution>> getCurrentExecutions(boolean innerMostOnly)
      Overrides:
      getCurrentExecutions in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • getThreadDump

      public CpsThreadDump getThreadDump()
      Synchronously obtain the current state of the workflow program.

      The workflow can be already completed, or it can still be running.

    • isCurrentHead

      public boolean isCurrentHead(org.jenkinsci.plugins.workflow.graph.FlowNode n)
      Specified by:
      isCurrentHead in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • addListener

      public void addListener(org.jenkinsci.plugins.workflow.flow.GraphListener listener)
      Specified by:
      addListener in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • removeListener

      public void removeListener(org.jenkinsci.plugins.workflow.flow.GraphListener listener)
      Overrides:
      removeListener in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • interrupt

      public void interrupt(Result result, CauseOfInterruption... causes) throws IOException, InterruptedException
      Specified by:
      interrupt in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      Throws:
      IOException
      InterruptedException
    • getNode

      public org.jenkinsci.plugins.workflow.graph.FlowNode getNode(String id) throws IOException
      Specified by:
      getNode in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      Throws:
      IOException
    • setResult

      public void setResult(Result v)
    • getResult

      public Result getResult()
    • loadActions

      public List<Action> loadActions(org.jenkinsci.plugins.workflow.graph.FlowNode node) throws IOException
      Specified by:
      loadActions in interface org.jenkinsci.plugins.workflow.graph.FlowActionStorage
      Throws:
      IOException
    • saveActions

      public void saveActions(org.jenkinsci.plugins.workflow.graph.FlowNode node, List<Action> actions) throws IOException
      Specified by:
      saveActions in interface org.jenkinsci.plugins.workflow.graph.FlowActionStorage
      Throws:
      IOException
    • maybeAutoPersistNode

      public static void maybeAutoPersistNode(@NonNull org.jenkinsci.plugins.workflow.graph.FlowNode node)
      Invoke me to toggle autopersist back on for steps that delay it.
    • isComplete

      public boolean isComplete()
      Overrides:
      isComplete in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • getAuthentication

      public Authentication getAuthentication()
      Specified by:
      getAuthentication in class org.jenkinsci.plugins.workflow.flow.FlowExecution
    • getNextScriptName

      @Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class) public String getNextScriptName(String path)
      Finds the expected next loaded script name, like Script1.
      Parameters:
      path - a file path being loaded (currently ignored)
    • isDoneFlagSet

      public boolean isDoneFlagSet()
      Has the execution been marked done - note that legacy builds may not have that flag persisted, in which case we look for a single FlowEndNode head (see: isComplete() and FlowExecution.isComplete())
    • isPaused

      public boolean isPaused()
    • pause

      public void pause(boolean v) throws IOException
      Pause or unpause the execution.
      Parameters:
      v - true to pause, false to unpause.
      Throws:
      IOException
    • toString

      public String toString()
      Overrides:
      toString in class Object
    • suspendAll

      @Restricted(org.kohsuke.accmod.restrictions.DoNotUse.class) @Terminator(attains="FlowExecutionList.EXECUTIONS_SUSPENDED") public static void suspendAll()