2 using System.Globalization;
3 using System.Collections.Generic;
4 using System.Diagnostics;
6 using System.Transactions;
7 using System.Workflow.ComponentModel;
9 namespace System.Workflow.Runtime
13 // Only one instance of this type is used for a workflow instance.
19 // state to be persisted for the scheduler
20 internal static DependencyProperty HighPriorityEntriesQueueProperty = DependencyProperty.RegisterAttached("HighPriorityEntriesQueue", typeof(Queue<SchedulableItem>), typeof(Scheduler));
21 internal static DependencyProperty NormalPriorityEntriesQueueProperty = DependencyProperty.RegisterAttached("NormalPriorityEntriesQueue", typeof(Queue<SchedulableItem>), typeof(Scheduler));
22 Queue<SchedulableItem> highPriorityEntriesQueue;
23 Queue<SchedulableItem> normalPriorityEntriesQueue;
25 // non-persisted state for the scheduler
26 WorkflowExecutor rootWorkflowExecutor;
30 bool abortOrTerminateRequested;
31 Queue<SchedulableItem> transactedEntries;
32 object syncObject = new object();
38 // loading with some state
39 public Scheduler(WorkflowExecutor rootExec, bool canRun)
41 this.rootWorkflowExecutor = rootExec;
42 this.threadRequested = false;
44 // canRun is true if normal creation
45 // false if loading from a persisted state. Will be set to true later at ResumeOnIdle
48 this.highPriorityEntriesQueue = (Queue<SchedulableItem>)rootExec.RootActivity.GetValue(Scheduler.HighPriorityEntriesQueueProperty);
49 this.normalPriorityEntriesQueue = (Queue<SchedulableItem>)rootExec.RootActivity.GetValue(Scheduler.NormalPriorityEntriesQueueProperty);
50 if (this.highPriorityEntriesQueue == null)
52 this.highPriorityEntriesQueue = new Queue<SchedulableItem>();
53 rootExec.RootActivity.SetValue(Scheduler.HighPriorityEntriesQueueProperty, this.highPriorityEntriesQueue);
55 if (this.normalPriorityEntriesQueue == null)
57 this.normalPriorityEntriesQueue = new Queue<SchedulableItem>();
58 rootExec.RootActivity.SetValue(Scheduler.NormalPriorityEntriesQueueProperty, this.normalPriorityEntriesQueue);
61 this.empty = ((this.normalPriorityEntriesQueue.Count == 0) && (this.highPriorityEntriesQueue.Count == 0));
66 #region Misc properties
68 public override string ToString()
70 return "Scheduler('" + ((Activity)this.RootWorkflowExecutor.WorkflowDefinition).QualifiedName + "')";
73 protected WorkflowExecutor RootWorkflowExecutor
75 get { return this.rootWorkflowExecutor; }
78 public bool IsStalledNow
99 internal bool AbortOrTerminateRequested
103 return abortOrTerminateRequested;
107 abortOrTerminateRequested = value;
111 #endregion Misc properties
119 this.RootWorkflowExecutor.ProcessQueuedEvents();
121 SchedulableItem item = GetItemToRun();
122 bool runningItem = false;
124 // no ready work to run... go away
128 Activity itemActivity = null;
129 Exception exp = null;
131 TransactionalProperties transactionalProperties = null;
132 int contextId = item.ContextId;
134 // This function gets the root or enclosing while-loop activity
135 Activity contextActivity = this.RootWorkflowExecutor.GetContextActivityForId(contextId);
136 if (contextActivity == null)
137 throw new InvalidOperationException(ExecutionStringManager.InvalidExecutionContext);
139 // This is the activity corresponding to the item's ActivityId
140 itemActivity = contextActivity.GetActivityByName(item.ActivityId);
141 using (new ServiceEnvironment(itemActivity))
144 bool ignoreFinallyBlock = false;
149 // set up the item transactional context if necessary
151 Debug.Assert(itemActivity != null, "null itemActivity");
152 if (itemActivity == null)
153 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InvalidActivityName, item.ActivityId));
155 Activity atomicActivity = null;
156 if (this.RootWorkflowExecutor.IsActivityInAtomicContext(itemActivity, out atomicActivity))
158 transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(WorkflowExecutor.TransactionalPropertiesProperty);
159 // If we've aborted for any reason stop now!
160 // If we attempt to enter a new TransactionScope the com+ context will get corrupted
162 if (!WorkflowExecutor.CheckAndProcessTransactionAborted(transactionalProperties))
164 if (transactionalProperties.TransactionScope == null)
166 // Use TimeSpan.Zero so scope will not create timeout independent of the transaction
167 // Use EnterpriseServicesInteropOption.Full to flow transaction to COM+
168 transactionalProperties.TransactionScope =
169 new TransactionScope(transactionalProperties.Transaction, TimeSpan.Zero, EnterpriseServicesInteropOption.Full);
171 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
172 "Workflow Runtime: Scheduler: instanceId: " + this.RootWorkflowExecutor.InstanceIdString +
173 "Entered into TransactionScope, Current atomic acitivity " + atomicActivity.Name);
181 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Running scheduled entry: {1}", this.RootWorkflowExecutor.InstanceIdString, item.ToString());
183 // running any entry implicitly changes some state of the workflow instance
184 this.RootWorkflowExecutor.stateChangedSincePersistence = true;
186 item.Run(this.RootWorkflowExecutor);
190 if (WorkflowExecutor.IsIrrecoverableException(e))
192 ignoreFinallyBlock = true;
197 if (transactionalProperties != null)
198 transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed;
204 if (!ignoreFinallyBlock)
207 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Done with running scheduled entry: {1}", this.RootWorkflowExecutor.InstanceIdString, item.ToString());
214 this.RootWorkflowExecutor.ExceptionOccured(exp, itemActivity == null ? contextActivity : itemActivity, null);
223 private SchedulableItem GetItemToRun()
225 SchedulableItem ret = null;
227 lock (this.syncObject)
229 bool workToDo = false;
230 if ((this.highPriorityEntriesQueue.Count > 0) || (this.normalPriorityEntriesQueue.Count > 0))
234 // If an abort or termination of the workflow has been requested,
235 // then the workflow should try to terminate ASAP. Even transaction scopes
236 // in progress shouldn't be executed to completion. (Ref: 16534)
237 if (this.AbortOrTerminateRequested)
241 // got work to do in the scheduler
242 else if ((this.highPriorityEntriesQueue.Count > 0))
244 ret = this.highPriorityEntriesQueue.Dequeue();
246 else if (this.CanRun)
248 // the scheduler can run right now
251 // pick an entry to run
253 if (((IWorkflowCoreRuntime)this.RootWorkflowExecutor).CurrentAtomicActivity == null &&
254 (this.normalPriorityEntriesQueue.Count > 0))
255 ret = this.normalPriorityEntriesQueue.Dequeue();
259 // scheduler can't run right now.. even though there is ready work
260 // do nothing in the scheduler
267 // no ready work to do in the scheduler...
268 // we are gonna return the thread back
272 // set it to true only iff there is something to run
273 this.threadRequested = (ret != null);
278 // This method should be called only after we have determined that
279 // this instance can start running now
286 // There is scheduled work
287 // ask the threadprovider for a thread
288 this.RootWorkflowExecutor.ScheduleForWork();
292 // This method should be called only after we have determined that
293 // this instance can start running now
294 public void ResumeIfRunnable()
301 // There is scheduled work
302 // ask the threadprovider for a thread
303 this.RootWorkflowExecutor.ScheduleForWork();
308 #region Schedule work
310 public void ScheduleItem(SchedulableItem s, bool isInAtomicTransaction, bool transacted)
312 lock (this.syncObject)
314 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Scheduling entry: {1}", this.RootWorkflowExecutor.InstanceIdString, s.ToString());
315 // SchedulableItems in AtomicTransaction has higher priority
316 Queue<SchedulableItem> q = isInAtomicTransaction ? this.highPriorityEntriesQueue : this.normalPriorityEntriesQueue;
321 if (transactedEntries == null)
322 transactedEntries = new Queue<SchedulableItem>();
323 transactedEntries.Enqueue(s);
326 if (!this.threadRequested)
330 this.RootWorkflowExecutor.ScheduleForWork();
331 this.threadRequested = true;
338 #endregion Schedule work
340 #region psuedo-transacted support
342 public void PostPersist()
344 transactedEntries = null;
347 public void Rollback()
349 if (transactedEntries != null && transactedEntries.Count > 0)
351 // make a list of non-transacted entries
352 // @undone: bmalhi: transacted entries only on priority-0
354 IEnumerator<SchedulableItem> e = this.normalPriorityEntriesQueue.GetEnumerator();
355 Queue<SchedulableItem> newScheduled = new Queue<SchedulableItem>();
358 if (!transactedEntries.Contains(e.Current))
359 newScheduled.Enqueue(e.Current);
362 // clear the scheduled items
363 this.normalPriorityEntriesQueue.Clear();
365 // schedule the non-transacted items back
366 e = newScheduled.GetEnumerator();
368 this.normalPriorityEntriesQueue.Enqueue(e.Current);
370 transactedEntries = null;
374 #endregion psuedo-transacted support