1 #pragma warning disable 1634, 1691
3 using System.Globalization;
4 using System.Collections;
5 using System.Collections.Generic;
6 using System.Collections.Specialized;
7 using System.Collections.ObjectModel;
8 using System.ComponentModel.Design.Serialization;
9 using System.Diagnostics;
11 using System.Reflection;
13 using System.Threading;
15 using System.Transactions;
16 using SES = System.EnterpriseServices;
17 using System.Workflow.ComponentModel;
18 using System.Workflow.Runtime.Hosting;
19 using System.Workflow.Runtime.DebugEngine;
21 namespace System.Workflow.Runtime
24 /// The runtime object that represents the schedule.
26 internal sealed class WorkflowExecutor : IWorkflowCoreRuntime, IServiceProvider, ISupportInterop
28 internal readonly static DependencyProperty WorkflowExecutorProperty = DependencyProperty.RegisterAttached("WorkflowExecutor", typeof(IWorkflowCoreRuntime), typeof(WorkflowExecutor), new PropertyMetadata(DependencyPropertyOptions.NonSerialized));
29 // The static method GetTransientBatch is used by this property to retrieve the WorkBatch.
30 // GetTransientBatch is defined in this class but if the workflow is running under a V2.0 Interop environment,
31 // it forwards the call to the Interop activity.
32 internal readonly static DependencyProperty TransientBatchProperty = DependencyProperty.RegisterAttached("TransientBatch", typeof(IWorkBatch), typeof(WorkflowExecutor), new PropertyMetadata(null, DependencyPropertyOptions.NonSerialized, new GetValueOverride(GetTransientBatch), null));
33 internal readonly static DependencyProperty TransactionalPropertiesProperty = DependencyProperty.RegisterAttached("TransactionalProperties", typeof(TransactionalProperties), typeof(WorkflowExecutor), new PropertyMetadata(DependencyPropertyOptions.NonSerialized));
34 internal readonly static DependencyProperty WorkflowInstanceIdProperty = DependencyProperty.RegisterAttached("WorkflowInstanceId", typeof(Guid), typeof(WorkflowExecutor), new PropertyMetadata(Guid.NewGuid()));
35 internal readonly static DependencyProperty IsBlockedProperty = DependencyProperty.RegisterAttached("IsBlocked", typeof(bool), typeof(WorkflowExecutor), new PropertyMetadata(false));
36 internal readonly static DependencyProperty WorkflowStatusProperty = DependencyProperty.RegisterAttached("WorkflowStatus", typeof(WorkflowStatus), typeof(WorkflowExecutor), new PropertyMetadata(WorkflowStatus.Created));
37 internal readonly static DependencyProperty SuspendOrTerminateInfoProperty = DependencyProperty.RegisterAttached("SuspendOrTerminateInfo", typeof(string), typeof(WorkflowExecutor));
39 // Persisted state properties
40 private static DependencyProperty ContextIdProperty = DependencyProperty.RegisterAttached("ContextId", typeof(int), typeof(WorkflowExecutor), new PropertyMetadata(new Int32()));
41 private static DependencyProperty TrackingCallingStateProperty = DependencyProperty.RegisterAttached("TrackingCallingState", typeof(TrackingCallingState), typeof(WorkflowExecutor));
42 internal static DependencyProperty TrackingListenerBrokerProperty = DependencyProperty.RegisterAttached("TrackingListenerBroker", typeof(TrackingListenerBroker), typeof(WorkflowExecutor));
43 private static DependencyProperty IsSuspensionRequestedProperty = DependencyProperty.RegisterAttached("IsSuspensionRequested", typeof(bool), typeof(WorkflowExecutor), new PropertyMetadata(false));
44 private static DependencyProperty IsIdleProperty = DependencyProperty.RegisterAttached("IsIdle", typeof(bool), typeof(WorkflowExecutor), new PropertyMetadata(false));
46 #region Data Members - Please keep all the data here
48 internal Activity currentAtomicActivity;
49 private ManualResetEvent atomicActivityEvent;
51 private Hashtable completedContextActivities = new Hashtable();
53 Activity rootActivity;
55 WorkflowRuntime _runtime; // hosting environment
57 private VolatileResourceManager _resourceManager = new VolatileResourceManager();
58 bool _isInstanceValid;
59 private bool isInstanceIdle;
60 Activity _lastExecutingActivity;
62 private Scheduler schedulingContext;
63 private WorkflowQueuingService qService;
65 private Exception thrownException;
66 private string activityThrowingException;
68 private List<SchedulerLockGuardInfo> eventsToFireList = new List<SchedulerLockGuardInfo>();
70 internal bool stateChangedSincePersistence;
72 private WorkflowInstance _workflowInstance;
73 private Guid workflowInstanceId;
74 private string workflowIdString = null;
76 WorkflowStateRollbackService workflowStateRollbackService;
78 private InstanceLock _executorLock;
79 private InstanceLock _msgDeliveryLock;
80 private InstanceLock _schedulerLock;
81 private TimerEventSubscriptionCollection _timerQueue;
82 private volatile Activity _workflowDefinition; // dependency property cache
84 private static BooleanSwitch disableWorkflowDebugging = new BooleanSwitch("DisableWorkflowDebugging", "Disables workflow debugging in host");
85 private static bool workflowDebuggingDisabled;
86 private WorkflowDebuggerService _workflowDebuggerService;
87 #endregion Data Members
91 static WorkflowExecutor()
93 // registered by workflow executor
94 DependencyProperty.RegisterAsKnown(ContextIdProperty, (byte)51, DependencyProperty.PropertyValidity.Reexecute);
95 DependencyProperty.RegisterAsKnown(IsSuspensionRequestedProperty, (byte)52, DependencyProperty.PropertyValidity.Uninitialize);
96 DependencyProperty.RegisterAsKnown(TrackingCallingStateProperty, (byte)53, DependencyProperty.PropertyValidity.Uninitialize);
97 DependencyProperty.RegisterAsKnown(TrackingListenerBrokerProperty, (byte)54, DependencyProperty.PropertyValidity.Uninitialize);
98 DependencyProperty.RegisterAsKnown(IsIdleProperty, (byte)56, DependencyProperty.PropertyValidity.Uninitialize);
100 // registered by Scheduler
101 DependencyProperty.RegisterAsKnown(Scheduler.NormalPriorityEntriesQueueProperty, (byte)61, DependencyProperty.PropertyValidity.Uninitialize);
102 DependencyProperty.RegisterAsKnown(Scheduler.HighPriorityEntriesQueueProperty, (byte)62, DependencyProperty.PropertyValidity.Uninitialize);
104 // registered by other services
105 DependencyProperty.RegisterAsKnown(WorkflowQueuingService.LocalPersistedQueueStatesProperty, (byte)63, DependencyProperty.PropertyValidity.Reexecute);
106 DependencyProperty.RegisterAsKnown(WorkflowQueuingService.RootPersistedQueueStatesProperty, (byte)64, DependencyProperty.PropertyValidity.Reexecute);
107 DependencyProperty.RegisterAsKnown(CorrelationTokenCollection.CorrelationTokenCollectionProperty, (byte)65, DependencyProperty.PropertyValidity.Always);
108 DependencyProperty.RegisterAsKnown(CorrelationToken.NameProperty, (byte)67, DependencyProperty.PropertyValidity.Uninitialize);
109 DependencyProperty.RegisterAsKnown(CorrelationToken.OwnerActivityNameProperty, (byte)68, DependencyProperty.PropertyValidity.Uninitialize);
110 DependencyProperty.RegisterAsKnown(CorrelationToken.PropertiesProperty, (byte)69, DependencyProperty.PropertyValidity.Uninitialize);
111 DependencyProperty.RegisterAsKnown(CorrelationToken.SubscriptionsProperty, (byte)70, DependencyProperty.PropertyValidity.Uninitialize);
112 DependencyProperty.RegisterAsKnown(CorrelationToken.InitializedProperty, (byte)71, DependencyProperty.PropertyValidity.Uninitialize);
114 //registered by the definition dispenser
115 DependencyProperty.RegisterAsKnown(WorkflowDefinitionDispenser.WorkflowDefinitionHashCodeProperty, (byte)80, DependencyProperty.PropertyValidity.Reexecute);
118 // registered by workflow instance
119 DependencyProperty.RegisterAsKnown(WorkflowInstanceIdProperty, (byte)102, DependencyProperty.PropertyValidity.Reexecute);
120 DependencyProperty.RegisterAsKnown(IsBlockedProperty, (byte)103, DependencyProperty.PropertyValidity.Reexecute);
121 DependencyProperty.RegisterAsKnown(WorkflowStatusProperty, (byte)104, DependencyProperty.PropertyValidity.Reexecute);
122 DependencyProperty.RegisterAsKnown(SuspendOrTerminateInfoProperty, (byte)105, DependencyProperty.PropertyValidity.Reexecute);
124 workflowDebuggingDisabled = disableWorkflowDebugging.Enabled;
127 internal WorkflowExecutor(Guid instanceId)
129 this._isInstanceValid = false;
130 this._executorLock = LockFactory.CreateWorkflowExecutorLock(instanceId);
131 this._msgDeliveryLock = LockFactory.CreateWorkflowMessageDeliveryLock(instanceId);
132 this.stateChangedSincePersistence = true;
134 // If DisableWorkflowDebugging switch is turned off create WorkflowDebuggerService
135 if (!workflowDebuggingDisabled)
136 this._workflowDebuggerService = new WorkflowDebuggerService(this);
139 // Initialize for the root schedule
140 internal void Initialize(Activity rootActivity, WorkflowExecutor invokerExec, string invokeActivityID, Guid instanceId, IDictionary<string, object> namedArguments, WorkflowInstance workflowInstance)
142 this.rootActivity = rootActivity;
143 this.InstanceId = instanceId;
145 // Set the persisted State properties
146 this.rootActivity.SetValue(WorkflowExecutor.ContextIdProperty, 0);
147 this.rootActivity.SetValue(WorkflowInstanceIdProperty, instanceId);
148 this.WorkflowStatus = WorkflowStatus.Created;
149 this.rootActivity.SetValue(Activity.ActivityExecutionContextInfoProperty, new ActivityExecutionContextInfo(this.rootActivity.QualifiedName, GetNewContextId(), instanceId, -1));
150 this.rootActivity.SetValue(Activity.ActivityContextGuidProperty, instanceId);
151 this.rootActivity.SetValue(WorkflowExecutor.IsIdleProperty, true);
152 this.isInstanceIdle = true;
154 // set workflow executor
155 this.rootActivity.SetValue(WorkflowExecutor.WorkflowExecutorProperty, this);
157 // initialize the root activity
158 RefreshWorkflowDefinition();
159 Activity workflowDefinition = this.WorkflowDefinition;
160 if (workflowDefinition == null)
161 throw new InvalidOperationException("workflowDefinition");
163 ((IDependencyObjectAccessor)this.rootActivity).InitializeActivatingInstanceForRuntime(null, this);
164 this.rootActivity.FixUpMetaProperties(workflowDefinition);
165 _runtime = workflowInstance.WorkflowRuntime;
167 if (invokerExec != null)
169 List<string> calleeBase = new List<string>();
170 TrackingCallingState parentTCS = (TrackingCallingState)invokerExec.rootActivity.GetValue(WorkflowExecutor.TrackingCallingStateProperty);
171 if ((parentTCS != null) && (parentTCS.CallerActivityPathProxy != null))
173 foreach (string qualifiedID in parentTCS.CallerActivityPathProxy)
174 calleeBase.Add(qualifiedID);
176 calleeBase.Add(invokeActivityID);
179 // This has been exec'd by another instance
180 // Set up tracking info to allow linking instances
181 Debug.Assert(invokeActivityID != null && invokeActivityID.Length > 0);
182 TrackingCallingState trackingCallingState = new TrackingCallingState();
183 trackingCallingState.CallerActivityPathProxy = calleeBase;
184 trackingCallingState.CallerWorkflowInstanceId = invokerExec.InstanceId;
185 trackingCallingState.CallerContextGuid = ((ActivityExecutionContextInfo)ContextActivityUtils.ContextActivity(invokerExec.CurrentActivity).GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid;
186 if (null == invokerExec.CurrentActivity.Parent)
187 trackingCallingState.CallerParentContextGuid = trackingCallingState.CallerContextGuid;
189 trackingCallingState.CallerParentContextGuid = ((ActivityExecutionContextInfo)ContextActivityUtils.ContextActivity(invokerExec.CurrentActivity.Parent).GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid;
190 this.rootActivity.SetValue(WorkflowExecutor.TrackingCallingStateProperty, trackingCallingState);
193 _setInArgsOnCompanion(namedArguments);
195 this.schedulingContext = new Scheduler(this, true);
196 this._schedulerLock = LockFactory.CreateWorkflowSchedulerLock(this.InstanceId);
198 qService = new WorkflowQueuingService(this);
200 _workflowInstance = workflowInstance;
202 TimerQueue = new TimerEventSubscriptionCollection(this, this.InstanceId);
204 // register the dynamic activity
205 using (new ServiceEnvironment(this.rootActivity))
207 using (SetCurrentActivity(this.rootActivity))
209 this.RegisterDynamicActivity(this.rootActivity, false);
214 internal void RegisterWithRuntime(WorkflowRuntime workflowRuntime)
216 _isInstanceValid = true;
217 _runtime = workflowRuntime;
218 using (new ServiceEnvironment(this.rootActivity))
220 using (SetCurrentActivity(this.rootActivity))
222 using (ActivityExecutionContext executionContext = new ActivityExecutionContext(this.rootActivity, true))
223 executionContext.InitializeActivity(this.rootActivity);
227 // Tell the runtime that the instance is ready
228 // so that internal components can set up event subscriptions
229 this._runtime.WorkflowExecutorCreated(this, false);
233 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Creating);
237 // Used to recreate the root schedule executor from its persisted state
238 internal void Reload(Activity rootActivity, WorkflowInstance workflowInstance)
240 _workflowInstance = workflowInstance;
241 ReloadHelper(rootActivity);
244 internal void ReRegisterWithRuntime(WorkflowRuntime workflowRuntime)
246 using (new SchedulerLockGuard(this._schedulerLock, this))
248 _isInstanceValid = true;
249 _runtime = workflowRuntime;
250 using (new ServiceEnvironment(this.rootActivity))
252 this._runtime.WorkflowExecutorCreated(this, true);
254 TimerQueue.Executor = this;
255 TimerQueue.ResumeDelivery();
257 // This will get the instance running so do it last otherwise we can end up
258 // with ----s between the running workflow and deliverying timers, etc.
259 if (this.WorkflowStatus == WorkflowStatus.Running)
260 this.Scheduler.CanRun = true;
262 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Loading);
267 internal void Registered(bool isActivation)
269 using (ScheduleWork work = new ScheduleWork(this))
271 this.Scheduler.ResumeIfRunnable();
274 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Created);
276 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Loaded);
279 // Used when replacing a workflow executor. Basically we move
280 // the locks from the previous executor so we guarantee that
281 // everything stays locks as it is supposed to be.
282 internal void Initialize(Activity rootActivity, WorkflowRuntime runtime, WorkflowExecutor previousWorkflowExecutor)
284 _workflowInstance = previousWorkflowExecutor.WorkflowInstance;
285 ReloadHelper(rootActivity);
286 // mark instance as valid now
287 IsInstanceValid = true;
289 this._runtime.WorkflowExecutorCreated(this, true);
291 TimerQueue.Executor = this;
292 TimerQueue.ResumeDelivery();
294 _executorLock = previousWorkflowExecutor._executorLock;
295 _msgDeliveryLock = previousWorkflowExecutor._msgDeliveryLock;
296 _schedulerLock = previousWorkflowExecutor._schedulerLock;
297 ScheduleWork.Executor = this;
300 // Used to recreate the root schedule executor from its persisted state
301 private void ReloadHelper(Activity rootActivity)
303 // assign activity state
304 this.rootActivity = rootActivity;
305 this.InstanceId = (Guid)rootActivity.GetValue(WorkflowInstanceIdProperty);
307 // set workflow executor
308 this.rootActivity.SetValue(WorkflowExecutor.WorkflowExecutorProperty, this);
310 this._schedulerLock = LockFactory.CreateWorkflowSchedulerLock(this.InstanceId);
312 this.schedulingContext = new Scheduler(this, false);
313 this.qService = new WorkflowQueuingService(this);
315 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Loading instance {0}", this.InstanceIdString);
316 DiagnosticStackTrace("load request");
318 using (new ServiceEnvironment(this.rootActivity))
321 // check if this instance can be loaded
322 switch (this.WorkflowStatus)
324 case WorkflowStatus.Completed:
325 case WorkflowStatus.Terminated:
326 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: attempt to load a completed/terminated instance: {0}", this.InstanceIdString);
327 throw new InvalidOperationException(
328 ExecutionStringManager.InvalidAttemptToLoad);
334 // new nonSerialized members
335 _resourceManager = new VolatileResourceManager();
336 _runtime = _workflowInstance.WorkflowRuntime;
338 // register all dynamic activities for loading
339 Queue<Activity> dynamicActivitiesQueue = new Queue<Activity>();
340 dynamicActivitiesQueue.Enqueue(this.rootActivity);
341 while (dynamicActivitiesQueue.Count > 0)
343 Activity dynamicActivity = dynamicActivitiesQueue.Dequeue();
344 ((IDependencyObjectAccessor)dynamicActivity).InitializeInstanceForRuntime(this);
345 this.RegisterDynamicActivity(dynamicActivity, true);
347 IList<Activity> nestedDynamicActivities = (IList<Activity>)dynamicActivity.GetValue(Activity.ActiveExecutionContextsProperty);
348 if (nestedDynamicActivities != null)
350 foreach (Activity nestedDynamicActivity in nestedDynamicActivities)
351 dynamicActivitiesQueue.Enqueue(nestedDynamicActivity);
356 this.isInstanceIdle = (bool)this.rootActivity.GetValue(IsIdleProperty);
357 RefreshWorkflowDefinition();
360 private void _setInArgsOnCompanion(IDictionary<string, object> namedInArguments)
362 // Do parameter property assignments.
363 if (namedInArguments != null)
365 foreach (string arg in namedInArguments.Keys)
368 PropertyInfo propertyInfo = this.WorkflowDefinition.GetType().GetProperty(arg);
370 if (propertyInfo != null && propertyInfo.CanWrite)
374 propertyInfo.SetValue(this.rootActivity, namedInArguments[arg], null);
376 catch (ArgumentException e)
378 throw new ArgumentException(ExecutionStringManager.InvalidWorkflowParameterValue, arg, e);
382 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.SemanticErrorInvalidNamedParameter, ((Activity)this.WorkflowDefinition).Name, arg));
388 #region Misc properties and methods
390 internal TrackingCallingState TrackingCallingState
394 return (TrackingCallingState)this.rootActivity.GetValue(WorkflowExecutor.TrackingCallingStateProperty);
398 internal WorkflowRuntime WorkflowRuntime
405 internal bool IsInstanceValid
407 get { return _isInstanceValid; }
412 this.ResourceManager.ClearAllBatchedWork();
413 InstanceLock.AssertIsLocked(this._schedulerLock);
414 InstanceLock.AssertIsLocked(this._msgDeliveryLock);
416 _isInstanceValid = value;
424 return this.isInstanceIdle;
428 using (InstanceLock.InstanceLockGuard messageDeliveryLockGuard = this.MessageDeliveryLock.Enter())
432 this.isInstanceIdle = value;
433 this.RootActivity.SetValue(WorkflowExecutor.IsIdleProperty, value);
437 // Playing it safe here. If the try block throws,
438 // we test what was the resulting value of the
439 // property to see if we need to signal the
442 messageDeliveryLockGuard.Pulse();
448 internal string AdditionalInformation
450 get { return (string)this.rootActivity.GetValue(SuspendOrTerminateInfoProperty); }
453 public WorkBatchCollection BatchCollection
457 return _resourceManager.BatchCollection;
461 internal VolatileResourceManager ResourceManager
465 return _resourceManager;
469 internal Activity WorkflowDefinition
473 Debug.Assert(_workflowDefinition != null, "WorkflowDefinition cannot be null.");
474 return _workflowDefinition;
478 private void RefreshWorkflowDefinition()
480 Activity tempDefinition = (Activity)this.rootActivity.GetValue(Activity.WorkflowDefinitionProperty);
481 Debug.Assert(tempDefinition != null, "WorkflowDefinition cannot be null.");
483 // Workflow definitions needs to have a locking object
484 // on them for use when cloning for public consumption
485 // (WorkflowInstance.GetWorkflowDefinition and
486 // WorkflowCompletedEventArgs.WorkflowDefinition).
487 WorkflowDefinitionLock.SetWorkflowDefinitionLockObject(tempDefinition, new object());
489 _workflowDefinition = tempDefinition;
492 internal Activity RootActivity
496 return this.rootActivity;
500 internal Guid InstanceId
504 return workflowInstanceId;
508 workflowInstanceId = value;
512 internal string InstanceIdString
516 if (workflowIdString == null)
517 workflowIdString = this.InstanceId.ToString();
518 return workflowIdString;
523 internal InstanceLock MessageDeliveryLock
527 return _msgDeliveryLock;
531 internal InstanceLock ExecutorLock
535 return _executorLock;
539 internal WorkflowStateRollbackService WorkflowStateRollbackService
543 if (this.workflowStateRollbackService == null)
544 this.workflowStateRollbackService = new WorkflowStateRollbackService(this);
545 return this.workflowStateRollbackService;
549 internal WorkflowInstance WorkflowInstance
553 System.Diagnostics.Debug.Assert(this._workflowInstance != null, "WorkflowInstance property should not be called before the proxy is initialized.");
554 return this._workflowInstance;
558 internal void Start()
560 using (ScheduleWork work = new ScheduleWork(this))
562 using (this.ExecutorLock.Enter())
564 if (this.WorkflowStatus != WorkflowStatus.Created)
565 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.CannotStartInstanceTwice, this.InstanceId));
567 // Set a new ServiceEnvironment to establish a current batch in TLS
568 // This is needed for synchronous status change notification at start
569 // (status init->executing) when there is no batch in TLS yet
570 // and there are subscribers like tracking
571 this.WorkflowStatus = WorkflowStatus.Running;
572 using (new ServiceEnvironment(this.rootActivity))
574 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Starting);
577 using (ActivityExecutionContext executionContext = new ActivityExecutionContext(this.rootActivity, true))
579 // make sure the scheduler is able to run
580 this.schedulingContext.CanRun = true;
582 // Since we are actually scheduling work at this point, we should grab
583 // the scheduler lock. This will avoid ----s some operations we schedule
584 // start executing before we are done scheduling all operations.
585 using (new SchedulerLockGuard(this._schedulerLock, this))
587 executionContext.ExecuteActivity(this.rootActivity);
593 Terminate(e.Message);
596 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Started);
603 internal Activity CurrentActivity
605 get { return _lastExecutingActivity; }
606 set { _lastExecutingActivity = value; }
609 internal Hashtable CompletedContextActivities
611 get { return this.completedContextActivities; }
612 set { this.completedContextActivities = value; }
616 private int GetNewContextId()
618 int conextId = (int)this.rootActivity.GetValue(WorkflowExecutor.ContextIdProperty) + 1;
619 this.rootActivity.SetValue(WorkflowExecutor.ContextIdProperty, conextId);
623 internal List<SchedulerLockGuardInfo> EventsToFireList
627 return eventsToFireList;
631 private void FireEventAfterSchedulerLockDrop(WorkflowEventInternal workflowEventInternal, object eventInfo)
633 eventsToFireList.Add(new SchedulerLockGuardInfo(this, workflowEventInternal, eventInfo));
636 private void FireEventAfterSchedulerLockDrop(WorkflowEventInternal workflowEventInternal)
638 eventsToFireList.Add(new SchedulerLockGuardInfo(this, workflowEventInternal));
641 #endregion Misc properties and methods
643 #region Scheduler Related
645 // asks the hosting env threadProvider for a thread
646 internal void ScheduleForWork()
650 if (this.IsInstanceValid)
651 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Runnable);
653 ScheduleWork.NeedsService = true;
656 internal void RequestHostingService()
658 WorkflowRuntime.SchedulerService.Schedule(this.RunSome, this.InstanceId);
661 internal void DeliverTimerSubscriptions()
663 using (ScheduleWork work = new ScheduleWork(this))
665 using (this._executorLock.Enter())
667 if (this.IsInstanceValid)
669 using (this.MessageDeliveryLock.Enter())
671 using (new ServiceEnvironment(this.rootActivity))
673 if (!this.IsInstanceValid)
676 TimerEventSubscriptionCollection queue = TimerQueue;
680 lock (queue.SyncRoot)
682 TimerEventSubscription sub = queue.Peek();
683 if (sub == null || sub.ExpiresAt > DateTime.UtcNow)
689 WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Delivering timer subscription for instance {0}", this.InstanceIdString);
690 stateChangedSincePersistence = true;
691 lock (qService.SyncRoot)
693 if (qService.Exists(sub.QueueName))
695 qService.EnqueueEvent(sub.QueueName, sub.SubscriptionId);
709 // call from the threadProvider about the availability of a thread.
710 internal void RunSome(object ignored)
712 using (ScheduleWork work = new ScheduleWork(this))
714 using (new WorkflowTraceTransfer(this.InstanceId))
716 using (new SchedulerLockGuard(this._schedulerLock, this))
718 using (new ServiceEnvironment(this.rootActivity))
720 // check if this is a valid in-memory instance
721 if (!this.IsInstanceValid)
724 // check if instance already done
725 if ((this.rootActivity.ExecutionStatus == ActivityExecutionStatus.Closed) || (WorkflowStatus.Completed == this.WorkflowStatus) || (WorkflowStatus.Terminated == this.WorkflowStatus))
728 bool ignoreFinallyBlock = false;
731 // For V1 we don't support flow through transaction on the service thread
732 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Suppress))
736 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Executing);
737 // run away ... run away...
742 if (WorkflowExecutor.IsIrrecoverableException(e))
744 ignoreFinallyBlock = true;
749 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Fatal exception thrown in the scheduler. Terminating the workflow instance '{0}'. Exception:{1}\n{2}", this.InstanceIdString, e.Message, e.StackTrace);
750 this.TerminateOnIdle(WorkflowExecutor.GetNestedExceptionMessage(e));
751 this.ThrownException = e;
756 if (!ignoreFinallyBlock)
758 FireWorkflowExecutionEvent(this, WorkflowEventInternal.NotExecuting);
769 // this method is called with the scheduler lock held
770 private void RunScheduler()
772 InstanceLock.AssertIsLocked(this._schedulerLock);
774 // run away ... run away...
777 this.Scheduler.Run();
784 if (!this.IsInstanceValid)
787 if (this.WorkflowStateRollbackService.IsInstanceStateRevertRequested)
790 // Protect against message delivery while reverting
791 using (MessageDeliveryLock.Enter())
793 this.WorkflowStateRollbackService.RevertToCheckpointState();
798 if (this.Scheduler.IsStalledNow)
800 // the instance has no ready work
802 // Protect against the host accessing DPs.
803 using (this.MessageDeliveryLock.Enter())
805 if (this.rootActivity.ExecutionStatus != ActivityExecutionStatus.Closed)
807 this.ProcessQueuedEvents(); // deliver any outstanding queued events before persisting
808 if (this.Scheduler.IsStalledNow)
810 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: workflow instance '{0}' has no work.", this.InstanceIdString);
811 FireWorkflowExecutionEvent(this, WorkflowEventInternal.SchedulerEmpty);
813 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Idle);
815 WorkflowPersistenceService persistence = this.WorkflowRuntime.WorkflowPersistenceService;
817 // instance is not done.. must be idle
818 // can potentially dehydrate now..
819 if ((persistence != null) && persistence.UnloadOnIdle(this.rootActivity))
821 if (!this.IsInstanceValid)
824 // Do not unload if we are not unloadable and if a persistence exception
825 // was thrown the last time.
826 if (this.IsUnloadableNow && !(this.ThrownException is PersistenceException))
828 PerformUnloading(true);
829 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "WorkflowExecutor: unloaded workflow instance '{0}'. IsInstanceValid={1}", this.InstanceIdString, IsInstanceValid);
834 if (this.ResourceManager.IsBatchDirty && this.currentAtomicActivity == null)
836 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: workflow instance '{0}' has no work and the batch is dirty. Persisting state and commiting batch.", this.InstanceIdString);
837 this.Persist(this.rootActivity, false, false);
846 // the instance has ready work but was told to stop
849 // if suspension was requested, suspend now.
851 if ((bool)this.rootActivity.GetValue(WorkflowExecutor.IsSuspensionRequestedProperty))
853 this.SuspendOnIdle(this.AdditionalInformation);
854 this.rootActivity.SetValue(WorkflowExecutor.IsSuspensionRequestedProperty, false);
858 if (this.currentAtomicActivity != null)
860 // Leave TransactionScope before giving up the thread
861 TransactionalProperties transactionalProperties = (TransactionalProperties)this.currentAtomicActivity.GetValue(TransactionalPropertiesProperty);
862 DisposeTransactionScope(transactionalProperties);
866 private bool attemptedRootAECUnload = false;
867 private bool attemptedRootDispose = false;
869 private void DisposeRootActivity(bool aborting)
873 if (!attemptedRootAECUnload)
875 attemptedRootAECUnload = true;
876 this.RootActivity.OnActivityExecutionContextUnload(this);
878 if (!attemptedRootDispose)
880 attemptedRootDispose = true;
881 this.RootActivity.Dispose();
888 using (_msgDeliveryLock.Enter())
898 internal Scheduler Scheduler
902 return this.schedulingContext;
906 #endregion Scheduler Related
908 #region IInstanceState
916 get { return InstanceId; }
920 /// Completed status for instances clean up
923 internal WorkflowStatus WorkflowStatus
925 get { return (WorkflowStatus)this.rootActivity.GetValue(WorkflowStatusProperty); }
926 private set { this.rootActivity.SetValue(WorkflowStatusProperty, value); }
929 internal TimerEventSubscriptionCollection TimerQueue
933 if (_timerQueue == null)
935 _timerQueue = (TimerEventSubscriptionCollection)this.rootActivity.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty);
936 Debug.Assert(_timerQueue != null, "TimerEventSubscriptionCollection on root activity should never be null, but it was");
943 this.rootActivity.SetValue(TimerEventSubscriptionCollection.TimerCollectionProperty, _timerQueue);
952 private bool ProtectedPersist(bool unlock)
957 this.Persist(this.rootActivity, unlock, false);
961 if (WorkflowExecutor.IsIrrecoverableException(e))
964 } //@@undone: for [....]:- we should not be running exception handler, when we are unlocking.
965 else if (this.WorkflowStatus != WorkflowStatus.Suspended && this.IsInstanceValid)
967 // the persistence attempt threw an exception
968 // lets give this exception to a scope
969 Activity activity = FindExecutorToHandleException();
971 this.Scheduler.CanRun = true;
972 this.ExceptionOccured(e, activity, null);
976 if (this.TerminateOnIdle(WorkflowExecutor.GetNestedExceptionMessage(e)))
978 this.stateChangedSincePersistence = true;
979 this.WorkflowStatus = WorkflowStatus.Terminated;
987 private Activity FindExecutorToHandleException()
989 Activity lastExecutingActivity = this.CurrentActivity;
990 if (lastExecutingActivity == null)
991 lastExecutingActivity = this.rootActivity;
992 return lastExecutingActivity;
995 // called by core runtime to persist the instance.
996 // 'exec' is the executor requesting the persistence
997 internal void Persist(Activity dynamicActivity, bool unlock, bool needsCompensation)
999 InstanceLock.AssertIsLocked(this._schedulerLock);
1000 Activity currentActivity = (this.CurrentActivity == null) ? dynamicActivity : this.CurrentActivity;
1002 // Save the current status. The status may change in PrePersist
1003 // and we need to reset if the commit fails for any reason.
1004 WorkflowStatus oldStatus = this.WorkflowStatus;
1006 // New a ServiceEnvironment to set the current batch to be of the exec to be persisted
1007 using (new ServiceEnvironment(currentActivity))
1011 // prevent the message delivery from outside
1012 using (this.MessageDeliveryLock.Enter())
1014 this.ProcessQueuedEvents(); // Must always process this queue before persisting state!
1015 // check what has changed since last persist
1017 if (this.ResourceManager.IsBatchDirty)
1019 // if there is work in the batch, persist the state to be consistent
1020 this.stateChangedSincePersistence = true;
1024 // no work in the batch...
1025 if (!this.stateChangedSincePersistence && !unlock)
1027 // the instance state is not dirty
1028 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: NOT Persisting Instance '{0}' since the batch is NOT dirty and the instance state is NOT dirty", this.InstanceIdString);
1033 // prepare the state for persistence
1037 if (WorkflowStatus.Completed == WorkflowStatus)
1039 // Any remaining messages in queues are zombie messages so move all to the pending queue
1040 this.qService.MoveAllMessagesToPendingQueue();
1042 // give the state to the persistence provider
1043 WorkflowPersistenceService persistence = this.WorkflowRuntime.WorkflowPersistenceService;
1045 // Create a transient batch for Persistence Service.
1046 currentActivity.SetValue(TransientBatchProperty, _resourceManager.BatchCollection.GetTransientBatch());
1048 bool firedPersistingEvent = false;
1050 if (persistence != null)
1052 foreach (Activity completedContextActivity in this.completedContextActivities.Values)
1054 // Save the committing activity
1055 completedContextActivity.SetValue(WorkflowInstanceIdProperty, this.InstanceId);
1057 if (!firedPersistingEvent)
1059 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Persisting);
1060 firedPersistingEvent = true;
1063 persistence.SaveCompletedContextActivity(completedContextActivity);
1064 completedContextActivity.Dispose();
1067 if (this.stateChangedSincePersistence)
1069 if (!firedPersistingEvent)
1071 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Persisting);
1072 firedPersistingEvent = true;
1075 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Calling SaveWorkflowInstanceState for instance {0} hc {1}", this.InstanceIdString, this.GetHashCode());
1076 persistence.SaveWorkflowInstanceState(this.rootActivity, unlock);
1080 persistence.UnlockWorkflowInstanceState(this.rootActivity);
1086 DisposeRootActivity(false);
1090 // check batch again, since the persistence provider may have added something.
1091 // If we are unlocking (unloading/dehydrating) commit the batch
1092 // regardless of whether the batch items signal that they need a commit
1093 if (this.currentAtomicActivity != null || this.ResourceManager.IsBatchDirty || (unlock && HasNonEmptyWorkBatch()))
1096 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Calling CommitTransaction for instance {0} hc {1}", this.InstanceIdString, this.GetHashCode());
1097 this.CommitTransaction(currentActivity);
1100 if (firedPersistingEvent)
1101 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Persisted);
1105 this.stateChangedSincePersistence = false;
1108 // Must do this after all persist related work has successfully finished
1109 // If we weren't successful we aren't actually completed
1110 if (WorkflowStatus.Completed == WorkflowStatus)
1112 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Completed);
1113 this.IsInstanceValid = false;
1117 catch (PersistenceException e)
1119 this.Rollback(oldStatus);
1120 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Persist attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace);
1125 if (WorkflowExecutor.IsIrrecoverableException(e))
1129 this.Rollback(oldStatus);
1130 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Persist attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace);
1131 throw new PersistenceException(e.Message, e);
1135 //Flush the transient Batch
1136 currentActivity.SetValue(TransientBatchProperty, null);
1142 /// There is always at least 1 BatchCollection (at root),
1143 /// check if any batch contains any work item
1145 /// <returns></returns>
1146 private bool HasNonEmptyWorkBatch()
1148 foreach (WorkBatch workBatch in ResourceManager.BatchCollection.Values)
1150 if (workBatch.Count > 0)
1159 /// Signal to prepare the state for persistence.
1161 private void PrePersist()
1164 // This is our hook to set the workflowstatus to Completed
1165 // so that it is correctly written to persistence
1166 WorkflowStatus workflowStatus = this.WorkflowStatus;
1167 if ((ActivityExecutionStatus.Closed == this.rootActivity.ExecutionStatus) && (WorkflowStatus.Terminated != workflowStatus))
1169 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Completing);
1170 this.WorkflowStatus = WorkflowStatus.Completed;
1173 switch (this.WorkflowStatus)
1175 case WorkflowStatus.Running:
1176 this.rootActivity.SetValue(IsBlockedProperty, this.Scheduler.IsStalledNow);
1178 case WorkflowStatus.Suspended:
1179 case WorkflowStatus.Completed:
1180 case WorkflowStatus.Terminated:
1181 case WorkflowStatus.Created:
1182 this.rootActivity.SetValue(IsBlockedProperty, false);
1185 Debug.Assert(false, "Unknown WorkflowStatus");
1189 qService.PrePersist();
1192 private void PostPersist()
1194 qService.PostPersist(true);
1195 if (this.Scheduler != null)
1196 this.Scheduler.PostPersist();
1197 this.completedContextActivities.Clear();
1200 private void Rollback(WorkflowStatus oldStatus)
1202 this.WorkflowStatus = oldStatus;
1204 if (this.Scheduler != null)
1205 this.Scheduler.Rollback();
1210 #region MessageArrival and Query
1212 internal void ProcessQueuedEvents()
1214 using (MessageDeliveryLock.Enter())
1216 qService.ProcessesQueuedAsynchronousEvents();
1220 internal void EnqueueItem(IComparable queueName, object item, IPendingWork pendingWork, Object workItem)
1222 using (ScheduleWork work = new ScheduleWork(this))
1224 bool lockedScheduler = false;
1225 if (!ServiceEnvironment.IsInServiceThread(InstanceId))
1226 lockedScheduler = _schedulerLock.TryEnter();
1229 // take the msg delivery lock to make sure the instance
1230 // doesn't persist while the message is being delivered.
1231 using (this.MessageDeliveryLock.Enter())
1233 if (!this.IsInstanceValid)
1234 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1236 if (lockedScheduler || ServiceEnvironment.IsInServiceThread(InstanceId))
1238 using (new ServiceEnvironment(this.RootActivity))
1240 qService.EnqueueEvent(queueName, item);
1245 if (qService.SafeEnqueueEvent(queueName, item))
1247 ScheduleWork.NeedsService = true;
1251 // add work items to the current batch if exists
1252 if (pendingWork != null)
1254 IWorkBatch batch = _resourceManager.BatchCollection.GetBatch(this.rootActivity);
1255 batch.Add(pendingWork, workItem);
1258 stateChangedSincePersistence = true;
1263 if (lockedScheduler)
1264 _schedulerLock.Exit();
1269 internal void EnqueueItemOnIdle(IComparable queueName, object item, IPendingWork pendingWork, Object workItem)
1271 using (ScheduleWork work = new ScheduleWork(this))
1273 // prevent other control operations from outside
1274 using (this._executorLock.Enter())
1276 if (!this.IsInstanceValid)
1277 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1279 // take the msg delivery lock to make sure the instance
1280 // doesn't persist while the message is being delivered.
1281 using (InstanceLock.InstanceLockGuard messageDeliveryLockGuard = this.MessageDeliveryLock.Enter())
1283 using (new ServiceEnvironment(this.rootActivity))
1286 if (!this.IsInstanceValid)
1287 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1289 // Wait until the Scheduler is idle.
1290 while (!this.IsIdle)
1292 messageDeliveryLockGuard.Wait();
1293 if (!this.IsInstanceValid)
1294 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1297 // At this point the scheduler is not running and it is
1298 // EnqueueItemOnIdle is not valid for suspended workflows
1299 if ((this.WorkflowStatus == WorkflowStatus.Suspended) || (!this.Scheduler.CanRun))
1300 throw new InvalidOperationException(ExecutionStringManager.InvalidWaitForIdleOnSuspendedWorkflow);
1304 // add work items to the current batch if exists
1305 if (pendingWork != null)
1307 IWorkBatch batch = (IWorkBatch)this.rootActivity.GetValue(WorkflowExecutor.TransientBatchProperty);
1308 batch.Add(pendingWork, workItem);
1311 stateChangedSincePersistence = true;
1312 qService.EnqueueEvent(queueName, item);
1317 messageDeliveryLockGuard.Pulse();
1325 internal ReadOnlyCollection<WorkflowQueueInfo> GetWorkflowQueueInfos()
1327 List<WorkflowQueueInfo> queuedItems = new List<WorkflowQueueInfo>();
1328 // take the msg delivery lock to make sure the queues don't
1329 // change during the list assembly.
1330 using (this.MessageDeliveryLock.Enter())
1332 using (new ServiceEnvironment(this.rootActivity))
1334 lock (qService.SyncRoot)
1336 IEnumerable<IComparable> names = qService.QueueNames;
1337 foreach (IComparable name in names)
1341 WorkflowQueue queue = qService.GetWorkflowQueue(name);
1344 Queue items = qService.GetQueue(name).Messages;
1345 List<ActivityExecutorDelegateInfo<QueueEventArgs>> listeners = qService.GetQueue(name).AsynchronousListeners;
1346 List<string> subscribedActivities = new List<string>();
1347 foreach (ActivityExecutorDelegateInfo<QueueEventArgs> l in listeners)
1349 string activity = (l.SubscribedActivityQualifiedName == null) ? l.ActivityQualifiedName : l.SubscribedActivityQualifiedName;
1350 subscribedActivities.Add(activity);
1352 queuedItems.Add(new WorkflowQueueInfo(name, items, subscribedActivities.AsReadOnly()));
1354 catch (InvalidOperationException)
1356 // ignore this queue if it has disappeared
1362 return queuedItems.AsReadOnly();
1365 internal DateTime GetWorkflowNextTimerExpiration()
1367 using (this._executorLock.Enter())
1369 using (this.MessageDeliveryLock.Enter())
1371 TimerEventSubscriptionCollection timers = TimerQueue;
1372 TimerEventSubscription sub = timers.Peek();
1373 return sub == null ? DateTime.MaxValue : sub.ExpiresAt;
1378 #endregion MessageArrival and Query
1380 #region executor to execution context mappings
1382 //This list is populated at loading time.
1383 //a map of SubState Tracking Context - SubState.
1385 private Dictionary<int, Activity> subStateMap = new Dictionary<int, Activity>();
1387 internal void RegisterDynamicActivity(Activity dynamicActivity, bool load)
1389 int contextId = ContextActivityUtils.ContextId(dynamicActivity);
1390 this.subStateMap.Add(contextId, dynamicActivity);
1392 System.Workflow.Runtime.WorkflowTrace.Runtime.TraceEvent(
1393 TraceEventType.Information, 0, "Adding context {0}:{1}",
1394 contextId, dynamicActivity.QualifiedName + (load ? " for load" : ""));
1396 dynamicActivity.OnActivityExecutionContextLoad(this);
1399 internal void UnregisterDynamicActivity(Activity dynamicActivity)
1401 int contextId = ContextActivityUtils.ContextId(dynamicActivity);
1402 this.subStateMap.Remove(contextId);
1404 System.Workflow.Runtime.WorkflowTrace.Runtime.TraceEvent(
1405 TraceEventType.Information, 0, "Removing context {0}:{1}",
1406 contextId, dynamicActivity.QualifiedName);
1408 dynamicActivity.OnActivityExecutionContextUnload(this);
1411 internal Activity GetContextActivityForId(int stateId)
1413 if (this.subStateMap.ContainsKey(stateId))
1414 return this.subStateMap[stateId];
1421 // indicates whether an this schedule instance can be unloaded right now
1422 internal bool IsUnloadableNow
1424 // Called by hosting environment
1425 get { return ((this.currentAtomicActivity == null) && (this.Scheduler.IsStalledNow || this.WorkflowStatus == WorkflowStatus.Suspended)); }
1429 /// Synchronously unload if currently idle
1431 /// <returns>true if successful</returns>
1432 internal bool TryUnload()
1434 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a TryUnload request for instance {0}", this.InstanceIdString);
1435 DiagnosticStackTrace("try unload request");
1439 // check if this is a valid in-memory instance
1440 if (!this.IsInstanceValid)
1443 // check if there is a persistence service
1444 if (this.WorkflowRuntime.WorkflowPersistenceService == null)
1446 string errMsg = String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceService, this.InstanceId);
1447 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, errMsg);
1448 throw new InvalidOperationException(errMsg);
1450 using (new ScheduleWork(this, true))
1452 // Stop threads from outside - message delivery and control operations
1453 if (this._executorLock.TryEnter())
1457 // we need to take these locks to make sure that we have a fixed picture of the
1458 // unloadability state of the workflow.
1459 if (this._schedulerLock.TryEnter())
1463 if (this._msgDeliveryLock.TryEnter())
1465 using (new ServiceEnvironment(this.rootActivity))
1469 if (!this.IsInstanceValid)
1472 this.ProcessQueuedEvents(); // deliver any outstanding queued events before persisting
1473 if (this.IsUnloadableNow)
1476 return PerformUnloading(false);
1483 this._msgDeliveryLock.Exit();
1490 SchedulerLockGuard.Exit(this._schedulerLock, this);
1496 this._executorLock.Exit();
1503 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: TryUnloading attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace);
1509 // this unloads the instance by assuming that it can be unloaded.
1510 private bool PerformUnloading(bool handleExceptions)
1512 InstanceLock.AssertIsLocked(this._schedulerLock);
1514 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Unloading instance {0}", this.InstanceIdString);
1515 DiagnosticStackTrace("unload request");
1517 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Unloading);
1519 // Block message delivery for duration of persist and marking as invalid
1520 using (_msgDeliveryLock.Enter())
1522 TimerQueue.SuspendDelivery();
1525 if (handleExceptions)
1527 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Calling PerformUnloading(false): InstanceId {0}, hc: {1}", InstanceIdString, this.GetHashCode());
1528 persisted = this.ProtectedPersist(true);
1529 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Returning from ProtectedPersist: InstanceId {0}, hc: {1}, ret={2}", InstanceIdString, this.GetHashCode(), persisted);
1533 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Calling Persist");
1534 this.Persist(this.rootActivity, true, false);
1536 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Returning from Persist: InstanceId {0}, hc: {1}, IsInstanceValid={2}", InstanceIdString, this.GetHashCode(), IsInstanceValid);
1540 // mark instance as invalid
1541 this.IsInstanceValid = false;
1543 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Unloaded);
1551 // shutsdown the schedule instance [....]
1552 internal void Unload()
1554 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got an unload request for instance {0}", this.InstanceIdString);
1555 DiagnosticStackTrace("unload request");
1559 using (new ScheduleWork(this, true))
1561 // Stop threads from outside - message delivery and control operations
1562 using (this._executorLock.Enter())
1564 if (this.WorkflowRuntime.WorkflowPersistenceService == null)
1566 string errMsg = String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceService, this.InstanceId);
1567 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, errMsg);
1568 throw new InvalidOperationException(errMsg);
1571 // tell the scheduler to stop running
1572 this.Scheduler.CanRun = false;
1573 // If there were some thread executing the instance, then setting up
1574 // the callback, the thread getting done and the notification coming back
1575 // is racy... so we lock the scheduler
1576 using (new SchedulerLockGuard(this._schedulerLock, this))
1578 using (new ServiceEnvironment(this.rootActivity))
1580 // check if this is a valid in-memory instance
1581 if (!this.IsInstanceValid)
1583 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1586 // the scheduler must be idle now
1587 if (this.currentAtomicActivity == null)
1589 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Calling PerformUnloading(false) on instance {0} hc {1}", InstanceIdString, this.GetHashCode());
1591 PerformUnloading(false);
1592 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Returning from PerformUnloading(false): IsInstanceValue: " + IsInstanceValid);
1596 this.Scheduler.CanRun = true;
1597 throw new ExecutorLocksHeldException(atomicActivityEvent);
1606 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Unload attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace);
1615 // terminates the schedule instance [....]
1616 // must be called only from outside the instance... the thread running the instance must
1617 // never call this method... it should call TerminateOnIdle instead.
1618 internal void Terminate(string error)
1620 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor::Terminate : Got a terminate request for instance {0}", this.InstanceIdString);
1624 using (new ScheduleWork(this, true))
1626 // Stop threads from outside - message delivery and control operations
1627 using (this._executorLock.Enter())
1629 // tell the scheduler to stop returnig items from its queue (ref: 16534)
1630 this.Scheduler.AbortOrTerminateRequested = true;
1631 // tell the scheduler to stop running
1632 this.Scheduler.CanRun = false;
1634 // If there were some thread executing the instance, then setting up
1635 // the callback, the thread getting done and the notification coming back
1636 // is racy... so we lock the scheduler
1637 using (new SchedulerLockGuard(this._schedulerLock, this))
1639 using (new ServiceEnvironment(this.rootActivity))
1642 // check if this is a valid in-memory instance
1643 if (!this.IsInstanceValid)
1644 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1646 this.TerminateOnIdle(error);
1654 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Terminate attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace);
1659 // this method must be called with the scheduler lock held
1660 internal bool TerminateOnIdle(string error)
1662 InstanceLock.AssertIsLocked(this._schedulerLock);
1664 // check if the instance can be terminated
1665 if (!this.IsInstanceValid)
1668 // tell the scheduler to stop running
1669 this.Scheduler.CanRun = false;
1673 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Terminating instance {0}", this.InstanceIdString);
1675 if (null != ThrownException)
1676 FireWorkflowTerminating(ThrownException);
1678 FireWorkflowTerminating(error);
1681 // mark instance as canceled
1682 this.stateChangedSincePersistence = true;
1683 WorkflowStatus oldStatus = this.WorkflowStatus;
1684 this.rootActivity.SetValue(SuspendOrTerminateInfoProperty, error);
1685 this.WorkflowStatus = WorkflowStatus.Terminated;
1687 // Block message delivery for duration of persistence and marking as invalid instance
1688 using (_msgDeliveryLock.Enter())
1690 TimerQueue.SuspendDelivery();
1691 this.rootActivity.SetValue(Activity.ExecutionResultProperty, ActivityExecutionResult.Canceled);
1694 // persist the instance state
1695 this.Persist(this.rootActivity, true, false);
1699 // the persistence at terminate threw an exception.
1700 this.WorkflowStatus = oldStatus;
1701 this.rootActivity.SetValue(Activity.ExecutionResultProperty, ActivityExecutionResult.None);
1702 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Persistence attempt at instance '{0}' termination threw an exception. Aborting the instance. The termination event would be raised. The instance would execute from the last persisted point whenever started by the host explicitly. Exception:{1}\n{2}", this.InstanceIdString, e.Message, e.StackTrace);
1707 // Any remaining messages in queues are zombie messages so move all to the pending queue
1708 this.qService.MoveAllMessagesToPendingQueue();
1710 if (null != ThrownException)
1711 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Terminated, ThrownException);
1713 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Terminated, error);
1715 // unsubscribe for model changes
1716 Debug.Assert(this.IsInstanceValid);
1717 // mark instance as invalid
1718 this.IsInstanceValid = false;
1721 if (currentAtomicActivity != null)
1723 atomicActivityEvent.Set();
1724 atomicActivityEvent.Close();
1729 if ((this.rootActivity == this.CurrentActivity) && this.rootActivity.ExecutionStatus == ActivityExecutionStatus.Closed)
1731 using (_msgDeliveryLock.Enter())
1739 this.Scheduler.CanRun = true;
1751 // aborts the schedule instance [....]
1752 // must be called only from outside the instance... the thread running the instance must
1753 // never call this method... it should call AbortOnIdle instead.
1754 internal void Abort()
1756 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor::Abort : Got a abort request for instance {0}", this.InstanceIdString);
1760 // Stop threads from outside - message delivery and control operations
1761 using (this._executorLock.Enter())
1763 // tell the scheduler to stop returnig items from its queue (ref: 16534)
1764 this.Scheduler.AbortOrTerminateRequested = true;
1765 // tell the scheduler to stop running
1766 this.Scheduler.CanRun = false;
1768 // If there were some thread executing the instance, then setting up
1769 // the callback, the thread getting done and the notification coming back
1770 // is racy... so we lock the scheduler
1771 using (new SchedulerLockGuard(this._schedulerLock, this))
1773 using (this._msgDeliveryLock.Enter())
1775 using (new ServiceEnvironment(this.rootActivity))
1778 // check if this is a valid in-memory instance
1779 if (!this.IsInstanceValid)
1780 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1791 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Abort attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace);
1796 // this method must be called with the scheduler lock held
1797 internal void AbortOnIdle()
1799 InstanceLock.AssertIsLocked(this._schedulerLock);
1800 InstanceLock.AssertIsLocked(this._msgDeliveryLock);
1802 // check if the instance can be aborted
1803 if (!this.IsInstanceValid)
1806 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Aborting);
1808 TimerQueue.SuspendDelivery();
1810 // tell the scheduler to stop running
1811 this.Scheduler.CanRun = false;
1813 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Aborting instance {0}", this.InstanceIdString);
1817 // abort any transaction in progress
1818 if (this.currentAtomicActivity != null)
1820 this.RollbackTransaction(null, this.currentAtomicActivity);
1821 this.currentAtomicActivity = null;
1824 // clear the batched work
1825 this.ResourceManager.ClearAllBatchedWork();
1827 // unlock instance state w/o saving it
1828 WorkflowPersistenceService persistenceSvc = this.WorkflowRuntime.WorkflowPersistenceService;
1829 if (persistenceSvc != null)
1831 persistenceSvc.UnlockWorkflowInstanceState(attemptedRootDispose ? null : this.rootActivity);
1832 if (HasNonEmptyWorkBatch())
1834 this.CommitTransaction(this.rootActivity);
1840 if (WorkflowExecutor.IsIrrecoverableException(e))
1847 // mark instance as invalid
1848 this.IsInstanceValid = false;
1849 DisposeRootActivity(true);
1850 if (currentAtomicActivity != null)
1852 atomicActivityEvent.Set();
1853 atomicActivityEvent.Close();
1855 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Aborted);
1863 // suspends the schedule instance [....]
1864 // must be called only from outside the instance... the thread running the instance must
1865 // never call this method... it should call SuspendOnIdle instead.
1866 internal bool Suspend(string error)
1868 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a suspend request for instance {0}", this.InstanceIdString);
1872 // check if this is a valid in-memory instance
1873 if (!this.IsInstanceValid)
1874 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1877 // Stop threads from outside - message delivery and control operations
1878 using (this._executorLock.Enter())
1880 // check if this is a valid in-memory instance
1881 if (!this.IsInstanceValid)
1882 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1884 // tell the scheduler to stop running
1885 this.Scheduler.CanRun = false;
1887 // If there were some thread executing the instance, then setting up
1888 // the callback, the thread getting done and the notification coming back
1889 // is racy... so we lock the scheduler
1890 using (new SchedulerLockGuard(this._schedulerLock, this))
1892 using (new ServiceEnvironment(this.rootActivity))
1894 // check if this is a valid in-memory instance
1895 if (!this.IsInstanceValid)
1896 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1898 return this.SuspendOnIdle(error);
1905 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Suspend attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace);
1910 // this method must be called with the scheduler lock held
1911 internal bool SuspendOnIdle(string error)
1913 InstanceLock.AssertIsLocked(this._schedulerLock);
1915 // check if the instance can be suspended
1916 if (!this.IsInstanceValid)
1919 // if atomic activity in progress, then throw
1920 if (this.currentAtomicActivity != null)
1922 this.Scheduler.CanRun = true;
1923 throw new ExecutorLocksHeldException(atomicActivityEvent);
1927 // if already suspended or if just created, then do nothing
1928 WorkflowStatus status = this.WorkflowStatus;
1929 if (status == WorkflowStatus.Suspended || status == WorkflowStatus.Created)
1932 FireWorkflowSuspending(error);
1934 // tell the scheduler to stop running
1935 this.Scheduler.CanRun = false;
1937 switch (this.rootActivity.ExecutionStatus)
1939 case ActivityExecutionStatus.Initialized:
1940 case ActivityExecutionStatus.Executing:
1941 case ActivityExecutionStatus.Canceling:
1942 case ActivityExecutionStatus.Faulting:
1943 case ActivityExecutionStatus.Compensating:
1946 case ActivityExecutionStatus.Closed:
1952 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Suspending instance {0}", this.InstanceIdString);
1954 // mark it as suspended
1955 this.stateChangedSincePersistence = true;
1956 this.WorkflowStatus = WorkflowStatus.Suspended;
1957 this.rootActivity.SetValue(SuspendOrTerminateInfoProperty, error);
1959 // note: don't persist the instance and don't mark it as invalid.
1960 // The suspended instances must be explicitly unloaded, if required.
1961 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Suspended, error);
1969 // resumes the schedule instance [....]
1970 // must be called only from outside the instance... the thread running the instance must
1971 // never call this method... it should call ResumeOnIdle instead.
1972 internal void Resume()
1974 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a resume request for instance {0}", this.InstanceIdString);
1978 // check if this is a valid in-memory instance
1979 if (!this.IsInstanceValid)
1980 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1982 using (ScheduleWork work = new ScheduleWork(this))
1984 // Stop threads from outside - message delivery and control operations
1985 using (this._executorLock.Enter())
1987 // check if this is a valid in-memory instance
1988 if (!this.IsInstanceValid)
1989 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1991 if ((this.WorkflowStatus != WorkflowStatus.Suspended))
1994 using (new SchedulerLockGuard(this._schedulerLock, this))
1996 //@@Undone-- bmalhi there is one test in bat
1997 //which fails here. This check is right thing but im
1998 //commenting it out for bat.
1999 // [....]: this fails because when we load an instance into memory it grabs
2000 // the scheduler lock and starts running. By the time the user Resume request
2001 // gets the scheduler lock the instance is often done (the AbortBat test case scenario)
2002 // Balinder is attempting a fix to separate rehydration from resuming execution.
2003 /*if (!this.IsInstanceValid)
2004 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
2006 using (new ServiceEnvironment(this.rootActivity))
2008 this.ResumeOnIdle(true);
2016 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Resume attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace);
2021 // this method must be called with the scheduler lock held
2022 internal bool ResumeOnIdle(bool outsideThread)
2024 InstanceLock.AssertIsLocked(this._schedulerLock);
2026 // check if this is a valid in-memory instance
2027 if (!this.IsInstanceValid)
2030 // if not suspended and CanRun is true, then nothing to resume
2031 if ((this.WorkflowStatus != WorkflowStatus.Suspended) && (!this.Scheduler.CanRun))
2034 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Resuming);
2036 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Resuming instance {0}", this.InstanceIdString);
2038 this.stateChangedSincePersistence = true;
2039 this.WorkflowStatus = WorkflowStatus.Running;
2040 this.rootActivity.SetValue(SuspendOrTerminateInfoProperty, string.Empty);
2042 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Resumed, ThrownException);
2044 using (this._msgDeliveryLock.Enter())
2046 TimerQueue.ResumeDelivery();
2049 // resume the instance
2051 this.Scheduler.Resume();
2053 // being called from within the scheduler thread, so just allow the
2054 // scheduler to run without requesting a new thread
2055 this.Scheduler.CanRun = true;
2062 #region Transaction Management
2064 internal bool IsActivityInAtomicContext(Activity activity, out Activity atomicActivity)
2066 Debug.Assert(activity != null);
2068 atomicActivity = null;
2069 while (activity != null)
2071 if (activity == this.currentAtomicActivity)
2073 atomicActivity = activity;
2076 activity = activity.Parent;
2081 private void CreateTransaction(Activity atomicActivity)
2083 Debug.Assert(this.currentAtomicActivity == null, "There is already a transacted activity running");
2085 TransactionalProperties transactionalProperties = new TransactionalProperties();
2087 TransactionOptions tranOpts = new TransactionOptions();
2088 WorkflowTransactionOptions atomicTxn = TransactedContextFilter.GetTransactionOptions(atomicActivity);
2089 Debug.Assert(atomicTxn != null, "null atomicTxn");
2092 tranOpts.IsolationLevel = atomicTxn.IsolationLevel;
2093 if (tranOpts.IsolationLevel == IsolationLevel.Unspecified)
2094 tranOpts.IsolationLevel = IsolationLevel.Serializable;
2096 tranOpts.Timeout = atomicTxn.TimeoutDuration;
2098 // Create a promotable transaction (can be promoted to DTC when necessary)
2099 // as COM+ user code may want to participate in the transaction
2100 // Enlist to the transaction for abort notification
2101 System.Transactions.CommittableTransaction transaction = new CommittableTransaction(tranOpts);
2102 // Can switch back to using TransactionCompletionHandler once VS562627 is fixed
2103 // transaction.TransactionCompleted += new TransactionCompletedEventHandler(TransactionCompletionHandler);
2104 //transaction.EnlistVolatile(new TransactionNotificationEnlistment(this, transaction, atomicActivity), EnlistmentOptions.None);
2105 transactionalProperties.Transaction = transaction;
2106 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2107 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2108 " .Created enlistable transaction " + ((System.Transactions.Transaction)transaction).GetHashCode() +
2109 " with timeout " + tranOpts.Timeout + ", isolation " + tranOpts.IsolationLevel);
2111 // create a local queuing service per atomic context
2112 transactionalProperties.LocalQueuingService = new WorkflowQueuingService(this.qService);
2114 // Store the transaction properties onto the activity
2115 atomicActivity.SetValue(TransactionalPropertiesProperty, transactionalProperties);
2117 // Set current atomic activity
2118 this.currentAtomicActivity = atomicActivity;
2119 atomicActivityEvent = new ManualResetEvent(false);
2120 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + " .Set CurrentAtomicActivity to " + atomicActivity.Name);
2123 private void DisposeTransaction(Activity atomicActivity)
2125 // Validates the assumption that only one atomic activity in execution at a time
2126 //Debug.Assert((atomicActivity == this.currentAtomicActivity),
2127 // "Activity context " + atomicActivity.Name + " different from currentAtomicActivity " + this.currentAtomicActivity.Name);
2129 // Cleanup work following a transaction commit or Rollback
2130 TransactionalProperties transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(TransactionalPropertiesProperty);
2132 // release transaction
2133 transactionalProperties.Transaction.Dispose();
2134 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2135 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2136 " .Disposed enlistable transaction " +
2137 ((System.Transactions.Transaction)transactionalProperties.Transaction).GetHashCode());
2139 // cleanup properties
2140 transactionalProperties.Transaction = null;
2141 transactionalProperties.LocalQueuingService = null;
2142 transactionalProperties.Transaction = null;
2144 // We no longer clear the currentAtomicActivity member here
2145 // but only in the callers of this method (CommitTransaction and RollbackTransaction).
2146 // However, we do this only in CommitTransaction but omit resetting it in RollbackTransaction
2147 // because a complete reversal of a TransactionScopeActivity will restore the
2148 // workflow instance state to a prior checkpointed state.
2149 atomicActivityEvent.Set();
2150 atomicActivityEvent.Close();
2154 private void CommitTransaction(Activity activityContext)
2156 if (null == Transaction.Current)
2159 // No TxScopeActivity or external tx
2160 // Ask the TxService to commit
2161 // In this scenario retries are OK as it owns the tx
2165 // Pass a delegate that does the batch commit
2166 // so that it can do retries
2167 this.WorkflowRuntime.TransactionService.CommitWorkBatch(DoResourceManagerCommit);
2168 this.ResourceManager.Complete();
2172 this.ResourceManager.HandleFault();
2178 Debug.Assert(activityContext != null, "null activityContext");
2180 TransactionalProperties transactionalProperties = null;
2181 bool inTxScope = (activityContext == this.currentAtomicActivity);
2183 // Tx is either from TxScopeActivity or it is external
2186 transactionalProperties = (TransactionalProperties)activityContext.GetValue(TransactionalPropertiesProperty);
2187 if (CheckAndProcessTransactionAborted(transactionalProperties))
2191 // Commit the batches and rely on the enlistment to do completion/rollback work for the batches
2192 // TxService must use the ambient transaction directly or do a dependent clone.
2195 this.WorkflowRuntime.TransactionService.CommitWorkBatch(DoResourceManagerCommit);
2200 // This tx is doomed, clean up batches
2201 ResourceManager.HandleFault();
2208 // DTC transaction commit needs to be done after TransactionScope Complete
2209 // because the Commit Voting needs to happen on the the original thread
2210 // that created the transaction. Otherwise the transaction will abort after timing out.
2211 Debug.Assert(null != transactionalProperties, "TransactionProperties from TransactionScopeActivity should not be null.");
2212 DisposeTransactionScope(transactionalProperties);
2216 // If we are in a tx scope we need to commit our tx
2220 // The tx will be Committable if there was not ambient tx when the scope started
2221 // It will be Dependent if there was an ambient tx when the scope started
2222 // (The external case is explicitly disabled for V1)
2225 CommittableTransaction ctx = transactionalProperties.Transaction as CommittableTransaction;
2234 qService.PostPersist(false);
2238 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2239 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2240 " .Committed CommittableTransaction " +
2241 ((System.Transactions.Transaction)transactionalProperties.Transaction).GetHashCode());
2244 DependentTransaction dtx = transactionalProperties.Transaction as DependentTransaction;
2253 qService.PostPersist(false);
2256 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2257 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2258 " .Completed DependentTransaction " +
2259 ((System.Transactions.Transaction)transactionalProperties.Transaction).GetHashCode());
2265 // This tx (scope activity or external) is doomed, clean up batches
2266 ResourceManager.HandleFault();
2271 // If commit throws we'll do this call in RollbackTransaction.
2272 // However, the currentAtomicActivity member is not reset in RollbackTransaction
2273 // because a complete reversal of a TransactionScopeActivity will restore the
2274 // workflow instance state to a prior checkpointed state.
2275 DisposeTransaction(activityContext);
2276 this.currentAtomicActivity = null;
2278 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2279 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2280 "Reset CurrentAtomicActivity to null");
2284 // Tell the batches that we committed successfully
2285 ResourceManager.Complete();
2289 /// Call commit on the VolatileResourceManager to commit all work in the batch.
2290 /// Transaction.Current must be non-null.
2292 private void DoResourceManagerCommit()
2294 if (null == Transaction.Current)
2295 throw new Exception(ExecutionStringManager.NullAmbientTransaction);
2297 this.ResourceManager.Commit();
2300 private void RollbackTransaction(Exception exp, Activity activityContext)
2302 if (activityContext == this.currentAtomicActivity)
2304 Debug.Assert((activityContext == this.currentAtomicActivity),
2305 "Activity context " + activityContext.Name + " different from currentAtomicActivity " + this.currentAtomicActivity.Name);
2307 TransactionalProperties transactionalProperties = (TransactionalProperties)activityContext.GetValue(TransactionalPropertiesProperty);
2308 if (transactionalProperties.TransactionState != TransactionProcessState.AbortProcessed)
2310 // If TransactionState is not already AbortProcessed, Set it to AbortProcessed as we have raised exception for it already
2311 // Possible call paths for which it's not already AbortProcessed:
2312 // TransactionState == Aborted if due to transaction failure notified through TransactionCompletionHandler
2313 // TransactionState == Ok if Called from external exception raising (e.g. a throw activity in Atomic context)
2314 transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed;
2317 Debug.Assert((transactionalProperties.Transaction != null), "Null Transaction while transaction is present");
2318 Debug.Assert((transactionalProperties.LocalQueuingService != null), "Null LocalQueuingService while transaction is present");
2322 DisposeTransactionScope(transactionalProperties);
2324 // roll back transaction
2325 System.Transactions.Transaction transaction = transactionalProperties.Transaction;
2326 if (System.Transactions.TransactionStatus.Aborted != transaction.TransactionInformation.Status)
2327 transaction.Rollback();
2328 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2329 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2330 " .Aborted enlistable transaction " +
2331 ((System.Transactions.Transaction)transaction).GetHashCode());
2335 // roolback queuing service state
2336 WorkflowQueuingService queuingService = transactionalProperties.LocalQueuingService;
2337 queuingService.Complete(false);
2339 // dispose transaction. However, do not reset the currentAtomicActivity member here
2340 // because a complete reversal of a TransactionScopeActivity will restore the
2341 // workflow instance state to a prior checkpointed state.
2342 DisposeTransaction(this.currentAtomicActivity);
2347 #region VolatileEnlistment for Transaction Completion Notification
2349 * Leaving this class in place as we will need it for the flow through tx story in V2
2350 class TransactionNotificationEnlistment : IEnlistmentNotification, IActivityEventListener<EventArgs>
2352 WorkflowExecutor workflowExecutor;
2353 Transaction transaction;
2354 Activity atomicActivity;
2355 internal TransactionNotificationEnlistment(WorkflowExecutor exec, Transaction tx, Activity atomicActivity)
2357 this.workflowExecutor = exec;
2358 this.transaction = tx;
2359 this.atomicActivity = atomicActivity;
2362 #region IEnlistmentNotification Members
2364 void IEnlistmentNotification.Commit(Enlistment enlistment)
2369 void IEnlistmentNotification.InDoubt(Enlistment enlistment)
2374 void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
2376 preparingEnlistment.Prepared();
2379 void IEnlistmentNotification.Rollback(Enlistment enlistment)
2382 // Currently this method isn't used.
2383 // The problem is that we must acquire the sched lock in order to schedule
2384 // an item. While we wait trying to acquire the lock the transaction is held open.
2385 // If the instance is idle we acquire the lock right away and this works fine.
2386 // However is we have items to run we'll check the transaction, find that it is aborted
2387 // and start exception handling. During the entire exception handling process the transaction
2388 // and the associated connections will be held open. This is not good.
2389 // Post V1 we need scheduler changes to allow us to safely asynchronously schedule work
2390 // without taking the scheduler lock.
2393 // ensure transaction timeout/abort is processed in case of a
2394 // blocked activity inside a transactional scope
2395 ScheduleTransactionTimeout();
2398 private void ScheduleTransactionTimeout()
2403 // We're going to check executor state and possibly enqueue a workitem
2404 // Must take the scheduleExecutor lock
2405 using (this.workflowExecutor._schedulerLock.Enter())
2407 if (!this.workflowExecutor.IsInstanceValid)
2410 // If the exception has already been taken care of, ignore this abort notification
2411 Activity curAtomicActivity = this.workflowExecutor.currentAtomicActivity;
2412 if ((curAtomicActivity != null)&&(curAtomicActivity==atomicActivity))
2414 TransactionalProperties transactionalProperties = (TransactionalProperties)curAtomicActivity.GetValue(TransactionalPropertiesProperty);
2415 if ((transactionalProperties.Transaction == this.transaction) &&
2416 (transactionalProperties.TransactionState != TransactionProcessState.AbortProcessed))
2418 transactionalProperties.TransactionState = TransactionProcessState.Aborted;
2420 using (this.workflowExecutor.MessageDeliveryLock.Enter())
2422 using (new ServiceEnvironment(this.workflowExecutor.RootActivity))
2424 using (this.workflowExecutor.SetCurrentActivity(curAtomicActivity))
2427 // This will schedule (async) a work item to cancel the tx scope activity
2428 // However this item will never get run - we always check if the
2429 // tx has aborted prior to running any items so this is really
2430 // just a "wake up" notification to the scheduler.
2431 Activity contextActivity = ContextActivityUtils.ContextActivity(curAtomicActivity);
2432 ActivityExecutorDelegateInfo<EventArgs> dummyCallback = new ActivityExecutorDelegateInfo<EventArgs>(this, contextActivity, true);
2433 dummyCallback.InvokeDelegate(contextActivity, EventArgs.Empty, false);
2443 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "AbortNotificationEnlistment: instanceId: {0} failed to process ScheduleTransactionTimeout with exception {1} ", this.workflowExecutor.this.InstanceIdString, e.Message);
2447 void IActivityEventListener<EventArgs>.OnEvent(object sender, EventArgs e)
2449 // this will never be invoked since Scheduler will process the transaction aborted request
2454 #endregion VolatileEnlistment for AbortNotification
2456 internal static bool CheckAndProcessTransactionAborted(TransactionalProperties transactionalProperties)
2458 if (transactionalProperties.Transaction != null && transactionalProperties.Transaction.TransactionInformation.Status != TransactionStatus.Aborted)
2461 // If transaction aborted but not processed,
2462 // process it (i.e. throw to invoke Exception handling)
2463 // otherwise return if transaction aborted
2464 switch (transactionalProperties.TransactionState)
2466 case TransactionProcessState.Ok:
2467 case TransactionProcessState.Aborted:
2468 transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed;
2469 throw new TransactionAbortedException();
2471 case TransactionProcessState.AbortProcessed:
2479 private void DisposeTransactionScope(TransactionalProperties transactionalProperties)
2481 if (transactionalProperties.TransactionScope != null)
2483 // Need to call Complete othwise the transaction will be aborted
2484 transactionalProperties.TransactionScope.Complete();
2485 transactionalProperties.TransactionScope.Dispose();
2486 transactionalProperties.TransactionScope = null;
2487 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2488 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2489 "Left TransactionScope, Current atomic acitivity was " +
2490 ((this.currentAtomicActivity == null) ? null : this.currentAtomicActivity.Name));
2494 #region delay scheduling of items for ACID purposes
2496 private void AddItemToBeScheduledLater(Activity atomicActivity, SchedulableItem item)
2498 if (atomicActivity == null)
2501 // Activity may not be atomic and is an activity which is not
2502 // yet scheduled for execution (typically receive case)
2503 if (!atomicActivity.SupportsTransaction)
2506 TransactionalProperties transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(TransactionalPropertiesProperty);
2507 if (transactionalProperties != null)
2509 lock (transactionalProperties)
2511 List<SchedulableItem> notifications = null;
2512 notifications = transactionalProperties.ItemsToBeScheduledAtCompletion;
2513 if (notifications == null)
2515 notifications = new List<SchedulableItem>();
2516 transactionalProperties.ItemsToBeScheduledAtCompletion = notifications;
2518 notifications.Add(item);
2523 private void ScheduleDelayedItems(Activity atomicActivity)
2525 List<SchedulableItem> items = null;
2526 TransactionalProperties transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(TransactionalPropertiesProperty);
2528 if (transactionalProperties == null)
2531 lock (transactionalProperties)
2533 items = transactionalProperties.ItemsToBeScheduledAtCompletion;
2537 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2538 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2539 " .Scheduling delayed " + items.Count + " number of items");
2541 foreach (SchedulableItem item in items)
2543 this.Scheduler.ScheduleItem(item, false, true);
2547 transactionalProperties.ItemsToBeScheduledAtCompletion = null;
2551 #endregion delay scheduling of items for ACID purposes
2553 #endregion Transaction Management
2555 #region Exception Management
2557 internal void ExceptionOccured(Exception exp, Activity currentActivity, string originalActivityId)
2559 Debug.Assert(exp != null, "null exp");
2560 Debug.Assert(currentActivity != null, "null currentActivity");
2561 // exception tracking work
2563 if (this.ThrownException != exp)
2565 // first time exception
2566 this.ThrownException = exp;
2567 this.activityThrowingException = currentActivity.QualifiedName;
2568 originalActivityId = currentActivity.QualifiedName;
2572 // rethrown exception
2573 originalActivityId = this.activityThrowingException;
2575 Guid contextGuid = ((ActivityExecutionContextInfo)ContextActivityUtils.ContextActivity(currentActivity).GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid;
2576 Guid parentContextGuid = Guid.Empty;
2577 if (null != currentActivity.Parent)
2578 parentContextGuid = ((ActivityExecutionContextInfo)ContextActivityUtils.ContextActivity(currentActivity.Parent).GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid;
2579 this.FireExceptionOccured(exp, currentActivity.QualifiedName, originalActivityId, contextGuid, parentContextGuid);
2581 // notify the activity.
2583 using (new ServiceEnvironment(currentActivity))
2585 using (SetCurrentActivity(currentActivity))
2587 using (ActivityExecutionContext executionContext = new ActivityExecutionContext(currentActivity, true))
2588 executionContext.FaultActivity(exp);
2592 // transaction and batching clean-up on the activity that handles the exception
2593 this.RollbackTransaction(exp, currentActivity);
2594 if ((currentActivity is TransactionScopeActivity) || (exp is PersistenceException))
2595 this.BatchCollection.RollbackBatch(currentActivity);
2598 internal Exception ThrownException
2600 get { return thrownException; }
2601 set { thrownException = value; }
2604 internal static bool IsIrrecoverableException(Exception e)
2606 return ((e is OutOfMemoryException) ||
2607 (e is StackOverflowException) ||
2608 (e is ThreadInterruptedException) ||
2609 (e is ThreadAbortException));
2612 #endregion Exception Management
2614 #region Tracking Management
2616 internal void Track(Activity activity, string key, object args)
2618 FireUserTrackPoint(activity, key, args);
2621 internal void FireExceptionOccured(Exception e, string currentActivityPath, string originalActivityPath, Guid contextGuid, Guid parentContextGuid)
2623 FireWorkflowException(e, currentActivityPath, originalActivityPath, contextGuid, parentContextGuid);
2628 #region Dynamic Update Management
2630 #region Dynamic Update From Outside the instance
2631 internal Activity GetWorkflowDefinition(string workflowContext)
2633 if (workflowContext == null)
2634 throw new ArgumentNullException("workflowContext");
2636 return this.WorkflowDefinition;
2639 internal Activity GetWorkflowDefinitionClone(string workflowContext)
2641 if (workflowContext == null)
2642 throw new ArgumentNullException("workflowContext");
2644 Activity definition = this.WorkflowDefinition;
2646 using (new WorkflowDefinitionLock(definition))
2648 return definition.Clone();
2652 internal void ApplyWorkflowChanges(WorkflowChanges workflowChanges)
2654 // Accessing InstanceId is not thread safe here!
2655 //WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a dynamic update request from outside for instance {0}", this.InstanceIdString);
2656 DiagnosticStackTrace("dynamic update request");
2659 if (workflowChanges == null)
2660 throw new ArgumentNullException("workflowChanges");
2662 // check if this is a valid in-memory instance
2663 if (!this.IsInstanceValid)
2664 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
2666 if (this.currentAtomicActivity != null)
2667 throw new InvalidOperationException(ExecutionStringManager.Error_InsideAtomicScope);
2671 using (ScheduleWork work = new ScheduleWork(this))
2673 // block other instance operations from outside
2674 using (this._executorLock.Enter())
2676 // check if this is a valid in-memory instance
2677 if (!this.IsInstanceValid)
2678 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
2680 // get the instance to stop running
2681 this.Scheduler.CanRun = false;
2682 using (new SchedulerLockGuard(this._schedulerLock, this))
2684 using (new ServiceEnvironment(this.rootActivity))
2686 bool localSuspend = false;
2688 // check if this is a valid in-memory instance
2689 if (!this.IsInstanceValid)
2690 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
2694 // check the status of the schedule
2695 switch (this.WorkflowStatus)
2697 ////case ActivityExecutionStatus.Completed:
2699 case WorkflowStatus.Completed:
2700 case WorkflowStatus.Terminated:
2701 throw new InvalidOperationException(
2702 ExecutionStringManager.InvalidOperationRequest);
2703 case WorkflowStatus.Suspended:
2704 // instance already suspended
2705 localSuspend = false;
2708 // suspend the instance
2709 this.SuspendOnIdle(null);
2710 localSuspend = true;
2714 // apply the changes
2715 workflowChanges.ApplyTo(this.rootActivity);
2721 // @undone: for now this will not return till the instance is done
2722 // Once Kumar has fixed 4335, we can enable this.
2723 this.ResumeOnIdle(true);
2727 } // release lock on scheduler
2733 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: dynamic update attempt from outside on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace);
2737 #endregion Dynamic Update From Outside the instance
2738 internal bool OnBeforeDynamicChange(IList<WorkflowChangeAction> changes)
2740 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a dynamic update request for instance {0}", this.InstanceIdString);
2742 if (!this.IsInstanceValid)
2743 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
2745 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Found a match for the schedule in updating instance {0}", this.InstanceIdString);
2747 FireDynamicUpdateBegin(changes);
2752 internal void OnAfterDynamicChange(bool updateSucceeded, IList<WorkflowChangeAction> changes)
2754 if (updateSucceeded)
2756 RefreshWorkflowDefinition();
2757 //Commit temporary work
2758 FireDynamicUpdateCommit(changes);
2759 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Changed);
2764 FireDynamicUpdateRollback(changes);
2767 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Done updating a schedule in instance {0}", this.InstanceIdString);
2771 bool IWorkflowCoreRuntime.IsDynamicallyUpdated
2775 return ((Activity)this.WorkflowDefinition).GetValue(WorkflowChanges.WorkflowChangeActionsProperty) != null;
2780 #region Diagnostic tracing
2782 [System.Diagnostics.Conditional("DEBUG")]
2783 void DiagnosticStackTrace(string reason)
2785 StackTrace st = new StackTrace(true);
2786 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: InstanceId: {0} : {1} stack trace: {2}", this.InstanceIdString, reason, st.ToString());
2791 #region Timer event support
2793 WaitCallback IWorkflowCoreRuntime.ProcessTimersCallback
2797 return new WaitCallback(this.WorkflowInstance.ProcessTimers);
2803 #region IServiceProvider members
2805 object IServiceProvider.GetService(Type serviceType)
2807 return ((IWorkflowCoreRuntime)this).GetService(this.rootActivity, serviceType);
2811 #region IWorkflowCoreRuntime Members
2813 Activity IWorkflowCoreRuntime.CurrentActivity
2817 #pragma warning disable 56503
2818 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2819 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2820 #pragma warning restore 56503
2821 return this.CurrentActivity;
2824 Activity IWorkflowCoreRuntime.CurrentAtomicActivity
2828 return this.currentAtomicActivity;
2831 Guid IWorkflowCoreRuntime.StartWorkflow(Type workflowType, Dictionary<string, object> namedArgumentValues)
2833 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2834 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2836 Guid instanceId = Guid.Empty;
2837 WorkflowInstance instance = this.WorkflowRuntime.InternalCreateWorkflow(new CreationContext(workflowType, this, this.CurrentActivity.QualifiedName, namedArgumentValues), Guid.NewGuid());
2838 if (instance != null)
2840 instanceId = instance.InstanceId;
2846 void IWorkflowCoreRuntime.ScheduleItem(SchedulableItem item, bool isInAtomicTransaction, bool transacted, bool queueInTransaction)
2848 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2849 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2850 if (!queueInTransaction)
2851 this.Scheduler.ScheduleItem(item, isInAtomicTransaction, transacted);
2853 this.AddItemToBeScheduledLater(this.CurrentActivity, item);
2855 public IDisposable SetCurrentActivity(Activity activity)
2857 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2858 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2859 Activity oldCurrentActivity = this.CurrentActivity;
2860 this.CurrentActivity = activity;
2861 return new ResetCurrentActivity(this, oldCurrentActivity);
2863 Guid IWorkflowCoreRuntime.InstanceID
2867 #pragma warning disable 56503
2868 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2869 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2870 #pragma warning restore 56503
2871 return this.InstanceId;
2874 bool IWorkflowCoreRuntime.SuspendInstance(string suspendDescription)
2876 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2877 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2878 return this.SuspendOnIdle(suspendDescription);
2880 void IWorkflowCoreRuntime.TerminateInstance(Exception e)
2882 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2883 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2885 this.ThrownException = e;
2886 this.TerminateOnIdle(WorkflowExecutor.GetNestedExceptionMessage(e));
2888 bool IWorkflowCoreRuntime.Resume()
2890 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2891 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2892 return this.ResumeOnIdle(false);
2894 void IWorkflowCoreRuntime.RaiseHandlerInvoking(Delegate handlerDelegate)
2896 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2897 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2898 FireWorkflowHandlerInvokingEvent(this, WorkflowEventInternal.HandlerInvoking, handlerDelegate);
2900 void IWorkflowCoreRuntime.RaiseActivityExecuting(Activity activity)
2902 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2903 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2904 FireActivityExecuting(this, activity);
2906 void IWorkflowCoreRuntime.RaiseHandlerInvoked()
2908 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2909 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2910 FireWorkflowExecutionEvent(this, WorkflowEventInternal.HandlerInvoked);
2912 void IWorkflowCoreRuntime.CheckpointInstanceState(Activity currentActivity)
2914 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2915 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2917 // Call CheckpointInstanceState() before CreateTransaction() because
2918 // creating a TX can fail and then we end up ----ing up in HandleFault().
2919 using (MessageDeliveryLock.Enter())
2921 this.WorkflowStateRollbackService.CheckpointInstanceState();
2923 this.CreateTransaction(currentActivity);
2925 void IWorkflowCoreRuntime.RequestRevertToCheckpointState(Activity currentActivity, EventHandler<EventArgs> callbackHandler, EventArgs callbackData, bool suspendOnRevert, string suspendInfo)
2927 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2928 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2929 this.WorkflowStateRollbackService.RequestRevertToCheckpointState(currentActivity, callbackHandler, callbackData, suspendOnRevert, suspendInfo);
2931 void IWorkflowCoreRuntime.DisposeCheckpointState()
2933 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2934 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2935 this.WorkflowStateRollbackService.DisposeCheckpointState();
2937 int IWorkflowCoreRuntime.GetNewContextActivityId()
2939 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2940 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2941 return this.GetNewContextId();
2943 Activity IWorkflowCoreRuntime.GetContextActivityForId(int stateId)
2945 if (this.subStateMap.ContainsKey(stateId))
2946 return this.subStateMap[stateId];
2949 void IWorkflowCoreRuntime.RaiseException(Exception e, Activity activity, string responsibleActivity)
2951 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2952 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2953 this.ExceptionOccured(e, activity, responsibleActivity);
2955 void IWorkflowCoreRuntime.RegisterContextActivity(Activity activity)
2957 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2958 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2959 this.RegisterDynamicActivity(activity, false);
2961 void IWorkflowCoreRuntime.UnregisterContextActivity(Activity activity)
2963 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2964 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2965 this.UnregisterDynamicActivity(activity);
2967 void IWorkflowCoreRuntime.ActivityStatusChanged(Activity activity, bool transacted, bool committed)
2969 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2970 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2973 if (activity.ExecutionStatus == ActivityExecutionStatus.Executing)
2975 bool mustPersistState = (TransactedContextFilter.GetTransactionOptions(activity) != null) ? true : false;
2976 if (mustPersistState && this.WorkflowRuntime.WorkflowPersistenceService == null)
2978 string errMsg = String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceService, this.InstanceId);
2979 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, errMsg);
2980 throw new InvalidOperationException(errMsg);
2983 else if (activity.ExecutionStatus == ActivityExecutionStatus.Closed)
2985 this.ScheduleDelayedItems(activity);
2987 else if (activity.ExecutionStatus == ActivityExecutionStatus.Canceling || activity.ExecutionStatus == ActivityExecutionStatus.Faulting)
2989 if (TransactedContextFilter.GetTransactionOptions(activity) != null)
2991 // If the activity is transactional and is being canceled, roll back
2992 // any batches associated with it. (This does nothing if the activity
2994 this.BatchCollection.RollbackBatch(activity);
3001 FireActivityStatusChange(this, activity);
3004 if (activity.ExecutionStatus == ActivityExecutionStatus.Closed)
3006 if (!(activity is ICompensatableActivity) || ((activity is ICompensatableActivity) && activity.CanUninitializeNow))
3007 CorrelationTokenCollection.UninitializeCorrelationTokens(activity);
3011 void IWorkflowCoreRuntime.PersistInstanceState(Activity activity)
3013 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3014 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3016 bool persistOnClose = false;
3017 if (activity.UserData.Contains(typeof(PersistOnCloseAttribute)))
3019 persistOnClose = (bool)activity.UserData[typeof(PersistOnCloseAttribute)];
3023 object[] attributes = activity.GetType().GetCustomAttributes(typeof(PersistOnCloseAttribute), true);
3024 if (attributes != null && attributes.Length > 0)
3025 persistOnClose = true;
3027 if (persistOnClose && this.WorkflowRuntime.GetService<WorkflowPersistenceService>() == null)
3028 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceServiceWithPersistOnClose, activity.Name));
3030 this.ScheduleDelayedItems(activity);
3032 bool unlock = (activity.Parent == null) ? true : false;
3033 bool needsCompensation = false; //
3034 this.Persist(activity, unlock, needsCompensation);
3037 Activity IWorkflowCoreRuntime.LoadContextActivity(ActivityExecutionContextInfo contextInfo, Activity outerActivity)
3039 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3040 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3041 Activity contextActivity = null;
3042 if (this.completedContextActivities.Contains(contextInfo))
3044 contextActivity = (Activity)this.completedContextActivities[contextInfo];
3045 this.completedContextActivities.Remove(contextInfo);
3047 if (contextActivity.Parent != outerActivity.Parent)
3048 contextActivity.parent = outerActivity.Parent;
3052 using (RuntimeEnvironment runtimeEnv = new RuntimeEnvironment(this.WorkflowRuntime))
3054 contextActivity = this.WorkflowRuntime.WorkflowPersistenceService.LoadCompletedContextActivity(contextInfo.ContextGuid, outerActivity);
3055 if (contextActivity == null)
3056 throw new InvalidOperationException(ExecutionStringManager.LoadContextActivityFailed);
3059 return contextActivity;
3061 void IWorkflowCoreRuntime.SaveContextActivity(Activity contextActivity)
3063 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3064 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3065 this.completedContextActivities.Add((ActivityExecutionContextInfo)contextActivity.GetValue(Activity.ActivityExecutionContextInfoProperty), contextActivity);
3067 Activity IWorkflowCoreRuntime.RootActivity
3071 return this.rootActivity;
3074 object IWorkflowCoreRuntime.GetService(Activity activity, Type serviceType)
3076 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3077 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3079 if (serviceType == typeof(IWorkflowCoreRuntime))
3083 else if (serviceType == typeof(WorkflowRuntime))//sorry, no.
3085 else if (serviceType == typeof(WorkflowQueuingService))
3087 WorkflowQueuingService queuingService = ServiceEnvironment.QueuingService;
3088 if (queuingService == null)
3089 queuingService = this.qService; // root Q service
3091 queuingService.CallingActivity = ContextActivityUtils.ContextActivity(activity);
3092 return queuingService;
3094 else if (serviceType == typeof(IWorkflowDebuggerService))
3096 return this._workflowDebuggerService as IWorkflowDebuggerService;
3099 return this.WorkflowRuntime.GetService(serviceType);
3101 bool IWorkflowCoreRuntime.OnBeforeDynamicChange(IList<WorkflowChangeAction> changes)
3103 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3104 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3105 return this.OnBeforeDynamicChange(changes);
3107 void IWorkflowCoreRuntime.OnAfterDynamicChange(bool updateSucceeded, IList<WorkflowChangeAction> changes)
3109 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3110 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3111 this.OnAfterDynamicChange(updateSucceeded, changes);
3113 void IWorkflowCoreRuntime.Track(string key, object args)
3115 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3116 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3117 this.Track(this.CurrentActivity, key, args);
3121 #region ResetCurrentActivity Class
3123 private class ResetCurrentActivity : IDisposable
3125 private WorkflowExecutor workflowExecutor = null;
3126 private Activity oldCurrentActivity = null;
3127 internal ResetCurrentActivity(WorkflowExecutor workflowExecutor, Activity oldCurrentActivity)
3129 this.workflowExecutor = workflowExecutor;
3130 this.oldCurrentActivity = oldCurrentActivity;
3132 void IDisposable.Dispose()
3134 this.workflowExecutor.CurrentActivity = oldCurrentActivity;
3139 // GetTransientBatch is defined in this class but if the workflow is running under a V2.0 Interop environment,
3140 // it calls the Interop activity to get the Batch collection.
3141 private static object GetTransientBatch(DependencyObject dependencyObject)
3143 if (dependencyObject == null)
3144 throw new ArgumentNullException("dependencyObject");
3145 if (!(dependencyObject is Activity))
3146 throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InvalidArgumentType, "dependencyObject", typeof(Activity).ToString()));
3148 Activity currentActivity = (Activity)dependencyObject;
3150 // fetch workflow executor
3151 IWorkflowCoreRuntime workflowExecutor = null;
3152 ISupportInterop interopSupport = null;
3153 if (currentActivity != null)
3155 workflowExecutor = ContextActivityUtils.RetrieveWorkflowExecutor(currentActivity);
3156 interopSupport = workflowExecutor as ISupportInterop;
3159 while (currentActivity != null)
3161 // If the current activity has a batch property, use it.
3162 IWorkBatch transientWorkBatch = currentActivity.GetValueBase(TransientBatchProperty) as IWorkBatch;
3163 if (transientWorkBatch != null)
3164 return transientWorkBatch;
3166 // If it's a transactional activity (transactional scope), create a batch for it.
3167 // (If the activity is not executing, it means that it has canceled, probably
3168 // due to an exception. In this case, we do not create the batch here, but keep
3169 // looking up until we find an appropriate scope, or the root.)
3170 if (TransactedContextFilter.GetTransactionOptions(currentActivity) != null && currentActivity.ExecutionStatus == ActivityExecutionStatus.Executing)
3171 return interopSupport.BatchCollection.GetBatch(currentActivity);
3173 // if activity has a fault handler create a batch for it.
3174 if (currentActivity is CompositeActivity)
3176 foreach (Activity flowActivity in ((ISupportAlternateFlow)currentActivity).AlternateFlowActivities)
3178 if (flowActivity is FaultHandlerActivity)
3179 return interopSupport.BatchCollection.GetBatch(currentActivity);
3183 // If it's the root activity, create a batch for it. Note that we'll only
3184 // ever get here if the root activity is not also an exception handling activity.
3185 if (currentActivity == workflowExecutor.RootActivity)
3186 return interopSupport.BatchCollection.GetBatch(currentActivity);
3188 currentActivity = currentActivity.Parent;
3194 private static string GetNestedExceptionMessage(Exception exp)
3196 string expMessage = "";
3199 if (expMessage == "")
3200 expMessage = exp.Message;
3202 expMessage = expMessage + " " + exp.Message;
3203 exp = exp.InnerException;
3208 #region Internal Events
3210 internal class WorkflowExecutionEventArgs : EventArgs
3212 protected WorkflowEventInternal _eventType;
3214 protected WorkflowExecutionEventArgs() { }
3216 internal WorkflowExecutionEventArgs(WorkflowEventInternal eventType)
3218 _eventType = eventType;
3221 internal WorkflowEventInternal EventType
3223 get { return _eventType; }
3226 private event EventHandler<WorkflowExecutionEventArgs> _workflowExecutionEvent;
3228 internal class WorkflowHandlerInvokingEventArgs : WorkflowExecutionEventArgs
3230 private Delegate _delegateHandler;
3232 internal WorkflowHandlerInvokingEventArgs(WorkflowEventInternal eventType, Delegate delegateHandler)
3235 _delegateHandler = delegateHandler;
3238 internal Delegate DelegateMethod
3240 get { return _delegateHandler; }
3245 /// Consolidated event for the majority of the general events.
3246 /// Filter specific events by WorkflowEventEventArgs.EventType.
3248 internal event EventHandler<WorkflowExecutor.WorkflowExecutionEventArgs> WorkflowExecutionEvent
3252 _workflowExecutionEvent += value;
3256 _workflowExecutionEvent -= value;
3260 internal void FireWorkflowExecutionEvent(object sender, WorkflowEventInternal eventType)
3265 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3266 if (null != localWorkflowExecutionEvent)
3267 localWorkflowExecutionEvent(sender, new WorkflowExecutionEventArgs(eventType));
3270 internal void FireWorkflowHandlerInvokingEvent(object sender, WorkflowEventInternal eventType, Delegate delegateHandler)
3275 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3276 if (null != localWorkflowExecutionEvent)
3277 localWorkflowExecutionEvent(sender, new WorkflowHandlerInvokingEventArgs(eventType, delegateHandler));
3280 internal sealed class WorkflowExecutionSuspendingEventArgs : WorkflowExecutionEventArgs
3282 private string _error;
3284 internal WorkflowExecutionSuspendingEventArgs(string error)
3286 _eventType = WorkflowEventInternal.Suspending;
3290 internal string Error
3292 get { return _error; }
3296 internal sealed class WorkflowExecutionSuspendedEventArgs : WorkflowExecutionEventArgs
3298 private string _error;
3300 internal WorkflowExecutionSuspendedEventArgs(string error)
3302 _eventType = WorkflowEventInternal.Suspended;
3306 internal string Error
3308 get { return _error; }
3312 /// Fires the WorkflowEvent with an EventType of Suspended and WorkflowSuspendedInternalEventArgs
3314 /// <param name="info">Reason for the suspension</param>
3315 private void FireWorkflowSuspending(string error)
3317 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3318 if (null != localWorkflowExecutionEvent)
3319 localWorkflowExecutionEvent(this, new WorkflowExecutionSuspendingEventArgs(error));
3323 /// Fires the WorkflowEvent with an EventType of Suspended and WorkflowSuspendInternalEventArgs
3325 /// <param name="info">Reason for the suspension.</param>
3326 internal void FireWorkflowSuspended(string error)
3328 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3329 if (null != localWorkflowExecutionEvent)
3330 localWorkflowExecutionEvent(this, new WorkflowExecutionSuspendedEventArgs(error));
3334 internal class WorkflowExecutionExceptionEventArgs : WorkflowExecutionEventArgs
3336 private System.Exception _exception;
3337 private string _currentPath, _originalPath;
3338 private Guid _contextGuid, _parentContextGuid;
3340 internal WorkflowExecutionExceptionEventArgs(Exception exception, string currentPath, string originalPath, Guid contextGuid, Guid parentContextGuid)
3342 if (null == exception)
3343 throw new ArgumentNullException("exception");
3345 _exception = exception;
3346 _currentPath = currentPath;
3347 _originalPath = originalPath;
3348 _eventType = WorkflowEventInternal.Exception;
3349 _contextGuid = contextGuid;
3350 _parentContextGuid = parentContextGuid;
3353 internal Exception Exception
3355 get { return _exception; }
3358 internal string CurrentPath
3360 get { return _currentPath; }
3363 internal string OriginalPath
3365 get { return _originalPath; }
3368 internal Guid ContextGuid
3370 get { return _contextGuid; }
3373 internal Guid ParentContextGuid
3375 get { return _parentContextGuid; }
3379 /// Fires the WorkflowEvent with an EventType of Exception and WorkflowExceptionInternalEventArgs
3381 /// <param name="exception">Thrown exception</param>
3382 private void FireWorkflowException(Exception exception, string currentPath, string originalPath, Guid contextGuid, Guid parentContextGuid)
3384 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3385 if (null != localWorkflowExecutionEvent)
3386 localWorkflowExecutionEvent(this, new WorkflowExecutionExceptionEventArgs(exception, currentPath, originalPath, contextGuid, parentContextGuid));
3390 internal sealed class WorkflowExecutionTerminatedEventArgs : WorkflowExecutionEventArgs
3392 private System.Exception _exception;
3393 private string _error;
3395 internal WorkflowExecutionTerminatedEventArgs(string error)
3398 _eventType = WorkflowEventInternal.Terminated;
3401 internal WorkflowExecutionTerminatedEventArgs(Exception exception)
3403 _exception = exception;
3404 _eventType = WorkflowEventInternal.Terminated;
3407 internal Exception Exception
3409 get { return _exception; }
3412 internal string Error
3414 get { return _error; }
3417 internal sealed class WorkflowExecutionTerminatingEventArgs : WorkflowExecutionEventArgs
3419 private System.Exception _exception;
3420 private string _error;
3422 internal WorkflowExecutionTerminatingEventArgs(string error)
3425 _eventType = WorkflowEventInternal.Terminating;
3428 internal WorkflowExecutionTerminatingEventArgs(Exception exception)
3430 if (null == exception)
3431 throw new ArgumentNullException("exception");
3433 _exception = exception;
3434 _eventType = WorkflowEventInternal.Terminating;
3437 internal Exception Exception
3439 get { return _exception; }
3442 internal string Error
3444 get { return _error; }
3448 /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs
3450 /// <param name="exception">Exception that caused the termination</param>
3451 private void FireWorkflowTerminating(Exception exception)
3453 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3454 if (null != localWorkflowExecutionEvent)
3455 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatingEventArgs(exception));
3458 /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs
3460 /// <param name="info">Reason for the termination</param>
3461 private void FireWorkflowTerminating(string error)
3463 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3464 if (null != localWorkflowExecutionEvent)
3465 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatingEventArgs(error));
3468 /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs
3470 /// <param name="exception">Exception that caused the termination</param>
3471 internal void FireWorkflowTerminated(Exception exception)
3473 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3474 if (null != localWorkflowExecutionEvent)
3475 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatedEventArgs(exception));
3478 /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs
3480 /// <param name="info">Reason for the termination</param>
3481 internal void FireWorkflowTerminated(string error)
3483 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3484 if (null != localWorkflowExecutionEvent)
3485 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatedEventArgs(error));
3490 internal class DynamicUpdateEventArgs : WorkflowExecutionEventArgs
3492 private IList<WorkflowChangeAction> _changeActions = new List<WorkflowChangeAction>();
3494 internal DynamicUpdateEventArgs(IList<WorkflowChangeAction> changeActions, WorkflowEventInternal eventType)
3496 _changeActions = changeActions;
3497 _eventType = eventType;
3500 internal IList<WorkflowChangeAction> ChangeActions
3502 get { return _changeActions; }
3506 /// Signals that a dynamic update is starting.
3508 private void FireDynamicUpdateBegin(IList<WorkflowChangeAction> changeActions)
3510 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3511 if (null != localWorkflowExecutionEvent)
3512 localWorkflowExecutionEvent(this, new DynamicUpdateEventArgs(changeActions, WorkflowEventInternal.DynamicChangeBegin));
3517 /// Signals that a dynamic update has errored and rolledback.
3519 private void FireDynamicUpdateRollback(IList<WorkflowChangeAction> changeActions)
3521 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3522 if (null != localWorkflowExecutionEvent)
3523 localWorkflowExecutionEvent(this, new DynamicUpdateEventArgs(changeActions, WorkflowEventInternal.DynamicChangeRollback));
3528 /// Signals that a dynamic update has completed successfully.
3530 private void FireDynamicUpdateCommit(IList<WorkflowChangeAction> changeActions)
3532 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3533 if (null != localWorkflowExecutionEvent)
3534 localWorkflowExecutionEvent(this, new DynamicUpdateEventArgs(changeActions, WorkflowEventInternal.DynamicChangeCommit));
3537 internal class ActivityStatusChangeEventArgs : WorkflowExecutionEventArgs
3539 private Activity _activity;
3541 internal ActivityStatusChangeEventArgs(Activity activity)
3543 _activity = activity;
3544 _eventType = WorkflowEventInternal.ActivityStatusChange;
3547 internal Activity Activity
3549 get { return _activity; }
3553 internal class ActivityExecutingEventArgs : WorkflowExecutionEventArgs
3555 private Activity _activity;
3557 internal ActivityExecutingEventArgs(Activity activity)
3559 _activity = activity;
3560 _eventType = WorkflowEventInternal.ActivityExecuting;
3563 internal Activity Activity
3565 get { return _activity; }
3569 /// Signals that an activity has changed status.
3570 /// This event applies to all status change events
3571 /// for all activities in the workflow.
3573 private void FireActivityStatusChange(object sender, Activity activity)
3575 ActivityStatusChangeEventArgs args = new ActivityStatusChangeEventArgs(activity);
3577 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3578 if (null != localWorkflowExecutionEvent)
3579 localWorkflowExecutionEvent(sender, args);
3582 private void FireActivityExecuting(object sender, Activity activity)
3584 ActivityExecutingEventArgs args = new ActivityExecutingEventArgs(activity);
3586 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3587 if (null != localWorkflowExecutionEvent)
3588 localWorkflowExecutionEvent(sender, args);
3591 internal class UserTrackPointEventArgs : WorkflowExecutionEventArgs
3597 internal UserTrackPointEventArgs(Activity activity, string key, object args)
3599 if (null == activity)
3600 throw new ArgumentNullException("activity");
3602 _activity = activity;
3604 // args may be null, user code can send non null value
3606 _eventType = WorkflowEventInternal.UserTrackPoint;
3610 internal Activity Activity
3612 get { return _activity; }
3617 get { return _key; }
3620 internal object Args
3622 get { return _args; }
3626 private void FireUserTrackPoint(Activity activity, string key, object args)
3628 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3629 if (null != localWorkflowExecutionEvent)
3630 localWorkflowExecutionEvent(this, new UserTrackPointEventArgs(activity, key, args));
3634 #endregion Internal Events
3637 internal class ScheduleWork : IDisposable
3639 internal class ScheduleInfo
3641 public bool scheduleWork;
3642 public WorkflowExecutor executor;
3643 public bool suppress;
3644 public ScheduleInfo(WorkflowExecutor executor, bool suppress)
3646 this.suppress = suppress;
3647 scheduleWork = false;
3648 this.executor = executor;
3652 protected static ScheduleInfo scheduleInfo;
3653 protected ScheduleInfo oldValue;
3655 public ScheduleWork(WorkflowExecutor executor)
3657 oldValue = scheduleInfo;
3658 scheduleInfo = new ScheduleInfo(executor, false);
3661 public ScheduleWork(WorkflowExecutor executor, bool suppress)
3663 oldValue = scheduleInfo;
3664 scheduleInfo = new ScheduleInfo(executor, suppress);
3667 static public bool NeedsService
3671 // Debug.Assert(ScheduleWork.scheduleInfo != null);
3672 // return ScheduleWork.scheduleInfo.scheduleWork;
3676 Debug.Assert(ScheduleWork.scheduleInfo != null);
3677 Debug.Assert(value == true || ScheduleWork.scheduleInfo.scheduleWork == false); // never go from true to false
3678 ScheduleWork.scheduleInfo.scheduleWork = value;
3681 static public WorkflowExecutor Executor
3685 // Debug.Assert(ScheduleWork.scheduleInfo != null);
3686 // return ScheduleWork.scheduleInfo.executor;
3690 Debug.Assert(ScheduleWork.scheduleInfo != null);
3691 ScheduleWork.scheduleInfo.executor = value;
3694 #region IDisposable Members
3696 public virtual void Dispose()
3698 if ((scheduleInfo.scheduleWork) && (!scheduleInfo.suppress))
3700 scheduleInfo.executor.RequestHostingService();
3702 scheduleInfo = oldValue;