async Task <object> ContinueState(SuspendState state, WorkflowInstanceState workflowstate, WorkableLogger tasklogger, IDictionary <string, object> variables, CancellationToken token) { object lastresult = null; if (state.Subflow != null) { WorkflowIdentifier workflow = workflowstate.Workflow; workflowstate.Workflow = state.Subflow.Workflow; lastresult = await ContinueState(state.Subflow, workflowstate, tasklogger, variables, token); workflowstate.Workflow = workflow; } else { if (variables != null) { foreach (KeyValuePair <string, object> entry in variables) { state.Variables[entry.Key] = entry.Value.DetermineValue(state.Variables); } } } InstanceTransition transition = await EvaluateTransitions(state.Node, tasklogger, state.Variables, state.Node.Transitions, token); if (transition == null) { tasklogger.Warning("Suspend node has no transition defined for current state. Workflow ends by default."); return(lastresult); } return(await Execute(workflowstate, token, transition.Target, lastresult)); }
private static SuspendState SuspendImpl(ICollection<ISuspendibleRegisteredObject> allRegisteredObjects) { // Our behavior is: // - We'll call each registered object's suspend method serially. // - All methods have a combined 5 seconds to respond, at which // point we'll forcibly return to our caller. // - If a Resume call comes in, we'll not call any Suspend methods // we haven't yet gotten around to, and we'll execute each // resume callback we got. // - Resume callbacks may fire in parallel, even if Suspend methods // fire sequentially. // - Resume methods fire asynchronously, so other events (such as // Stop or a new Suspend call) could happen while a Resume callback // is in progress. CountdownEvent countdownEvent = new CountdownEvent(2); SuspendState suspendState = new SuspendState(allRegisteredObjects); // Unsafe QUWI since occurs outside the context of a request. // We are not concerned about impersonation, identity, etc. // Invoke any registered subscribers to let them know that we're about // to suspend. This is done in parallel with ASP.NET's own cleanup below. if (allRegisteredObjects.Count > 0) { ThreadPool.UnsafeQueueUserWorkItem(_ => { suspendState.Suspend(); countdownEvent.Signal(); }, null); } else { countdownEvent.Signal(); // nobody is subscribed } // Release any unnecessary memory that we're holding on to. The GC will // be able to reclaim these, which means that we'll have to page in less // memory when the next request comes in. ThreadPool.UnsafeQueueUserWorkItem(_ => { // Release any char[] buffers we're keeping around HttpWriter.ReleaseAllPooledBuffers(); // Trim expired entries from the runtime cache var cache = HttpRuntime.GetCacheInternal(createIfDoesNotExist: false); if (cache != null) { cache.TrimCache(0); } // Trim all pooled HttpApplication instances HttpApplicationFactory.TrimApplicationInstances(removeAll: true); countdownEvent.Signal(); }, null); if (Debug.IsDebuggerPresent()) { countdownEvent.Wait(); // to assist with debugging, don't time out if a debugger is attached } else { countdownEvent.Wait(_suspendMethodTimeout); // blocking call, ok for our needs since has finite wait time } return suspendState; }
/// <inheritdoc /> public override Task <object> Execute(WorkflowInstanceState state, CancellationToken token) { if (!string.IsNullOrEmpty(Parameters.Variable)) { state.Variables[Parameters.Variable] = null; } SuspendState suspendstate = new SuspendState(state.Workflow, this, state.Variables, state.Language, state.Profiling); return(Task.FromResult((object)suspendstate)); }
/// <inheritdoc /> public override async Task <object> Execute(WorkflowInstanceState state, CancellationToken token) { WorkflowInstance instance = await state.GetWorkflow(parameters.Name); WorkflowIdentifier parent = state.Workflow; state.Workflow = new WorkflowIdentifier(instance.Id, instance.Revision, instance.Name); object result = await state.WorkflowExecutor.Execute(instance, state.Logger, await Arguments.EvaluateArguments(state.Variables, token), state.Profiling, token); state.Workflow = parent; if (result is SuspendState suspend) { result = new SuspendState(state.Workflow, this, state.Variables, state.Language, state.Profiling, suspend); } return(result); }
protected override void CreateStates() { StatePreRun = new PreRunState(this); StateRun = new RunState(this); StatePreStop = new PreStopState(this); StateStop = new StopState(this); StatePreInitialize = new PreInitializeState(this); StateInitialize = new InitializeState(this); StateRundown = new RundownState(this); StatePreEmergency = new PreEmergencyState(this); StateEmergency = new EmergencyState(this); StateEmergencyReset = new EmergencyResetState(this); StatePreAlarm = new PreAlarmState(this); StateAlarm = new AlarmState(this); StatePreWarning = new PreWarningState(this); StateWarning = new WarningState(this); StatePreSuspend = new PreSuspendState(this); StateSuspend = new SuspendState(this); }
private static SuspendState SuspendImpl(ICollection <ISuspendibleRegisteredObject> allRegisteredObjects) { // Our behavior is: // - We'll call each registered object's suspend method serially. // - All methods have a combined 5 seconds to respond, at which // point we'll forcibly return to our caller. // - If a Resume call comes in, we'll not call any Suspend methods // we haven't yet gotten around to, and we'll execute each // resume callback we got. // - Resume callbacks may fire in parallel, even if Suspend methods // fire sequentially. // - Resume methods fire asynchronously, so other events (such as // Stop or a new Suspend call) could happen while a Resume callback // is in progress. CountdownEvent countdownEvent = new CountdownEvent(2); SuspendState suspendState = new SuspendState(allRegisteredObjects); // Unsafe QUWI since occurs outside the context of a request. // We are not concerned about impersonation, identity, etc. // Invoke any registered subscribers to let them know that we're about // to suspend. This is done in parallel with ASP.NET's own cleanup below. if (allRegisteredObjects.Count > 0) { ThreadPool.UnsafeQueueUserWorkItem(_ => { suspendState.Suspend(); countdownEvent.Signal(); }, null); } else { countdownEvent.Signal(); // nobody is subscribed } // Release any unnecessary memory that we're holding on to. The GC will // be able to reclaim these, which means that we'll have to page in less // memory when the next request comes in. ThreadPool.UnsafeQueueUserWorkItem(_ => { // Release any char[] buffers we're keeping around HttpWriter.ReleaseAllPooledBuffers(); // Trim expired entries from the runtime cache var cache = HttpRuntime.GetCacheInternal(createIfDoesNotExist: false); if (cache != null) { cache.TrimCache(0); } // Trim all pooled HttpApplication instances HttpApplicationFactory.TrimApplicationInstances(removeAll: true); countdownEvent.Signal(); }, null); if (Debug.IsDebuggerPresent()) { countdownEvent.Wait(); // to assist with debugging, don't time out if a debugger is attached } else { countdownEvent.Wait(_suspendMethodTimeout); // blocking call, ok for our needs since has finite wait time } return(suspendState); }