Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Workflow.Runtime / WorkflowExecutor.cs
1 #pragma warning disable 1634, 1691
2 using System;
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;
10 using System.IO;
11 using System.Reflection;
12 using System.Text;
13 using System.Threading;
14 using System.Xml;
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;
20
21 namespace System.Workflow.Runtime
22 {
23     /// <remarks>
24     /// The runtime object that represents the schedule.
25     /// </remarks>
26     internal sealed class WorkflowExecutor : IWorkflowCoreRuntime, IServiceProvider, ISupportInterop
27     {
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));
38
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));
45
46         #region Data Members - Please keep all the data here
47
48         internal Activity currentAtomicActivity;
49         private ManualResetEvent atomicActivityEvent;
50
51         private Hashtable completedContextActivities = new Hashtable();
52
53         Activity rootActivity;
54
55         WorkflowRuntime _runtime;                            // hosting environment
56
57         private VolatileResourceManager _resourceManager = new VolatileResourceManager();
58         bool _isInstanceValid;
59         private bool isInstanceIdle;
60         Activity _lastExecutingActivity;
61
62         private Scheduler schedulingContext;
63         private WorkflowQueuingService qService;
64
65         private Exception thrownException;
66         private string activityThrowingException;
67
68         private List<SchedulerLockGuardInfo> eventsToFireList = new List<SchedulerLockGuardInfo>();
69
70         internal bool stateChangedSincePersistence;
71
72         private WorkflowInstance _workflowInstance;
73         private Guid workflowInstanceId;
74         private string workflowIdString = null;
75
76         WorkflowStateRollbackService workflowStateRollbackService;
77
78         private InstanceLock _executorLock;
79         private InstanceLock _msgDeliveryLock;
80         private InstanceLock _schedulerLock;
81         private TimerEventSubscriptionCollection _timerQueue;
82         private volatile Activity _workflowDefinition;   // dependency property cache
83
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
88
89         #region Ctors
90
91         static WorkflowExecutor()
92         {
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);
99
100             // registered by Scheduler
101             DependencyProperty.RegisterAsKnown(Scheduler.NormalPriorityEntriesQueueProperty, (byte)61, DependencyProperty.PropertyValidity.Uninitialize);
102             DependencyProperty.RegisterAsKnown(Scheduler.HighPriorityEntriesQueueProperty, (byte)62, DependencyProperty.PropertyValidity.Uninitialize);
103
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);
113
114             //registered by the definition dispenser
115             DependencyProperty.RegisterAsKnown(WorkflowDefinitionDispenser.WorkflowDefinitionHashCodeProperty, (byte)80, DependencyProperty.PropertyValidity.Reexecute);
116
117
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);
123
124             workflowDebuggingDisabled = disableWorkflowDebugging.Enabled;
125         }
126
127         internal WorkflowExecutor(Guid instanceId)
128         {
129             this._isInstanceValid = false;
130             this._executorLock = LockFactory.CreateWorkflowExecutorLock(instanceId);
131             this._msgDeliveryLock = LockFactory.CreateWorkflowMessageDeliveryLock(instanceId);
132             this.stateChangedSincePersistence = true;
133
134             // If DisableWorkflowDebugging switch is turned off create WorkflowDebuggerService
135             if (!workflowDebuggingDisabled)
136                 this._workflowDebuggerService = new WorkflowDebuggerService(this);
137         }
138
139         // Initialize for the root schedule
140         internal void Initialize(Activity rootActivity, WorkflowExecutor invokerExec, string invokeActivityID, Guid instanceId, IDictionary<string, object> namedArguments, WorkflowInstance workflowInstance)
141         {
142             this.rootActivity = rootActivity;
143             this.InstanceId = instanceId;
144
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;
153
154             // set workflow executor
155             this.rootActivity.SetValue(WorkflowExecutor.WorkflowExecutorProperty, this);
156
157             // initialize the root activity
158             RefreshWorkflowDefinition();
159             Activity workflowDefinition = this.WorkflowDefinition;
160             if (workflowDefinition == null)
161                 throw new InvalidOperationException("workflowDefinition");
162
163             ((IDependencyObjectAccessor)this.rootActivity).InitializeActivatingInstanceForRuntime(null, this);
164             this.rootActivity.FixUpMetaProperties(workflowDefinition);
165             _runtime = workflowInstance.WorkflowRuntime;
166
167             if (invokerExec != null)
168             {
169                 List<string> calleeBase = new List<string>();
170                 TrackingCallingState parentTCS = (TrackingCallingState)invokerExec.rootActivity.GetValue(WorkflowExecutor.TrackingCallingStateProperty);
171                 if ((parentTCS != null) && (parentTCS.CallerActivityPathProxy != null))
172                 {
173                     foreach (string qualifiedID in parentTCS.CallerActivityPathProxy)
174                         calleeBase.Add(qualifiedID);
175                 }
176                 calleeBase.Add(invokeActivityID);
177
178                 //
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;
188                 else
189                     trackingCallingState.CallerParentContextGuid = ((ActivityExecutionContextInfo)ContextActivityUtils.ContextActivity(invokerExec.CurrentActivity.Parent).GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid;
190                 this.rootActivity.SetValue(WorkflowExecutor.TrackingCallingStateProperty, trackingCallingState);
191             }
192
193             _setInArgsOnCompanion(namedArguments);
194
195             this.schedulingContext = new Scheduler(this, true);
196             this._schedulerLock = LockFactory.CreateWorkflowSchedulerLock(this.InstanceId);
197
198             qService = new WorkflowQueuingService(this);
199
200             _workflowInstance = workflowInstance;
201
202             TimerQueue = new TimerEventSubscriptionCollection(this, this.InstanceId);
203
204             // register the dynamic activity
205             using (new ServiceEnvironment(this.rootActivity))
206             {
207                 using (SetCurrentActivity(this.rootActivity))
208                 {
209                     this.RegisterDynamicActivity(this.rootActivity, false);
210                 }
211             }
212         }
213
214         internal void RegisterWithRuntime(WorkflowRuntime workflowRuntime)
215         {
216             _isInstanceValid = true;
217             _runtime = workflowRuntime;
218             using (new ServiceEnvironment(this.rootActivity))
219             {
220                 using (SetCurrentActivity(this.rootActivity))
221                 {
222                     using (ActivityExecutionContext executionContext = new ActivityExecutionContext(this.rootActivity, true))
223                         executionContext.InitializeActivity(this.rootActivity);
224                 }
225
226                 //
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);
230
231                 //
232                 // Fire first events
233                 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Creating);
234             }
235         }
236
237         // Used to recreate the root schedule executor from its persisted state
238         internal void Reload(Activity rootActivity, WorkflowInstance workflowInstance)
239         {
240             _workflowInstance = workflowInstance;
241             ReloadHelper(rootActivity);
242         }
243
244         internal void ReRegisterWithRuntime(WorkflowRuntime workflowRuntime)
245         {
246             using (new SchedulerLockGuard(this._schedulerLock, this))
247             {
248                 _isInstanceValid = true;
249                 _runtime = workflowRuntime;
250                 using (new ServiceEnvironment(this.rootActivity))
251                 {
252                     this._runtime.WorkflowExecutorCreated(this, true);
253
254                     TimerQueue.Executor = this;
255                     TimerQueue.ResumeDelivery();
256
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;
261
262                     FireWorkflowExecutionEvent(this, WorkflowEventInternal.Loading);
263                 }
264             }
265         }
266
267         internal void Registered(bool isActivation)
268         {
269             using (ScheduleWork work = new ScheduleWork(this))
270             {
271                 this.Scheduler.ResumeIfRunnable();
272             }
273             if (isActivation)
274                 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Created);
275             else
276                 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Loaded);
277         }
278
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)
283         {
284             _workflowInstance = previousWorkflowExecutor.WorkflowInstance;
285             ReloadHelper(rootActivity);
286             // mark instance as valid now
287             IsInstanceValid = true;
288             _runtime = runtime;
289             this._runtime.WorkflowExecutorCreated(this, true);
290
291             TimerQueue.Executor = this;
292             TimerQueue.ResumeDelivery();
293
294             _executorLock = previousWorkflowExecutor._executorLock;
295             _msgDeliveryLock = previousWorkflowExecutor._msgDeliveryLock;
296             _schedulerLock = previousWorkflowExecutor._schedulerLock;
297             ScheduleWork.Executor = this;
298         }
299
300         // Used to recreate the root schedule executor from its persisted state
301         private void ReloadHelper(Activity rootActivity)
302         {
303             // assign activity state
304             this.rootActivity = rootActivity;
305             this.InstanceId = (Guid)rootActivity.GetValue(WorkflowInstanceIdProperty);
306
307             // set workflow executor
308             this.rootActivity.SetValue(WorkflowExecutor.WorkflowExecutorProperty, this);
309
310             this._schedulerLock = LockFactory.CreateWorkflowSchedulerLock(this.InstanceId);
311
312             this.schedulingContext = new Scheduler(this, false);
313             this.qService = new WorkflowQueuingService(this);
314
315             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Loading instance {0}", this.InstanceIdString);
316             DiagnosticStackTrace("load request");
317
318             using (new ServiceEnvironment(this.rootActivity))
319             {
320
321                 // check if this instance can be loaded
322                 switch (this.WorkflowStatus)
323                 {
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);
329
330                     default:
331                         break;
332                 }
333
334                 // new nonSerialized members
335                 _resourceManager = new VolatileResourceManager();
336                 _runtime = _workflowInstance.WorkflowRuntime;
337
338                 // register all dynamic activities for loading
339                 Queue<Activity> dynamicActivitiesQueue = new Queue<Activity>();
340                 dynamicActivitiesQueue.Enqueue(this.rootActivity);
341                 while (dynamicActivitiesQueue.Count > 0)
342                 {
343                     Activity dynamicActivity = dynamicActivitiesQueue.Dequeue();
344                     ((IDependencyObjectAccessor)dynamicActivity).InitializeInstanceForRuntime(this);
345                     this.RegisterDynamicActivity(dynamicActivity, true);
346
347                     IList<Activity> nestedDynamicActivities = (IList<Activity>)dynamicActivity.GetValue(Activity.ActiveExecutionContextsProperty);
348                     if (nestedDynamicActivities != null)
349                     {
350                         foreach (Activity nestedDynamicActivity in nestedDynamicActivities)
351                             dynamicActivitiesQueue.Enqueue(nestedDynamicActivity);
352                     }
353                 }
354             }
355
356             this.isInstanceIdle = (bool)this.rootActivity.GetValue(IsIdleProperty);
357             RefreshWorkflowDefinition();
358         }
359
360         private void _setInArgsOnCompanion(IDictionary<string, object> namedInArguments)
361         {
362             // Do parameter property assignments.
363             if (namedInArguments != null)
364             {
365                 foreach (string arg in namedInArguments.Keys)
366                 {
367
368                     PropertyInfo propertyInfo = this.WorkflowDefinition.GetType().GetProperty(arg);
369
370                     if (propertyInfo != null && propertyInfo.CanWrite)
371                     {
372                         try
373                         {
374                             propertyInfo.SetValue(this.rootActivity, namedInArguments[arg], null);
375                         }
376                         catch (ArgumentException e)
377                         {
378                             throw new ArgumentException(ExecutionStringManager.InvalidWorkflowParameterValue, arg, e);
379                         }
380                     }
381                     else
382                         throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.SemanticErrorInvalidNamedParameter, ((Activity)this.WorkflowDefinition).Name, arg));
383                 }
384             }
385         }
386         #endregion Ctors
387
388         #region Misc properties and methods
389
390         internal TrackingCallingState TrackingCallingState
391         {
392             get
393             {
394                 return (TrackingCallingState)this.rootActivity.GetValue(WorkflowExecutor.TrackingCallingStateProperty);
395             }
396         }
397
398         internal WorkflowRuntime WorkflowRuntime
399         {
400             get
401             {
402                 return _runtime;
403             }
404         }
405         internal bool IsInstanceValid
406         {
407             get { return _isInstanceValid; }
408             set
409             {
410                 if (!value)
411                 {
412                     this.ResourceManager.ClearAllBatchedWork();
413                     InstanceLock.AssertIsLocked(this._schedulerLock);
414                     InstanceLock.AssertIsLocked(this._msgDeliveryLock);
415                 }
416                 _isInstanceValid = value;
417             }
418         }
419
420         internal bool IsIdle
421         {
422             get
423             {
424                 return this.isInstanceIdle;
425             }
426             set
427             {
428                 using (InstanceLock.InstanceLockGuard messageDeliveryLockGuard = this.MessageDeliveryLock.Enter())
429                 {
430                     try
431                     {
432                         this.isInstanceIdle = value;
433                         this.RootActivity.SetValue(WorkflowExecutor.IsIdleProperty, value);
434                     }
435                     finally
436                     {
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
440                         // waiting threads
441                         if (this.IsIdle)
442                             messageDeliveryLockGuard.Pulse();
443                     }
444                 }
445             }
446         }
447
448         internal string AdditionalInformation
449         {
450             get { return (string)this.rootActivity.GetValue(SuspendOrTerminateInfoProperty); }
451         }
452
453         public WorkBatchCollection BatchCollection
454         {
455             get
456             {
457                 return _resourceManager.BatchCollection;
458             }
459         }
460
461         internal VolatileResourceManager ResourceManager
462         {
463             get
464             {
465                 return _resourceManager;
466             }
467         }
468
469         internal Activity WorkflowDefinition
470         {
471             get
472             {
473                 Debug.Assert(_workflowDefinition != null, "WorkflowDefinition cannot be null.");
474                 return _workflowDefinition;
475             }
476         }
477
478         private void RefreshWorkflowDefinition()
479         {
480             Activity tempDefinition = (Activity)this.rootActivity.GetValue(Activity.WorkflowDefinitionProperty);
481             Debug.Assert(tempDefinition != null, "WorkflowDefinition cannot be null.");
482
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());
488
489             _workflowDefinition = tempDefinition;
490         }
491
492         internal Activity RootActivity
493         {
494             get
495             {
496                 return this.rootActivity;
497             }
498         }
499
500         internal Guid InstanceId
501         {
502             get
503             {
504                 return workflowInstanceId;
505             }
506             private set
507             {
508                 workflowInstanceId = value;
509             }
510         }
511
512         internal string InstanceIdString
513         {
514             get
515             {
516                 if (workflowIdString == null)
517                     workflowIdString = this.InstanceId.ToString();
518                 return workflowIdString;
519             }
520         }
521
522
523         internal InstanceLock MessageDeliveryLock
524         {
525             get
526             {
527                 return _msgDeliveryLock;
528             }
529         }
530
531         internal InstanceLock ExecutorLock
532         {
533             get
534             {
535                 return _executorLock;
536             }
537         }
538
539         internal WorkflowStateRollbackService WorkflowStateRollbackService
540         {
541             get
542             {
543                 if (this.workflowStateRollbackService == null)
544                     this.workflowStateRollbackService = new WorkflowStateRollbackService(this);
545                 return this.workflowStateRollbackService;
546             }
547         }
548
549         internal WorkflowInstance WorkflowInstance
550         {
551             get
552             {
553                 System.Diagnostics.Debug.Assert(this._workflowInstance != null, "WorkflowInstance property should not be called before the proxy is initialized.");
554                 return this._workflowInstance;
555             }
556         }
557
558         internal void Start()
559         {
560             using (ScheduleWork work = new ScheduleWork(this))
561             {
562                 using (this.ExecutorLock.Enter())
563                 {
564                     if (this.WorkflowStatus != WorkflowStatus.Created)
565                         throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.CannotStartInstanceTwice, this.InstanceId));
566
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))
573                     {
574                         FireWorkflowExecutionEvent(this, WorkflowEventInternal.Starting);
575                         try
576                         {
577                             using (ActivityExecutionContext executionContext = new ActivityExecutionContext(this.rootActivity, true))
578                             {
579                                 // make sure the scheduler is able to run
580                                 this.schedulingContext.CanRun = true;
581
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))
586                                 {
587                                     executionContext.ExecuteActivity(this.rootActivity);
588                                 }
589                             }
590                         }
591                         catch (Exception e)
592                         {
593                             Terminate(e.Message);
594                             throw;
595                         }
596                         FireWorkflowExecutionEvent(this, WorkflowEventInternal.Started);
597
598                     }
599                 }
600             }
601         }
602
603         internal Activity CurrentActivity
604         {
605             get { return _lastExecutingActivity; }
606             set { _lastExecutingActivity = value; }
607         }
608
609         internal Hashtable CompletedContextActivities
610         {
611             get { return this.completedContextActivities; }
612             set { this.completedContextActivities = value; }
613         }
614
615
616         private int GetNewContextId()
617         {
618             int conextId = (int)this.rootActivity.GetValue(WorkflowExecutor.ContextIdProperty) + 1;
619             this.rootActivity.SetValue(WorkflowExecutor.ContextIdProperty, conextId);
620             return conextId;
621         }
622
623         internal List<SchedulerLockGuardInfo> EventsToFireList
624         {
625             get
626             {
627                 return eventsToFireList;
628             }
629         }
630
631         private void FireEventAfterSchedulerLockDrop(WorkflowEventInternal workflowEventInternal, object eventInfo)
632         {
633             eventsToFireList.Add(new SchedulerLockGuardInfo(this, workflowEventInternal, eventInfo));
634         }
635
636         private void FireEventAfterSchedulerLockDrop(WorkflowEventInternal workflowEventInternal)
637         {
638             eventsToFireList.Add(new SchedulerLockGuardInfo(this, workflowEventInternal));
639         }
640
641         #endregion Misc properties and methods
642
643         #region Scheduler Related
644
645         // asks the hosting env threadProvider for a thread
646         internal void ScheduleForWork()
647         {
648             this.IsIdle = false;
649
650             if (this.IsInstanceValid)
651                 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Runnable);
652
653             ScheduleWork.NeedsService = true;
654         }
655
656         internal void RequestHostingService()
657         {
658             WorkflowRuntime.SchedulerService.Schedule(this.RunSome, this.InstanceId);
659         }
660
661         internal void DeliverTimerSubscriptions()
662         {
663             using (ScheduleWork work = new ScheduleWork(this))
664             {
665                 using (this._executorLock.Enter())
666                 {
667                     if (this.IsInstanceValid)
668                     {
669                         using (this.MessageDeliveryLock.Enter())
670                         {
671                             using (new ServiceEnvironment(this.rootActivity))
672                             {
673                                 if (!this.IsInstanceValid)
674                                     return;
675
676                                 TimerEventSubscriptionCollection queue = TimerQueue;
677                                 bool done = false;
678                                 while (!done)
679                                 {
680                                     lock (queue.SyncRoot)
681                                     {
682                                         TimerEventSubscription sub = queue.Peek();
683                                         if (sub == null || sub.ExpiresAt > DateTime.UtcNow)
684                                         {
685                                             done = true;
686                                         }
687                                         else
688                                         {
689                                             WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Delivering timer subscription for instance {0}", this.InstanceIdString);
690                                             stateChangedSincePersistence = true;
691                                             lock (qService.SyncRoot)
692                                             {
693                                                 if (qService.Exists(sub.QueueName))
694                                                 {
695                                                     qService.EnqueueEvent(sub.QueueName, sub.SubscriptionId);
696                                                 }
697                                             }
698                                             queue.Dequeue();
699                                         }
700                                     }
701                                 }
702                             }
703                         }
704                     }
705                 }
706             }
707         }
708
709         // call from the threadProvider about the availability of a thread.
710         internal void RunSome(object ignored)
711         {
712             using (ScheduleWork work = new ScheduleWork(this))
713             {
714                 using (new WorkflowTraceTransfer(this.InstanceId))
715                 {
716                     using (new SchedulerLockGuard(this._schedulerLock, this))
717                     {
718                         using (new ServiceEnvironment(this.rootActivity))
719                         {
720                             // check if this is a valid in-memory instance
721                             if (!this.IsInstanceValid)
722                                 return;
723
724                             // check if instance already done
725                             if ((this.rootActivity.ExecutionStatus == ActivityExecutionStatus.Closed) || (WorkflowStatus.Completed == this.WorkflowStatus) || (WorkflowStatus.Terminated == this.WorkflowStatus))
726                                 return;
727
728                             bool ignoreFinallyBlock = false;
729
730                             //
731                             // For V1 we don't support flow through transaction on the service thread
732                             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Suppress))
733                             {
734                                 try
735                                 {
736                                     FireWorkflowExecutionEvent(this, WorkflowEventInternal.Executing);
737                                     // run away ... run away...
738                                     this.RunScheduler();
739                                 }
740                                 catch (Exception e)
741                                 {
742                                     if (WorkflowExecutor.IsIrrecoverableException(e))
743                                     {
744                                         ignoreFinallyBlock = true;
745                                         throw;
746                                     }
747                                     else
748                                     {
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;
752                                     }
753                                 }
754                                 finally
755                                 {
756                                     if (!ignoreFinallyBlock)
757                                     {
758                                         FireWorkflowExecutionEvent(this, WorkflowEventInternal.NotExecuting);
759                                     }
760                                 }
761                                 scope.Complete();
762                             }
763                         }
764                     }
765                 }
766             }
767         }
768
769         // this method is called with the scheduler lock held
770         private void RunScheduler()
771         {
772             InstanceLock.AssertIsLocked(this._schedulerLock);
773
774             // run away ... run away...
775             try
776             {
777                 this.Scheduler.Run();
778             }
779             finally
780             {
781                 this.IsIdle = true;
782             }
783
784             if (!this.IsInstanceValid)
785                 return;
786
787             if (this.WorkflowStateRollbackService.IsInstanceStateRevertRequested)
788             {
789                 //
790                 // Protect against message delivery while reverting
791                 using (MessageDeliveryLock.Enter())
792                 {
793                     this.WorkflowStateRollbackService.RevertToCheckpointState();
794                     return;
795                 }
796             }
797
798             if (this.Scheduler.IsStalledNow)
799             {
800                 // the instance has no ready work
801
802                 // Protect against the host accessing DPs.
803                 using (this.MessageDeliveryLock.Enter())
804                 {
805                     if (this.rootActivity.ExecutionStatus != ActivityExecutionStatus.Closed)
806                     {
807                         this.ProcessQueuedEvents(); // deliver any outstanding queued events before persisting
808                         if (this.Scheduler.IsStalledNow)
809                         {
810                             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: workflow instance '{0}' has no work.", this.InstanceIdString);
811                             FireWorkflowExecutionEvent(this, WorkflowEventInternal.SchedulerEmpty);
812
813                             FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Idle);
814
815                             WorkflowPersistenceService persistence = this.WorkflowRuntime.WorkflowPersistenceService;
816
817                             // instance is not done.. must be idle
818                             // can potentially dehydrate now..
819                             if ((persistence != null) && persistence.UnloadOnIdle(this.rootActivity))
820                             {
821                                 if (!this.IsInstanceValid)
822                                     return;
823
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))
827                                 {
828                                     PerformUnloading(true);
829                                     WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "WorkflowExecutor: unloaded workflow instance '{0}'.  IsInstanceValid={1}", this.InstanceIdString, IsInstanceValid);
830                                 }
831                             }
832                             else
833                             {
834                                 if (this.ResourceManager.IsBatchDirty && this.currentAtomicActivity == null)
835                                 {
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);
838                                 }
839                             }
840                         }
841                     }
842                 }
843             }
844             else
845             {
846                 // the instance has ready work but was told to stop
847                 //
848
849                 // if suspension was requested, suspend now.
850
851                 if ((bool)this.rootActivity.GetValue(WorkflowExecutor.IsSuspensionRequestedProperty))
852                 {
853                     this.SuspendOnIdle(this.AdditionalInformation);
854                     this.rootActivity.SetValue(WorkflowExecutor.IsSuspensionRequestedProperty, false);
855                 }
856             }
857
858             if (this.currentAtomicActivity != null)
859             {
860                 // Leave TransactionScope before giving up the thread                
861                 TransactionalProperties transactionalProperties = (TransactionalProperties)this.currentAtomicActivity.GetValue(TransactionalPropertiesProperty);
862                 DisposeTransactionScope(transactionalProperties);
863             }
864         }
865
866         private bool attemptedRootAECUnload = false;
867         private bool attemptedRootDispose = false;
868
869         private void DisposeRootActivity(bool aborting)
870         {
871             try
872             {
873                 if (!attemptedRootAECUnload)
874                 {
875                     attemptedRootAECUnload = true;
876                     this.RootActivity.OnActivityExecutionContextUnload(this);
877                 }
878                 if (!attemptedRootDispose)
879                 {
880                     attemptedRootDispose = true;
881                     this.RootActivity.Dispose();
882                 }
883             }
884             catch (Exception)
885             {
886                 if (!aborting)
887                 {
888                     using (_msgDeliveryLock.Enter())
889                     {
890                         this.AbortOnIdle();
891                         throw;
892                     }
893                 }
894             }
895         }
896
897
898         internal Scheduler Scheduler
899         {
900             get
901             {
902                 return this.schedulingContext;
903             }
904         }
905
906         #endregion Scheduler Related
907
908         #region IInstanceState
909
910         /// <summary>
911         /// Instance Id
912         /// </summary>
913         /// <value></value>
914         internal Guid ID
915         {
916             get { return InstanceId; }
917         }
918
919         /// <summary>
920         /// Completed status for instances clean up
921         /// </summary>
922         /// <value></value>        
923         internal WorkflowStatus WorkflowStatus
924         {
925             get { return (WorkflowStatus)this.rootActivity.GetValue(WorkflowStatusProperty); }
926             private set { this.rootActivity.SetValue(WorkflowStatusProperty, value); }
927         }
928
929         internal TimerEventSubscriptionCollection TimerQueue
930         {
931             get
932             {
933                 if (_timerQueue == null)
934                 {
935                     _timerQueue = (TimerEventSubscriptionCollection)this.rootActivity.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty);
936                     Debug.Assert(_timerQueue != null, "TimerEventSubscriptionCollection on root activity should never be null, but it was");
937                 }
938                 return _timerQueue;
939             }
940             private set
941             {
942                 _timerQueue = value;
943                 this.rootActivity.SetValue(TimerEventSubscriptionCollection.TimerCollectionProperty, _timerQueue);
944             }
945         }
946
947
948         #endregion
949
950         #region Persistence
951
952         private bool ProtectedPersist(bool unlock)
953         {
954             try
955             {
956                 // persist
957                 this.Persist(this.rootActivity, unlock, false);
958             }
959             catch (Exception e)
960             {
961                 if (WorkflowExecutor.IsIrrecoverableException(e))
962                 {
963                     throw;
964                 } //@@undone: for [....]:- we should not be running exception handler, when we are unlocking.
965                 else if (this.WorkflowStatus != WorkflowStatus.Suspended && this.IsInstanceValid)
966                 {
967                     // the persistence attempt threw an exception
968                     // lets give this exception to a scope
969                     Activity activity = FindExecutorToHandleException();
970
971                     this.Scheduler.CanRun = true;
972                     this.ExceptionOccured(e, activity, null);
973                 }
974                 else
975                 {
976                     if (this.TerminateOnIdle(WorkflowExecutor.GetNestedExceptionMessage(e)))
977                     {
978                         this.stateChangedSincePersistence = true;
979                         this.WorkflowStatus = WorkflowStatus.Terminated;
980                     }
981                 }
982                 return false;
983             }
984             return true;
985         }
986
987         private Activity FindExecutorToHandleException()
988         {
989             Activity lastExecutingActivity = this.CurrentActivity;
990             if (lastExecutingActivity == null)
991                 lastExecutingActivity = this.rootActivity;
992             return lastExecutingActivity;
993         }
994
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)
998         {
999             InstanceLock.AssertIsLocked(this._schedulerLock);
1000             Activity currentActivity = (this.CurrentActivity == null) ? dynamicActivity : this.CurrentActivity;
1001             //
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;
1005
1006             // New a ServiceEnvironment to set the current batch to be of the exec to be persisted 
1007             using (new ServiceEnvironment(currentActivity))
1008             {
1009                 try
1010                 {
1011                     // prevent the message delivery from outside
1012                     using (this.MessageDeliveryLock.Enter())
1013                     {
1014                         this.ProcessQueuedEvents(); // Must always process this queue before persisting state!
1015                         // check what has changed since last persist
1016                         //
1017                         if (this.ResourceManager.IsBatchDirty)
1018                         {
1019                             // if there is work in the batch, persist the state to be consistent
1020                             this.stateChangedSincePersistence = true;
1021                         }
1022                         else
1023                         {
1024                             // no work in the batch...
1025                             if (!this.stateChangedSincePersistence && !unlock)
1026                             {
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);
1029                                 return;
1030                             }
1031                         }
1032
1033                         // prepare the state for persistence
1034                         //
1035                         this.PrePersist();
1036
1037                         if (WorkflowStatus.Completed == WorkflowStatus)
1038                         {
1039                             // Any remaining messages in queues are zombie messages so move all to the pending queue
1040                             this.qService.MoveAllMessagesToPendingQueue();
1041                         }
1042                         // give the state to the persistence provider
1043                         WorkflowPersistenceService persistence = this.WorkflowRuntime.WorkflowPersistenceService;
1044
1045                         // Create a transient batch for Persistence Service.
1046                         currentActivity.SetValue(TransientBatchProperty, _resourceManager.BatchCollection.GetTransientBatch());
1047
1048                         bool firedPersistingEvent = false;
1049
1050                         if (persistence != null)
1051                         {
1052                             foreach (Activity completedContextActivity in this.completedContextActivities.Values)
1053                             {
1054                                 // Save the committing activity 
1055                                 completedContextActivity.SetValue(WorkflowInstanceIdProperty, this.InstanceId);
1056
1057                                 if (!firedPersistingEvent)
1058                                 {
1059                                     FireWorkflowExecutionEvent(this, WorkflowEventInternal.Persisting);
1060                                     firedPersistingEvent = true;
1061                                 }
1062
1063                                 persistence.SaveCompletedContextActivity(completedContextActivity);
1064                                 completedContextActivity.Dispose();
1065                             }
1066
1067                             if (this.stateChangedSincePersistence)
1068                             {
1069                                 if (!firedPersistingEvent)
1070                                 {
1071                                     FireWorkflowExecutionEvent(this, WorkflowEventInternal.Persisting);
1072                                     firedPersistingEvent = true;
1073                                 }
1074
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);
1077                             }
1078                             else if (unlock)
1079                             {
1080                                 persistence.UnlockWorkflowInstanceState(this.rootActivity);
1081                             }
1082                         }
1083
1084                         if (unlock)
1085                         {
1086                             DisposeRootActivity(false);
1087                         }
1088
1089                         // commit 
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()))
1094                         {
1095
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);
1098                         }
1099
1100                         if (firedPersistingEvent)
1101                             FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Persisted);
1102
1103                         // post-persist
1104                         //
1105                         this.stateChangedSincePersistence = false;
1106                         this.PostPersist();
1107                         //
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)
1111                         {
1112                             FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Completed);
1113                             this.IsInstanceValid = false;
1114                         }
1115                     }
1116                 }
1117                 catch (PersistenceException e)
1118                 {
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);
1121                     throw;
1122                 }
1123                 catch (Exception e)
1124                 {
1125                     if (WorkflowExecutor.IsIrrecoverableException(e))
1126                     {
1127                         throw;
1128                     }
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);
1132                 }
1133                 finally
1134                 {
1135                     //Flush the transient Batch
1136                     currentActivity.SetValue(TransientBatchProperty, null);
1137                 }
1138             }
1139         }
1140
1141         /// <summary>
1142         /// There is always at least 1 BatchCollection (at root),
1143         /// check if any batch contains any work item
1144         /// </summary>
1145         /// <returns></returns>
1146         private bool HasNonEmptyWorkBatch()
1147         {
1148             foreach (WorkBatch workBatch in ResourceManager.BatchCollection.Values)
1149             {
1150                 if (workBatch.Count > 0)
1151                     return true;
1152             }
1153             return false;
1154         }
1155
1156         /// <summary>
1157         /// PrePersist
1158         /// 
1159         /// Signal to prepare the state for persistence.
1160         /// </summary>
1161         private void PrePersist()
1162         {
1163             //
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))
1168             {
1169                 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Completing);
1170                 this.WorkflowStatus = WorkflowStatus.Completed;
1171             }
1172
1173             switch (this.WorkflowStatus)
1174             {
1175                 case WorkflowStatus.Running:
1176                     this.rootActivity.SetValue(IsBlockedProperty, this.Scheduler.IsStalledNow);
1177                     break;
1178                 case WorkflowStatus.Suspended:
1179                 case WorkflowStatus.Completed:
1180                 case WorkflowStatus.Terminated:
1181                 case WorkflowStatus.Created:
1182                     this.rootActivity.SetValue(IsBlockedProperty, false);
1183                     break;
1184                 default:
1185                     Debug.Assert(false, "Unknown WorkflowStatus");
1186                     break;
1187             }
1188
1189             qService.PrePersist();
1190         }
1191
1192         private void PostPersist()
1193         {
1194             qService.PostPersist(true);
1195             if (this.Scheduler != null)
1196                 this.Scheduler.PostPersist();
1197             this.completedContextActivities.Clear();
1198         }
1199
1200         private void Rollback(WorkflowStatus oldStatus)
1201         {
1202             this.WorkflowStatus = oldStatus;
1203
1204             if (this.Scheduler != null)
1205                 this.Scheduler.Rollback();
1206         }
1207
1208         #endregion
1209
1210         #region MessageArrival and Query
1211
1212         internal void ProcessQueuedEvents()
1213         {
1214             using (MessageDeliveryLock.Enter())
1215             {
1216                 qService.ProcessesQueuedAsynchronousEvents();
1217             }
1218         }
1219
1220         internal void EnqueueItem(IComparable queueName, object item, IPendingWork pendingWork, Object workItem)
1221         {
1222             using (ScheduleWork work = new ScheduleWork(this))
1223             {
1224                 bool lockedScheduler = false;
1225                 if (!ServiceEnvironment.IsInServiceThread(InstanceId))
1226                     lockedScheduler = _schedulerLock.TryEnter();
1227                 try
1228                 {
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())
1232                     {
1233                         if (!this.IsInstanceValid)
1234                             throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1235
1236                         if (lockedScheduler || ServiceEnvironment.IsInServiceThread(InstanceId))
1237                         {
1238                             using (new ServiceEnvironment(this.RootActivity))
1239                             {
1240                                 qService.EnqueueEvent(queueName, item);
1241                             }
1242                         }
1243                         else
1244                         {
1245                             if (qService.SafeEnqueueEvent(queueName, item))
1246                             {
1247                                 ScheduleWork.NeedsService = true;
1248                             }
1249                         }
1250
1251                         // add work items to the current batch if exists
1252                         if (pendingWork != null)
1253                         {
1254                             IWorkBatch batch = _resourceManager.BatchCollection.GetBatch(this.rootActivity);
1255                             batch.Add(pendingWork, workItem);
1256                         }
1257
1258                         stateChangedSincePersistence = true;
1259                     }
1260                 }
1261                 finally
1262                 {
1263                     if (lockedScheduler)
1264                         _schedulerLock.Exit();
1265                 }
1266             }
1267         }
1268
1269         internal void EnqueueItemOnIdle(IComparable queueName, object item, IPendingWork pendingWork, Object workItem)
1270         {
1271             using (ScheduleWork work = new ScheduleWork(this))
1272             {
1273                 // prevent other control operations from outside
1274                 using (this._executorLock.Enter())
1275                 {
1276                     if (!this.IsInstanceValid)
1277                         throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1278
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())
1282                     {
1283                         using (new ServiceEnvironment(this.rootActivity))
1284                         {
1285
1286                             if (!this.IsInstanceValid)
1287                                 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1288
1289                             // Wait until the Scheduler is idle.
1290                             while (!this.IsIdle)
1291                             {
1292                                 messageDeliveryLockGuard.Wait();
1293                                 if (!this.IsInstanceValid)
1294                                     throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1295                             }
1296
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);
1301
1302                             try
1303                             {
1304                                 // add work items to the current batch if exists
1305                                 if (pendingWork != null)
1306                                 {
1307                                     IWorkBatch batch = (IWorkBatch)this.rootActivity.GetValue(WorkflowExecutor.TransientBatchProperty);
1308                                     batch.Add(pendingWork, workItem);
1309                                 }
1310
1311                                 stateChangedSincePersistence = true;
1312                                 qService.EnqueueEvent(queueName, item);
1313                             }
1314                             finally
1315                             {
1316                                 if (this.IsIdle)
1317                                     messageDeliveryLockGuard.Pulse();
1318                             }
1319                         }
1320                     }
1321                 }
1322             }
1323         }
1324
1325         internal ReadOnlyCollection<WorkflowQueueInfo> GetWorkflowQueueInfos()
1326         {
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())
1331             {
1332                 using (new ServiceEnvironment(this.rootActivity))
1333                 {
1334                     lock (qService.SyncRoot)
1335                     {
1336                         IEnumerable<IComparable> names = qService.QueueNames;
1337                         foreach (IComparable name in names)
1338                         {
1339                             try
1340                             {
1341                                 WorkflowQueue queue = qService.GetWorkflowQueue(name);
1342                                 if (!queue.Enabled)
1343                                     continue;
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)
1348                                 {
1349                                     string activity = (l.SubscribedActivityQualifiedName == null) ? l.ActivityQualifiedName : l.SubscribedActivityQualifiedName;
1350                                     subscribedActivities.Add(activity);
1351                                 }
1352                                 queuedItems.Add(new WorkflowQueueInfo(name, items, subscribedActivities.AsReadOnly()));
1353                             }
1354                             catch (InvalidOperationException)
1355                             {
1356                                 // ignore this queue if it has disappeared
1357                             }
1358                         }
1359                     }
1360                 }
1361             }
1362             return queuedItems.AsReadOnly();
1363         }
1364
1365         internal DateTime GetWorkflowNextTimerExpiration()
1366         {
1367             using (this._executorLock.Enter())
1368             {
1369                 using (this.MessageDeliveryLock.Enter())
1370                 {
1371                     TimerEventSubscriptionCollection timers = TimerQueue;
1372                     TimerEventSubscription sub = timers.Peek();
1373                     return sub == null ? DateTime.MaxValue : sub.ExpiresAt;
1374                 }
1375             }
1376         }
1377
1378         #endregion MessageArrival and Query
1379
1380         #region executor to execution context mappings
1381
1382         //This list is populated at loading time.
1383         //a map of SubState Tracking Context - SubState.
1384         [NonSerialized]
1385         private Dictionary<int, Activity> subStateMap = new Dictionary<int, Activity>();
1386
1387         internal void RegisterDynamicActivity(Activity dynamicActivity, bool load)
1388         {
1389             int contextId = ContextActivityUtils.ContextId(dynamicActivity);
1390             this.subStateMap.Add(contextId, dynamicActivity);
1391
1392             System.Workflow.Runtime.WorkflowTrace.Runtime.TraceEvent(
1393                 TraceEventType.Information, 0, "Adding context {0}:{1}",
1394                 contextId, dynamicActivity.QualifiedName + (load ? " for load" : ""));
1395
1396             dynamicActivity.OnActivityExecutionContextLoad(this);
1397         }
1398
1399         internal void UnregisterDynamicActivity(Activity dynamicActivity)
1400         {
1401             int contextId = ContextActivityUtils.ContextId(dynamicActivity);
1402             this.subStateMap.Remove(contextId);
1403
1404             System.Workflow.Runtime.WorkflowTrace.Runtime.TraceEvent(
1405                 TraceEventType.Information, 0, "Removing context {0}:{1}",
1406                 contextId, dynamicActivity.QualifiedName);
1407
1408             dynamicActivity.OnActivityExecutionContextUnload(this);
1409         }
1410
1411         internal Activity GetContextActivityForId(int stateId)
1412         {
1413             if (this.subStateMap.ContainsKey(stateId))
1414                 return this.subStateMap[stateId];
1415             return null;
1416         }
1417
1418         #endregion
1419
1420         #region Unloading
1421         // indicates whether an this schedule instance can be unloaded right now
1422         internal bool IsUnloadableNow
1423         {
1424             // Called by hosting environment
1425             get { return ((this.currentAtomicActivity == null) && (this.Scheduler.IsStalledNow || this.WorkflowStatus == WorkflowStatus.Suspended)); }
1426         }
1427
1428         /// <summary>
1429         /// Synchronously unload if currently idle
1430         /// </summary>
1431         /// <returns>true if successful</returns>
1432         internal bool TryUnload()
1433         {
1434             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a TryUnload request for instance {0}", this.InstanceIdString);
1435             DiagnosticStackTrace("try unload request");
1436
1437             try
1438             {
1439                 // check if this is a valid in-memory instance
1440                 if (!this.IsInstanceValid)
1441                     return false;
1442
1443                 // check if there is a persistence service
1444                 if (this.WorkflowRuntime.WorkflowPersistenceService == null)
1445                 {
1446                     string errMsg = String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceService, this.InstanceId);
1447                     WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, errMsg);
1448                     throw new InvalidOperationException(errMsg);
1449                 }
1450                 using (new ScheduleWork(this, true))
1451                 {
1452                     // Stop threads from outside - message delivery and control operations
1453                     if (this._executorLock.TryEnter())
1454                     {
1455                         try
1456                         {
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())
1460                             {
1461                                 try
1462                                 {
1463                                     if (this._msgDeliveryLock.TryEnter())
1464                                     {
1465                                         using (new ServiceEnvironment(this.rootActivity))
1466                                         {
1467                                             try
1468                                             {
1469                                                 if (!this.IsInstanceValid)
1470                                                     return false;
1471
1472                                                 this.ProcessQueuedEvents(); // deliver any outstanding queued events before persisting
1473                                                 if (this.IsUnloadableNow)
1474                                                 {
1475                                                     // can unload now
1476                                                     return PerformUnloading(false);
1477                                                 }
1478                                                 else
1479                                                     return false;
1480                                             }
1481                                             finally
1482                                             {
1483                                                 this._msgDeliveryLock.Exit();
1484                                             }
1485                                         }
1486                                     }
1487                                 }
1488                                 finally
1489                                 {
1490                                     SchedulerLockGuard.Exit(this._schedulerLock, this);
1491                                 }
1492                             }
1493                         }
1494                         finally
1495                         {
1496                             this._executorLock.Exit();
1497                         }
1498                     }
1499                 }
1500             }
1501             catch (Exception e)
1502             {
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);
1504                 throw;
1505             }
1506             return false;
1507         }
1508
1509         // this unloads the instance by assuming that it can be unloaded.
1510         private bool PerformUnloading(bool handleExceptions)
1511         {
1512             InstanceLock.AssertIsLocked(this._schedulerLock);
1513
1514             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Unloading instance {0}", this.InstanceIdString);
1515             DiagnosticStackTrace("unload request");
1516
1517             FireWorkflowExecutionEvent(this, WorkflowEventInternal.Unloading);
1518             //
1519             // Block message delivery for duration of persist and marking as invalid
1520             using (_msgDeliveryLock.Enter())
1521             {
1522                 TimerQueue.SuspendDelivery();
1523
1524                 bool persisted;
1525                 if (handleExceptions)
1526                 {
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);
1530                 }
1531                 else
1532                 {
1533                     WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Calling Persist");
1534                     this.Persist(this.rootActivity, true, false);
1535                     persisted = true;
1536                     WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Returning from Persist: InstanceId {0}, hc: {1}, IsInstanceValid={2}", InstanceIdString, this.GetHashCode(), IsInstanceValid);
1537                 }
1538                 if (persisted)
1539                 {
1540                     // mark instance as invalid
1541                     this.IsInstanceValid = false;
1542
1543                     FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Unloaded);
1544                     return true;
1545                 }
1546                 else
1547                     return false;
1548             }
1549         }
1550
1551         // shutsdown the schedule instance [....]
1552         internal void Unload()
1553         {
1554             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got an unload request for instance {0}", this.InstanceIdString);
1555             DiagnosticStackTrace("unload request");
1556
1557             try
1558             {
1559                 using (new ScheduleWork(this, true))
1560                 {
1561                     // Stop threads from outside - message delivery and control operations
1562                     using (this._executorLock.Enter())
1563                     {
1564                         if (this.WorkflowRuntime.WorkflowPersistenceService == null)
1565                         {
1566                             string errMsg = String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceService, this.InstanceId);
1567                             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, errMsg);
1568                             throw new InvalidOperationException(errMsg);
1569                         }
1570
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))
1577                         {
1578                             using (new ServiceEnvironment(this.rootActivity))
1579                             {
1580                                 // check if this is a valid in-memory instance
1581                                 if (!this.IsInstanceValid)
1582                                 {
1583                                     throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1584                                 }
1585
1586                                 // the scheduler must be idle now
1587                                 if (this.currentAtomicActivity == null)
1588                                 {
1589                                     WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Calling PerformUnloading(false) on instance {0} hc {1}", InstanceIdString, this.GetHashCode());
1590                                     // unload
1591                                     PerformUnloading(false);
1592                                     WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Returning from PerformUnloading(false): IsInstanceValue: " + IsInstanceValid);
1593                                 }
1594                                 else
1595                                 {
1596                                     this.Scheduler.CanRun = true;
1597                                     throw new ExecutorLocksHeldException(atomicActivityEvent);
1598                                 }
1599                             }
1600                         }
1601                     }
1602                 }
1603             }
1604             catch (Exception e)
1605             {
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);
1607                 throw;
1608             }
1609         }
1610
1611         #endregion
1612
1613         #region Terminate
1614
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)
1619         {
1620             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor::Terminate : Got a terminate request for instance {0}", this.InstanceIdString);
1621
1622             try
1623             {
1624                 using (new ScheduleWork(this, true))
1625                 {
1626                     // Stop threads from outside - message delivery and control operations
1627                     using (this._executorLock.Enter())
1628                     {
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;
1633
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))
1638                         {
1639                             using (new ServiceEnvironment(this.rootActivity))
1640                             {
1641
1642                                 // check if this is a valid in-memory instance
1643                                 if (!this.IsInstanceValid)
1644                                     throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1645
1646                                 this.TerminateOnIdle(error);
1647                             }
1648                         }
1649                     }
1650                 }
1651             }
1652             catch (Exception e)
1653             {
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);
1655                 throw;
1656             }
1657         }
1658
1659         // this method must be called with the scheduler lock held
1660         internal bool TerminateOnIdle(string error)
1661         {
1662             InstanceLock.AssertIsLocked(this._schedulerLock);
1663
1664             // check if the instance can be terminated
1665             if (!this.IsInstanceValid)
1666                 return false;
1667
1668             // tell the scheduler to stop running
1669             this.Scheduler.CanRun = false;
1670
1671             try
1672             {
1673                 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Terminating instance {0}", this.InstanceIdString);
1674
1675                 if (null != ThrownException)
1676                     FireWorkflowTerminating(ThrownException);
1677                 else
1678                     FireWorkflowTerminating(error);
1679
1680
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;
1686                 //
1687                 // Block message delivery for duration of persistence and marking as invalid instance
1688                 using (_msgDeliveryLock.Enter())
1689                 {
1690                     TimerQueue.SuspendDelivery();
1691                     this.rootActivity.SetValue(Activity.ExecutionResultProperty, ActivityExecutionResult.Canceled);
1692                     try
1693                     {
1694                         // persist the instance state
1695                         this.Persist(this.rootActivity, true, false);
1696                     }
1697                     catch (Exception e)
1698                     {
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);
1703                         this.AbortOnIdle();
1704                         return false;
1705                     }
1706
1707                     // Any remaining messages in queues are zombie messages so move all to the pending queue
1708                     this.qService.MoveAllMessagesToPendingQueue();
1709
1710                     if (null != ThrownException)
1711                         FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Terminated, ThrownException);
1712                     else
1713                         FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Terminated, error);
1714
1715                     // unsubscribe for model changes
1716                     Debug.Assert(this.IsInstanceValid);
1717                     // mark instance as invalid
1718                     this.IsInstanceValid = false;
1719                 }
1720
1721                 if (currentAtomicActivity != null)
1722                 {
1723                     atomicActivityEvent.Set();
1724                     atomicActivityEvent.Close();
1725                 }
1726             }
1727             catch (Exception)
1728             {
1729                 if ((this.rootActivity == this.CurrentActivity) && this.rootActivity.ExecutionStatus == ActivityExecutionStatus.Closed)
1730                 {
1731                     using (_msgDeliveryLock.Enter())
1732                     {
1733                         this.AbortOnIdle();
1734                         return false;
1735                     }
1736                 }
1737                 else
1738                 {
1739                     this.Scheduler.CanRun = true;
1740                     throw;
1741                 }
1742             }
1743
1744             return true;
1745         }
1746
1747         #endregion
1748
1749         #region Abort
1750
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()
1755         {
1756             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor::Abort : Got a abort request for instance {0}", this.InstanceIdString);
1757
1758             try
1759             {
1760                 // Stop threads from outside - message delivery and control operations
1761                 using (this._executorLock.Enter())
1762                 {
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;
1767
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))
1772                     {
1773                         using (this._msgDeliveryLock.Enter())
1774                         {
1775                             using (new ServiceEnvironment(this.rootActivity))
1776                             {
1777
1778                                 // check if this is a valid in-memory instance
1779                                 if (!this.IsInstanceValid)
1780                                     throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1781
1782
1783                                 this.AbortOnIdle();
1784                             }
1785                         }
1786                     }
1787                 }
1788             }
1789             catch (Exception e)
1790             {
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);
1792                 throw;
1793             }
1794         }
1795
1796         // this method must be called with the scheduler lock held
1797         internal void AbortOnIdle()
1798         {
1799             InstanceLock.AssertIsLocked(this._schedulerLock);
1800             InstanceLock.AssertIsLocked(this._msgDeliveryLock);
1801
1802             // check if the instance can be aborted
1803             if (!this.IsInstanceValid)
1804                 return;
1805
1806             FireWorkflowExecutionEvent(this, WorkflowEventInternal.Aborting);
1807
1808             TimerQueue.SuspendDelivery();
1809
1810             // tell the scheduler to stop running
1811             this.Scheduler.CanRun = false;
1812
1813             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Aborting instance {0}", this.InstanceIdString);
1814
1815             try
1816             {
1817                 // abort any transaction in progress
1818                 if (this.currentAtomicActivity != null)
1819                 {
1820                     this.RollbackTransaction(null, this.currentAtomicActivity);
1821                     this.currentAtomicActivity = null;
1822                 }
1823
1824                 // clear the batched work
1825                 this.ResourceManager.ClearAllBatchedWork();
1826
1827                 // unlock instance state w/o saving it
1828                 WorkflowPersistenceService persistenceSvc = this.WorkflowRuntime.WorkflowPersistenceService;
1829                 if (persistenceSvc != null)
1830                 {
1831                     persistenceSvc.UnlockWorkflowInstanceState(attemptedRootDispose ? null : this.rootActivity);
1832                     if (HasNonEmptyWorkBatch())
1833                     {
1834                         this.CommitTransaction(this.rootActivity);
1835                     }
1836                 }
1837             }
1838             catch (Exception e)
1839             {
1840                 if (WorkflowExecutor.IsIrrecoverableException(e))
1841                 {
1842                     throw;
1843                 }
1844             }
1845             finally
1846             {
1847                 // mark instance as invalid
1848                 this.IsInstanceValid = false;
1849                 DisposeRootActivity(true);
1850                 if (currentAtomicActivity != null)
1851                 {
1852                     atomicActivityEvent.Set();
1853                     atomicActivityEvent.Close();
1854                 }
1855                 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Aborted);
1856             }
1857         }
1858
1859         #endregion
1860
1861         #region Suspend
1862
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)
1867         {
1868             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a suspend request for instance {0}", this.InstanceIdString);
1869
1870             try
1871             {
1872                 // check if this is a valid in-memory instance
1873                 if (!this.IsInstanceValid)
1874                     throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1875
1876
1877                 // Stop threads from outside - message delivery and control operations
1878                 using (this._executorLock.Enter())
1879                 {
1880                     // check if this is a valid in-memory instance
1881                     if (!this.IsInstanceValid)
1882                         throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1883
1884                     // tell the scheduler to stop running
1885                     this.Scheduler.CanRun = false;
1886
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))
1891                     {
1892                         using (new ServiceEnvironment(this.rootActivity))
1893                         {
1894                             // check if this is a valid in-memory instance
1895                             if (!this.IsInstanceValid)
1896                                 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1897
1898                             return this.SuspendOnIdle(error);
1899                         }
1900                     }
1901                 }
1902             }
1903             catch (Exception e)
1904             {
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);
1906                 throw;
1907             }
1908         }
1909
1910         // this method must be called with the scheduler lock held
1911         internal bool SuspendOnIdle(string error)
1912         {
1913             InstanceLock.AssertIsLocked(this._schedulerLock);
1914
1915             // check if the instance can be suspended
1916             if (!this.IsInstanceValid)
1917                 return false;
1918
1919             // if atomic activity in progress, then throw
1920             if (this.currentAtomicActivity != null)
1921             {
1922                 this.Scheduler.CanRun = true;
1923                 throw new ExecutorLocksHeldException(atomicActivityEvent);
1924             }
1925             else
1926             {
1927                 // if already suspended or if just created, then do nothing
1928                 WorkflowStatus status = this.WorkflowStatus;
1929                 if (status == WorkflowStatus.Suspended || status == WorkflowStatus.Created)
1930                     return false;
1931
1932                 FireWorkflowSuspending(error);
1933
1934                 // tell the scheduler to stop running
1935                 this.Scheduler.CanRun = false;
1936
1937                 switch (this.rootActivity.ExecutionStatus)
1938                 {
1939                     case ActivityExecutionStatus.Initialized:
1940                     case ActivityExecutionStatus.Executing:
1941                     case ActivityExecutionStatus.Canceling:
1942                     case ActivityExecutionStatus.Faulting:
1943                     case ActivityExecutionStatus.Compensating:
1944                         break;
1945
1946                     case ActivityExecutionStatus.Closed:
1947                         return false;
1948                     default:
1949                         return false;
1950                 }
1951
1952                 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Suspending instance {0}", this.InstanceIdString);
1953
1954                 // mark it as suspended
1955                 this.stateChangedSincePersistence = true;
1956                 this.WorkflowStatus = WorkflowStatus.Suspended;
1957                 this.rootActivity.SetValue(SuspendOrTerminateInfoProperty, error);
1958
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);
1962                 return true;
1963             }
1964         }
1965         #endregion
1966
1967         #region Resume
1968
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()
1973         {
1974             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a resume request for instance {0}", this.InstanceIdString);
1975
1976             try
1977             {
1978                 // check if this is a valid in-memory instance
1979                 if (!this.IsInstanceValid)
1980                     throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1981
1982                 using (ScheduleWork work = new ScheduleWork(this))
1983                 {
1984                     // Stop threads from outside - message delivery and control operations
1985                     using (this._executorLock.Enter())
1986                     {
1987                         // check if this is a valid in-memory instance
1988                         if (!this.IsInstanceValid)
1989                             throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
1990
1991                         if ((this.WorkflowStatus != WorkflowStatus.Suspended))
1992                             return;
1993
1994                         using (new SchedulerLockGuard(this._schedulerLock, this))
1995                         {
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);
2005                              */
2006                             using (new ServiceEnvironment(this.rootActivity))
2007                             {
2008                                 this.ResumeOnIdle(true);
2009                             }
2010                         }
2011                     }
2012                 }
2013             }
2014             catch (Exception e)
2015             {
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);
2017                 throw;
2018             }
2019         }
2020
2021         // this method must be called with the scheduler lock held
2022         internal bool ResumeOnIdle(bool outsideThread)
2023         {
2024             InstanceLock.AssertIsLocked(this._schedulerLock);
2025
2026             // check if this is a valid in-memory instance
2027             if (!this.IsInstanceValid)
2028                 return false;
2029
2030             // if not suspended and CanRun is true, then nothing to resume
2031             if ((this.WorkflowStatus != WorkflowStatus.Suspended) && (!this.Scheduler.CanRun))
2032                 return false;
2033
2034             FireWorkflowExecutionEvent(this, WorkflowEventInternal.Resuming);
2035
2036             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Resuming instance {0}", this.InstanceIdString);
2037
2038             this.stateChangedSincePersistence = true;
2039             this.WorkflowStatus = WorkflowStatus.Running;
2040             this.rootActivity.SetValue(SuspendOrTerminateInfoProperty, string.Empty);
2041
2042             FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Resumed, ThrownException);
2043
2044             using (this._msgDeliveryLock.Enter())
2045             {
2046                 TimerQueue.ResumeDelivery();
2047             }
2048
2049             // resume the instance
2050             if (outsideThread)
2051                 this.Scheduler.Resume();
2052             else
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;
2056
2057             return true;
2058         }
2059
2060         #endregion
2061
2062         #region Transaction Management
2063
2064         internal bool IsActivityInAtomicContext(Activity activity, out Activity atomicActivity)
2065         {
2066             Debug.Assert(activity != null);
2067
2068             atomicActivity = null;
2069             while (activity != null)
2070             {
2071                 if (activity == this.currentAtomicActivity)
2072                 {
2073                     atomicActivity = activity;
2074                     return true;
2075                 }
2076                 activity = activity.Parent;
2077             }
2078             return false;
2079         }
2080
2081         private void CreateTransaction(Activity atomicActivity)
2082         {
2083             Debug.Assert(this.currentAtomicActivity == null, "There is already a transacted activity running");
2084
2085             TransactionalProperties transactionalProperties = new TransactionalProperties();
2086
2087             TransactionOptions tranOpts = new TransactionOptions();
2088             WorkflowTransactionOptions atomicTxn = TransactedContextFilter.GetTransactionOptions(atomicActivity);
2089             Debug.Assert(atomicTxn != null, "null atomicTxn");
2090
2091             // 
2092             tranOpts.IsolationLevel = atomicTxn.IsolationLevel;
2093             if (tranOpts.IsolationLevel == IsolationLevel.Unspecified)
2094                 tranOpts.IsolationLevel = IsolationLevel.Serializable;
2095
2096             tranOpts.Timeout = atomicTxn.TimeoutDuration;
2097
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);
2110
2111             // create a local queuing service per atomic context
2112             transactionalProperties.LocalQueuingService = new WorkflowQueuingService(this.qService);
2113
2114             // Store the transaction properties onto the activity
2115             atomicActivity.SetValue(TransactionalPropertiesProperty, transactionalProperties);
2116
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);
2121         }
2122
2123         private void DisposeTransaction(Activity atomicActivity)
2124         {
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);
2128
2129             // Cleanup work following a transaction commit or Rollback
2130             TransactionalProperties transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(TransactionalPropertiesProperty);
2131
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());
2138
2139             // cleanup properties
2140             transactionalProperties.Transaction = null;
2141             transactionalProperties.LocalQueuingService = null;
2142             transactionalProperties.Transaction = null;
2143
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();
2151
2152         }
2153
2154         private void CommitTransaction(Activity activityContext)
2155         {
2156             if (null == Transaction.Current)
2157             {
2158                 //
2159                 // No TxScopeActivity or external tx
2160                 // Ask the TxService to commit
2161                 // In this scenario retries are OK as it owns the tx
2162                 try
2163                 {
2164                     //
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();
2169                 }
2170                 catch
2171                 {
2172                     this.ResourceManager.HandleFault();
2173                     throw;
2174                 }
2175             }
2176             else
2177             {
2178                 Debug.Assert(activityContext != null, "null activityContext");
2179
2180                 TransactionalProperties transactionalProperties = null;
2181                 bool inTxScope = (activityContext == this.currentAtomicActivity);
2182                 //
2183                 // Tx is either from TxScopeActivity or it is external                
2184                 if (inTxScope)
2185                 {
2186                     transactionalProperties = (TransactionalProperties)activityContext.GetValue(TransactionalPropertiesProperty);
2187                     if (CheckAndProcessTransactionAborted(transactionalProperties))
2188                         return;
2189                 }
2190                 //
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.
2193                 try
2194                 {
2195                     this.WorkflowRuntime.TransactionService.CommitWorkBatch(DoResourceManagerCommit);
2196                 }
2197                 catch
2198                 {
2199                     //
2200                     // This tx is doomed, clean up batches
2201                     ResourceManager.HandleFault();
2202                     throw;
2203                 }
2204                 finally
2205                 {
2206                     if (inTxScope)
2207                     {
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);
2213                     }
2214                 }
2215                 //
2216                 // If we are in a tx scope we need to commit our tx
2217                 if (inTxScope)
2218                 {
2219                     //
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)
2223                     try
2224                     {
2225                         CommittableTransaction ctx = transactionalProperties.Transaction as CommittableTransaction;
2226                         if (null != ctx)
2227                         {
2228                             try
2229                             {
2230                                 ctx.Commit();
2231                             }
2232                             catch
2233                             {
2234                                 qService.PostPersist(false);
2235                                 throw;
2236                             }
2237
2238                             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2239                                 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2240                                 " .Committed CommittableTransaction " +
2241                                 ((System.Transactions.Transaction)transactionalProperties.Transaction).GetHashCode());
2242                         }
2243
2244                         DependentTransaction dtx = transactionalProperties.Transaction as DependentTransaction;
2245                         if (null != dtx)
2246                         {
2247                             try
2248                             {
2249                                 dtx.Complete();
2250                             }
2251                             catch
2252                             {
2253                                 qService.PostPersist(false);
2254                                 throw;
2255                             }
2256                             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2257                                 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2258                                 " .Completed DependentTransaction " +
2259                                 ((System.Transactions.Transaction)transactionalProperties.Transaction).GetHashCode());
2260                         }
2261                     }
2262                     catch
2263                     {
2264                         //
2265                         // This tx (scope activity or external) is doomed, clean up batches
2266                         ResourceManager.HandleFault();
2267                         throw;
2268                     }
2269
2270                     //
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;
2277
2278                     WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2279                         "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2280                         "Reset CurrentAtomicActivity to null");
2281
2282                 }
2283                 //
2284                 // Tell the batches that we committed successfully
2285                 ResourceManager.Complete();
2286             }
2287         }
2288         /// <summary>
2289         /// Call commit on the VolatileResourceManager to commit all work in the batch.
2290         /// Transaction.Current must be non-null.
2291         /// </summary>
2292         private void DoResourceManagerCommit()
2293         {
2294             if (null == Transaction.Current)
2295                 throw new Exception(ExecutionStringManager.NullAmbientTransaction);
2296
2297             this.ResourceManager.Commit();
2298         }
2299
2300         private void RollbackTransaction(Exception exp, Activity activityContext)
2301         {
2302             if (activityContext == this.currentAtomicActivity)
2303             {
2304                 Debug.Assert((activityContext == this.currentAtomicActivity),
2305                     "Activity context " + activityContext.Name + " different from currentAtomicActivity " + this.currentAtomicActivity.Name);
2306
2307                 TransactionalProperties transactionalProperties = (TransactionalProperties)activityContext.GetValue(TransactionalPropertiesProperty);
2308                 if (transactionalProperties.TransactionState != TransactionProcessState.AbortProcessed)
2309                 {
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;
2315                 }
2316
2317                 Debug.Assert((transactionalProperties.Transaction != null), "Null Transaction while transaction is present");
2318                 Debug.Assert((transactionalProperties.LocalQueuingService != null), "Null LocalQueuingService while transaction is present");
2319
2320                 try
2321                 {
2322                     DisposeTransactionScope(transactionalProperties);
2323
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());
2332                 }
2333                 finally
2334                 {
2335                     // roolback queuing service state
2336                     WorkflowQueuingService queuingService = transactionalProperties.LocalQueuingService;
2337                     queuingService.Complete(false);
2338
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);
2343                 }
2344             }
2345         }
2346
2347         #region VolatileEnlistment for Transaction Completion Notification
2348         /*
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>
2351         {
2352             WorkflowExecutor workflowExecutor;
2353             Transaction transaction;
2354             Activity atomicActivity;
2355             internal TransactionNotificationEnlistment(WorkflowExecutor exec, Transaction tx, Activity atomicActivity)
2356             {
2357                 this.workflowExecutor = exec;
2358                 this.transaction = tx;
2359                 this.atomicActivity = atomicActivity;
2360             }
2361
2362             #region IEnlistmentNotification Members
2363
2364             void IEnlistmentNotification.Commit(Enlistment enlistment)
2365             {
2366                 enlistment.Done();
2367             }
2368
2369             void IEnlistmentNotification.InDoubt(Enlistment enlistment)
2370             {
2371                 enlistment.Done();
2372             }
2373
2374             void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
2375             {
2376                 preparingEnlistment.Prepared();
2377             }
2378
2379             void IEnlistmentNotification.Rollback(Enlistment enlistment)
2380             {
2381                 //
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.
2391                 enlistment.Done();
2392                 //
2393                 // ensure transaction timeout/abort is processed in case of a
2394                 // blocked activity inside a transactional scope
2395                 ScheduleTransactionTimeout();
2396             }
2397
2398             private void ScheduleTransactionTimeout()
2399             {
2400                 try
2401                 {
2402                     //
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())
2406                     {
2407                         if (!this.workflowExecutor.IsInstanceValid)
2408                             return;
2409
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))
2413                         {
2414                             TransactionalProperties transactionalProperties = (TransactionalProperties)curAtomicActivity.GetValue(TransactionalPropertiesProperty);
2415                             if ((transactionalProperties.Transaction == this.transaction) &&
2416                                 (transactionalProperties.TransactionState != TransactionProcessState.AbortProcessed))
2417                             {
2418                                 transactionalProperties.TransactionState = TransactionProcessState.Aborted;
2419
2420                                 using (this.workflowExecutor.MessageDeliveryLock.Enter())
2421                                 {
2422                                     using (new ServiceEnvironment(this.workflowExecutor.RootActivity))
2423                                     {
2424                                         using (this.workflowExecutor.SetCurrentActivity(curAtomicActivity))
2425                                         {
2426                                             //
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);
2434                                         }
2435                                     }
2436                                 }
2437                             }
2438                         }
2439                     }
2440                 }
2441                 catch (Exception e)
2442                 {
2443                     WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "AbortNotificationEnlistment: instanceId: {0} failed to process ScheduleTransactionTimeout with exception {1} ", this.workflowExecutor.this.InstanceIdString, e.Message);
2444                 }
2445             }
2446
2447             void IActivityEventListener<EventArgs>.OnEvent(object sender, EventArgs e)
2448             {
2449                 // this will never be invoked since Scheduler will process the transaction aborted request
2450             }
2451
2452             #endregion
2453         }*/
2454         #endregion VolatileEnlistment for AbortNotification
2455
2456         internal static bool CheckAndProcessTransactionAborted(TransactionalProperties transactionalProperties)
2457         {
2458             if (transactionalProperties.Transaction != null && transactionalProperties.Transaction.TransactionInformation.Status != TransactionStatus.Aborted)
2459                 return false;
2460
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)
2465             {
2466                 case TransactionProcessState.Ok:
2467                 case TransactionProcessState.Aborted:
2468                     transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed;
2469                     throw new TransactionAbortedException();
2470
2471                 case TransactionProcessState.AbortProcessed:
2472                     return true;
2473
2474                 default:
2475                     return false;
2476             }
2477         }
2478
2479         private void DisposeTransactionScope(TransactionalProperties transactionalProperties)
2480         {
2481             if (transactionalProperties.TransactionScope != null)
2482             {
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));
2491             }
2492         }
2493
2494         #region delay scheduling of items for ACID purposes
2495
2496         private void AddItemToBeScheduledLater(Activity atomicActivity, SchedulableItem item)
2497         {
2498             if (atomicActivity == null)
2499                 return;
2500
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)
2504                 return;
2505
2506             TransactionalProperties transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(TransactionalPropertiesProperty);
2507             if (transactionalProperties != null)
2508             {
2509                 lock (transactionalProperties)
2510                 {
2511                     List<SchedulableItem> notifications = null;
2512                     notifications = transactionalProperties.ItemsToBeScheduledAtCompletion;
2513                     if (notifications == null)
2514                     {
2515                         notifications = new List<SchedulableItem>();
2516                         transactionalProperties.ItemsToBeScheduledAtCompletion = notifications;
2517                     }
2518                     notifications.Add(item);
2519                 }
2520             }
2521         }
2522
2523         private void ScheduleDelayedItems(Activity atomicActivity)
2524         {
2525             List<SchedulableItem> items = null;
2526             TransactionalProperties transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(TransactionalPropertiesProperty);
2527
2528             if (transactionalProperties == null)
2529                 return;
2530
2531             lock (transactionalProperties)
2532             {
2533                 items = transactionalProperties.ItemsToBeScheduledAtCompletion;
2534                 if (items == null)
2535                     return;
2536
2537                 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0,
2538                     "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString +
2539                     " .Scheduling delayed " + items.Count + " number of items");
2540
2541                 foreach (SchedulableItem item in items)
2542                 {
2543                     this.Scheduler.ScheduleItem(item, false, true);
2544                 }
2545                 items.Clear();
2546
2547                 transactionalProperties.ItemsToBeScheduledAtCompletion = null;
2548             }
2549         }
2550
2551         #endregion delay scheduling of items for ACID purposes
2552
2553         #endregion Transaction Management
2554
2555         #region Exception Management
2556
2557         internal void ExceptionOccured(Exception exp, Activity currentActivity, string originalActivityId)
2558         {
2559             Debug.Assert(exp != null, "null exp");
2560             Debug.Assert(currentActivity != null, "null currentActivity");
2561             // exception tracking work
2562             //
2563             if (this.ThrownException != exp)
2564             {
2565                 // first time exception
2566                 this.ThrownException = exp;
2567                 this.activityThrowingException = currentActivity.QualifiedName;
2568                 originalActivityId = currentActivity.QualifiedName;
2569             }
2570             else
2571             {
2572                 // rethrown exception
2573                 originalActivityId = this.activityThrowingException;
2574             }
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);
2580
2581             // notify the activity.
2582             //
2583             using (new ServiceEnvironment(currentActivity))
2584             {
2585                 using (SetCurrentActivity(currentActivity))
2586                 {
2587                     using (ActivityExecutionContext executionContext = new ActivityExecutionContext(currentActivity, true))
2588                         executionContext.FaultActivity(exp);
2589                 }
2590             }
2591
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);
2596         }
2597
2598         internal Exception ThrownException
2599         {
2600             get { return thrownException; }
2601             set { thrownException = value; }
2602         }
2603
2604         internal static bool IsIrrecoverableException(Exception e)
2605         {
2606             return ((e is OutOfMemoryException) ||
2607                     (e is StackOverflowException) ||
2608                     (e is ThreadInterruptedException) ||
2609                     (e is ThreadAbortException));
2610         }
2611
2612         #endregion Exception Management
2613
2614         #region Tracking Management
2615
2616         internal void Track(Activity activity, string key, object args)
2617         {
2618             FireUserTrackPoint(activity, key, args);
2619         }
2620
2621         internal void FireExceptionOccured(Exception e, string currentActivityPath, string originalActivityPath, Guid contextGuid, Guid parentContextGuid)
2622         {
2623             FireWorkflowException(e, currentActivityPath, originalActivityPath, contextGuid, parentContextGuid);
2624         }
2625
2626         #endregion
2627
2628         #region Dynamic Update Management
2629
2630         #region Dynamic Update From Outside the instance
2631         internal Activity GetWorkflowDefinition(string workflowContext)
2632         {
2633             if (workflowContext == null)
2634                 throw new ArgumentNullException("workflowContext");
2635
2636             return this.WorkflowDefinition;
2637         }
2638
2639         internal Activity GetWorkflowDefinitionClone(string workflowContext)
2640         {
2641             if (workflowContext == null)
2642                 throw new ArgumentNullException("workflowContext");
2643
2644             Activity definition = this.WorkflowDefinition;
2645
2646             using (new WorkflowDefinitionLock(definition))
2647             {
2648                 return definition.Clone();
2649             }
2650         }
2651
2652         internal void ApplyWorkflowChanges(WorkflowChanges workflowChanges)
2653         {
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");
2657
2658             // check arguments
2659             if (workflowChanges == null)
2660                 throw new ArgumentNullException("workflowChanges");
2661
2662             // check if this is a valid in-memory instance
2663             if (!this.IsInstanceValid)
2664                 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
2665
2666             if (this.currentAtomicActivity != null)
2667                 throw new InvalidOperationException(ExecutionStringManager.Error_InsideAtomicScope);
2668
2669             try
2670             {
2671                 using (ScheduleWork work = new ScheduleWork(this))
2672                 {
2673                     // block other instance operations from outside
2674                     using (this._executorLock.Enter())
2675                     {
2676                         // check if this is a valid in-memory instance
2677                         if (!this.IsInstanceValid)
2678                             throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
2679
2680                         // get the instance to stop running
2681                         this.Scheduler.CanRun = false;
2682                         using (new SchedulerLockGuard(this._schedulerLock, this))
2683                         {
2684                             using (new ServiceEnvironment(this.rootActivity))
2685                             {
2686                                 bool localSuspend = false;
2687
2688                                 // check if this is a valid in-memory instance
2689                                 if (!this.IsInstanceValid)
2690                                     throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
2691
2692                                 try
2693                                 {
2694                                     // check the status of the schedule
2695                                     switch (this.WorkflowStatus)
2696                                     {
2697                                         ////case ActivityExecutionStatus.Completed:
2698                                         // 
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;
2706                                             break;
2707                                         default:
2708                                             // suspend the instance
2709                                             this.SuspendOnIdle(null);
2710                                             localSuspend = true;
2711                                             break;
2712                                     }
2713
2714                                     // apply the changes
2715                                     workflowChanges.ApplyTo(this.rootActivity);
2716                                 }
2717                                 finally
2718                                 {
2719                                     if (localSuspend)
2720                                     {
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);
2724                                     }
2725                                 }
2726                             }
2727                         } // release lock on scheduler
2728                     }
2729                 }
2730             }
2731             catch (Exception e)
2732             {
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);
2734                 throw;
2735             }
2736         }
2737         #endregion Dynamic Update From Outside the instance
2738         internal bool OnBeforeDynamicChange(IList<WorkflowChangeAction> changes)
2739         {
2740             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a dynamic update request for instance {0}", this.InstanceIdString);
2741
2742             if (!this.IsInstanceValid)
2743                 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid);
2744
2745             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Found a match for the schedule in updating instance {0}", this.InstanceIdString);
2746
2747             FireDynamicUpdateBegin(changes);
2748
2749             return true;
2750         }
2751
2752         internal void OnAfterDynamicChange(bool updateSucceeded, IList<WorkflowChangeAction> changes)
2753         {
2754             if (updateSucceeded)
2755             {
2756                 RefreshWorkflowDefinition();
2757                 //Commit temporary work
2758                 FireDynamicUpdateCommit(changes);
2759                 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Changed);
2760             }
2761             else
2762             {
2763                 // Rollback
2764                 FireDynamicUpdateRollback(changes);
2765             }
2766
2767             WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Done updating a schedule in instance {0}", this.InstanceIdString);
2768
2769         }
2770
2771         bool IWorkflowCoreRuntime.IsDynamicallyUpdated
2772         {
2773             get
2774             {
2775                 return ((Activity)this.WorkflowDefinition).GetValue(WorkflowChanges.WorkflowChangeActionsProperty) != null;
2776             }
2777         }
2778         #endregion
2779
2780         #region Diagnostic tracing
2781
2782         [System.Diagnostics.Conditional("DEBUG")]
2783         void DiagnosticStackTrace(string reason)
2784         {
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());
2787         }
2788
2789         #endregion
2790
2791         #region Timer event support
2792
2793         WaitCallback IWorkflowCoreRuntime.ProcessTimersCallback
2794         {
2795             get
2796             {
2797                 return new WaitCallback(this.WorkflowInstance.ProcessTimers);
2798             }
2799         }
2800
2801         #endregion
2802
2803         #region IServiceProvider members
2804
2805         object IServiceProvider.GetService(Type serviceType)
2806         {
2807             return ((IWorkflowCoreRuntime)this).GetService(this.rootActivity, serviceType);
2808         }
2809         #endregion
2810
2811         #region IWorkflowCoreRuntime Members
2812
2813         Activity IWorkflowCoreRuntime.CurrentActivity
2814         {
2815             get
2816             {
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;
2822             }
2823         }
2824         Activity IWorkflowCoreRuntime.CurrentAtomicActivity
2825         {
2826             get
2827             {
2828                 return this.currentAtomicActivity;
2829             }
2830         }
2831         Guid IWorkflowCoreRuntime.StartWorkflow(Type workflowType, Dictionary<string, object> namedArgumentValues)
2832         {
2833             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2834                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2835
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)
2839             {
2840                 instanceId = instance.InstanceId;
2841                 instance.Start();
2842             }
2843
2844             return instanceId;
2845         }
2846         void IWorkflowCoreRuntime.ScheduleItem(SchedulableItem item, bool isInAtomicTransaction, bool transacted, bool queueInTransaction)
2847         {
2848             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2849                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2850             if (!queueInTransaction)
2851                 this.Scheduler.ScheduleItem(item, isInAtomicTransaction, transacted);
2852             else
2853                 this.AddItemToBeScheduledLater(this.CurrentActivity, item);
2854         }
2855         public IDisposable SetCurrentActivity(Activity activity)
2856         {
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);
2862         }
2863         Guid IWorkflowCoreRuntime.InstanceID
2864         {
2865             get
2866             {
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;
2872             }
2873         }
2874         bool IWorkflowCoreRuntime.SuspendInstance(string suspendDescription)
2875         {
2876             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2877                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2878             return this.SuspendOnIdle(suspendDescription);
2879         }
2880         void IWorkflowCoreRuntime.TerminateInstance(Exception e)
2881         {
2882             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2883                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2884
2885             this.ThrownException = e;
2886             this.TerminateOnIdle(WorkflowExecutor.GetNestedExceptionMessage(e));
2887         }
2888         bool IWorkflowCoreRuntime.Resume()
2889         {
2890             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2891                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2892             return this.ResumeOnIdle(false);
2893         }
2894         void IWorkflowCoreRuntime.RaiseHandlerInvoking(Delegate handlerDelegate)
2895         {
2896             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2897                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2898             FireWorkflowHandlerInvokingEvent(this, WorkflowEventInternal.HandlerInvoking, handlerDelegate);
2899         }
2900         void IWorkflowCoreRuntime.RaiseActivityExecuting(Activity activity)
2901         {
2902             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2903                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2904             FireActivityExecuting(this, activity);
2905         }
2906         void IWorkflowCoreRuntime.RaiseHandlerInvoked()
2907         {
2908             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2909                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2910             FireWorkflowExecutionEvent(this, WorkflowEventInternal.HandlerInvoked);
2911         }
2912         void IWorkflowCoreRuntime.CheckpointInstanceState(Activity currentActivity)
2913         {
2914             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2915                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2916
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())
2920             {
2921                 this.WorkflowStateRollbackService.CheckpointInstanceState();
2922             }
2923             this.CreateTransaction(currentActivity);
2924         }
2925         void IWorkflowCoreRuntime.RequestRevertToCheckpointState(Activity currentActivity, EventHandler<EventArgs> callbackHandler, EventArgs callbackData, bool suspendOnRevert, string suspendInfo)
2926         {
2927             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2928                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2929             this.WorkflowStateRollbackService.RequestRevertToCheckpointState(currentActivity, callbackHandler, callbackData, suspendOnRevert, suspendInfo);
2930         }
2931         void IWorkflowCoreRuntime.DisposeCheckpointState()
2932         {
2933             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2934                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2935             this.WorkflowStateRollbackService.DisposeCheckpointState();
2936         }
2937         int IWorkflowCoreRuntime.GetNewContextActivityId()
2938         {
2939             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2940                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2941             return this.GetNewContextId();
2942         }
2943         Activity IWorkflowCoreRuntime.GetContextActivityForId(int stateId)
2944         {
2945             if (this.subStateMap.ContainsKey(stateId))
2946                 return this.subStateMap[stateId];
2947             return null;
2948         }
2949         void IWorkflowCoreRuntime.RaiseException(Exception e, Activity activity, string responsibleActivity)
2950         {
2951             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2952                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2953             this.ExceptionOccured(e, activity, responsibleActivity);
2954         }
2955         void IWorkflowCoreRuntime.RegisterContextActivity(Activity activity)
2956         {
2957             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2958                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2959             this.RegisterDynamicActivity(activity, false);
2960         }
2961         void IWorkflowCoreRuntime.UnregisterContextActivity(Activity activity)
2962         {
2963             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2964                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2965             this.UnregisterDynamicActivity(activity);
2966         }
2967         void IWorkflowCoreRuntime.ActivityStatusChanged(Activity activity, bool transacted, bool committed)
2968         {
2969             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
2970                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
2971             if (!committed)
2972             {
2973                 if (activity.ExecutionStatus == ActivityExecutionStatus.Executing)
2974                 {
2975                     bool mustPersistState = (TransactedContextFilter.GetTransactionOptions(activity) != null) ? true : false;
2976                     if (mustPersistState && this.WorkflowRuntime.WorkflowPersistenceService == null)
2977                     {
2978                         string errMsg = String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceService, this.InstanceId);
2979                         WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, errMsg);
2980                         throw new InvalidOperationException(errMsg);
2981                     }
2982                 }
2983                 else if (activity.ExecutionStatus == ActivityExecutionStatus.Closed)
2984                 {
2985                     this.ScheduleDelayedItems(activity);
2986                 }
2987                 else if (activity.ExecutionStatus == ActivityExecutionStatus.Canceling || activity.ExecutionStatus == ActivityExecutionStatus.Faulting)
2988                 {
2989                     if (TransactedContextFilter.GetTransactionOptions(activity) != null)
2990                     {
2991                         // If the activity is transactional and is being canceled, roll back
2992                         // any batches associated with it.  (This does nothing if the activity
2993                         // had no batch.)
2994                         this.BatchCollection.RollbackBatch(activity);
2995                     }
2996                 }
2997             }
2998
2999             if (!committed)
3000             {
3001                 FireActivityStatusChange(this, activity);
3002             }
3003
3004             if (activity.ExecutionStatus == ActivityExecutionStatus.Closed)
3005             {
3006                 if (!(activity is ICompensatableActivity) || ((activity is ICompensatableActivity) && activity.CanUninitializeNow))
3007                     CorrelationTokenCollection.UninitializeCorrelationTokens(activity);
3008             }
3009         }
3010
3011         void IWorkflowCoreRuntime.PersistInstanceState(Activity activity)
3012         {
3013             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3014                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3015
3016             bool persistOnClose = false;
3017             if (activity.UserData.Contains(typeof(PersistOnCloseAttribute)))
3018             {
3019                 persistOnClose = (bool)activity.UserData[typeof(PersistOnCloseAttribute)];
3020             }
3021             else
3022             {
3023                 object[] attributes = activity.GetType().GetCustomAttributes(typeof(PersistOnCloseAttribute), true);
3024                 if (attributes != null && attributes.Length > 0)
3025                     persistOnClose = true;
3026             }
3027             if (persistOnClose && this.WorkflowRuntime.GetService<WorkflowPersistenceService>() == null)
3028                 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceServiceWithPersistOnClose, activity.Name));
3029
3030             this.ScheduleDelayedItems(activity);
3031
3032             bool unlock = (activity.Parent == null) ? true : false;
3033             bool needsCompensation = false; // 
3034             this.Persist(activity, unlock, needsCompensation);
3035         }
3036
3037         Activity IWorkflowCoreRuntime.LoadContextActivity(ActivityExecutionContextInfo contextInfo, Activity outerActivity)
3038         {
3039             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3040                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3041             Activity contextActivity = null;
3042             if (this.completedContextActivities.Contains(contextInfo))
3043             {
3044                 contextActivity = (Activity)this.completedContextActivities[contextInfo];
3045                 this.completedContextActivities.Remove(contextInfo);
3046
3047                 if (contextActivity.Parent != outerActivity.Parent)
3048                     contextActivity.parent = outerActivity.Parent;
3049             }
3050             else
3051             {
3052                 using (RuntimeEnvironment runtimeEnv = new RuntimeEnvironment(this.WorkflowRuntime))
3053                 {
3054                     contextActivity = this.WorkflowRuntime.WorkflowPersistenceService.LoadCompletedContextActivity(contextInfo.ContextGuid, outerActivity);
3055                     if (contextActivity == null)
3056                         throw new InvalidOperationException(ExecutionStringManager.LoadContextActivityFailed);
3057                 }
3058             }
3059             return contextActivity;
3060         }
3061         void IWorkflowCoreRuntime.SaveContextActivity(Activity contextActivity)
3062         {
3063             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3064                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3065             this.completedContextActivities.Add((ActivityExecutionContextInfo)contextActivity.GetValue(Activity.ActivityExecutionContextInfoProperty), contextActivity);
3066         }
3067         Activity IWorkflowCoreRuntime.RootActivity
3068         {
3069             get
3070             {
3071                 return this.rootActivity;
3072             }
3073         }
3074         object IWorkflowCoreRuntime.GetService(Activity activity, Type serviceType)
3075         {
3076             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3077                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3078
3079             if (serviceType == typeof(IWorkflowCoreRuntime))
3080             {
3081                 return this;
3082             }
3083             else if (serviceType == typeof(WorkflowRuntime))//sorry, no.
3084                 return null;
3085             else if (serviceType == typeof(WorkflowQueuingService))
3086             {
3087                 WorkflowQueuingService queuingService = ServiceEnvironment.QueuingService;
3088                 if (queuingService == null)
3089                     queuingService = this.qService; // root Q service
3090
3091                 queuingService.CallingActivity = ContextActivityUtils.ContextActivity(activity);
3092                 return queuingService;
3093             }
3094             else if (serviceType == typeof(IWorkflowDebuggerService))
3095             {
3096                 return this._workflowDebuggerService as IWorkflowDebuggerService;
3097             }
3098
3099             return this.WorkflowRuntime.GetService(serviceType);
3100         }
3101         bool IWorkflowCoreRuntime.OnBeforeDynamicChange(IList<WorkflowChangeAction> changes)
3102         {
3103             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3104                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3105             return this.OnBeforeDynamicChange(changes);
3106         }
3107         void IWorkflowCoreRuntime.OnAfterDynamicChange(bool updateSucceeded, IList<WorkflowChangeAction> changes)
3108         {
3109             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3110                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3111             this.OnAfterDynamicChange(updateSucceeded, changes);
3112         }
3113         void IWorkflowCoreRuntime.Track(string key, object args)
3114         {
3115             if (!ServiceEnvironment.IsInServiceThread(this.InstanceId))
3116                 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread);
3117             this.Track(this.CurrentActivity, key, args);
3118         }
3119         #endregion
3120
3121         #region ResetCurrentActivity Class
3122
3123         private class ResetCurrentActivity : IDisposable
3124         {
3125             private WorkflowExecutor workflowExecutor = null;
3126             private Activity oldCurrentActivity = null;
3127             internal ResetCurrentActivity(WorkflowExecutor workflowExecutor, Activity oldCurrentActivity)
3128             {
3129                 this.workflowExecutor = workflowExecutor;
3130                 this.oldCurrentActivity = oldCurrentActivity;
3131             }
3132             void IDisposable.Dispose()
3133             {
3134                 this.workflowExecutor.CurrentActivity = oldCurrentActivity;
3135             }
3136         }
3137         #endregion
3138
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)
3142         {
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()));
3147
3148             Activity currentActivity = (Activity)dependencyObject;
3149
3150             // fetch workflow executor
3151             IWorkflowCoreRuntime workflowExecutor = null;
3152             ISupportInterop interopSupport = null;
3153             if (currentActivity != null)
3154             {
3155                 workflowExecutor = ContextActivityUtils.RetrieveWorkflowExecutor(currentActivity);
3156                 interopSupport = workflowExecutor as ISupportInterop;
3157             }
3158
3159             while (currentActivity != null)
3160             {
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;
3165
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);
3172
3173                 // if activity has a fault handler create a batch for it.
3174                 if (currentActivity is CompositeActivity)
3175                 {
3176                     foreach (Activity flowActivity in ((ISupportAlternateFlow)currentActivity).AlternateFlowActivities)
3177                     {
3178                         if (flowActivity is FaultHandlerActivity)
3179                             return interopSupport.BatchCollection.GetBatch(currentActivity);
3180                     }
3181                 }
3182
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);
3187
3188                 currentActivity = currentActivity.Parent;
3189             }
3190
3191             return null;
3192         }
3193
3194         private static string GetNestedExceptionMessage(Exception exp)
3195         {
3196             string expMessage = "";
3197             while (exp != null)
3198             {
3199                 if (expMessage == "")
3200                     expMessage = exp.Message;
3201                 else
3202                     expMessage = expMessage + " " + exp.Message;
3203                 exp = exp.InnerException;
3204             }
3205             return expMessage;
3206         }
3207
3208         #region Internal Events
3209
3210         internal class WorkflowExecutionEventArgs : EventArgs
3211         {
3212             protected WorkflowEventInternal _eventType;
3213
3214             protected WorkflowExecutionEventArgs() { }
3215
3216             internal WorkflowExecutionEventArgs(WorkflowEventInternal eventType)
3217             {
3218                 _eventType = eventType;
3219             }
3220
3221             internal WorkflowEventInternal EventType
3222             {
3223                 get { return _eventType; }
3224             }
3225         }
3226         private event EventHandler<WorkflowExecutionEventArgs> _workflowExecutionEvent;
3227
3228         internal class WorkflowHandlerInvokingEventArgs : WorkflowExecutionEventArgs
3229         {
3230             private Delegate _delegateHandler;
3231
3232             internal WorkflowHandlerInvokingEventArgs(WorkflowEventInternal eventType, Delegate delegateHandler)
3233                 : base(eventType)
3234             {
3235                 _delegateHandler = delegateHandler;
3236             }
3237
3238             internal Delegate DelegateMethod
3239             {
3240                 get { return _delegateHandler; }
3241             }
3242         }
3243
3244         /// <summary>
3245         /// Consolidated event for the majority of the general events.  
3246         /// Filter specific events by WorkflowEventEventArgs.EventType.
3247         /// </summary>
3248         internal event EventHandler<WorkflowExecutor.WorkflowExecutionEventArgs> WorkflowExecutionEvent
3249         {
3250             add
3251             {
3252                 _workflowExecutionEvent += value;
3253             }
3254             remove
3255             {
3256                 _workflowExecutionEvent -= value;
3257             }
3258         }
3259
3260         internal void FireWorkflowExecutionEvent(object sender, WorkflowEventInternal eventType)
3261         {
3262             if (null == sender)
3263                 sender = this;
3264
3265             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3266             if (null != localWorkflowExecutionEvent)
3267                 localWorkflowExecutionEvent(sender, new WorkflowExecutionEventArgs(eventType));
3268         }
3269
3270         internal void FireWorkflowHandlerInvokingEvent(object sender, WorkflowEventInternal eventType, Delegate delegateHandler)
3271         {
3272             if (null == sender)
3273                 sender = this;
3274
3275             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3276             if (null != localWorkflowExecutionEvent)
3277                 localWorkflowExecutionEvent(sender, new WorkflowHandlerInvokingEventArgs(eventType, delegateHandler));
3278         }
3279
3280         internal sealed class WorkflowExecutionSuspendingEventArgs : WorkflowExecutionEventArgs
3281         {
3282             private string _error;
3283
3284             internal WorkflowExecutionSuspendingEventArgs(string error)
3285             {
3286                 _eventType = WorkflowEventInternal.Suspending;
3287                 _error = error;
3288             }
3289
3290             internal string Error
3291             {
3292                 get { return _error; }
3293             }
3294         }
3295
3296         internal sealed class WorkflowExecutionSuspendedEventArgs : WorkflowExecutionEventArgs
3297         {
3298             private string _error;
3299
3300             internal WorkflowExecutionSuspendedEventArgs(string error)
3301             {
3302                 _eventType = WorkflowEventInternal.Suspended;
3303                 _error = error;
3304             }
3305
3306             internal string Error
3307             {
3308                 get { return _error; }
3309             }
3310         }
3311         /// <summary>
3312         /// Fires the WorkflowEvent with an EventType of Suspended and WorkflowSuspendedInternalEventArgs
3313         /// </summary>
3314         /// <param name="info">Reason for the suspension</param>
3315         private void FireWorkflowSuspending(string error)
3316         {
3317             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3318             if (null != localWorkflowExecutionEvent)
3319                 localWorkflowExecutionEvent(this, new WorkflowExecutionSuspendingEventArgs(error));
3320         }
3321
3322         /// <summary>
3323         /// Fires the WorkflowEvent with an EventType of Suspended and WorkflowSuspendInternalEventArgs
3324         /// </summary>
3325         /// <param name="info">Reason for the suspension.</param>
3326         internal void FireWorkflowSuspended(string error)
3327         {
3328             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3329             if (null != localWorkflowExecutionEvent)
3330                 localWorkflowExecutionEvent(this, new WorkflowExecutionSuspendedEventArgs(error));
3331         }
3332
3333
3334         internal class WorkflowExecutionExceptionEventArgs : WorkflowExecutionEventArgs
3335         {
3336             private System.Exception _exception;
3337             private string _currentPath, _originalPath;
3338             private Guid _contextGuid, _parentContextGuid;
3339
3340             internal WorkflowExecutionExceptionEventArgs(Exception exception, string currentPath, string originalPath, Guid contextGuid, Guid parentContextGuid)
3341             {
3342                 if (null == exception)
3343                     throw new ArgumentNullException("exception");
3344
3345                 _exception = exception;
3346                 _currentPath = currentPath;
3347                 _originalPath = originalPath;
3348                 _eventType = WorkflowEventInternal.Exception;
3349                 _contextGuid = contextGuid;
3350                 _parentContextGuid = parentContextGuid;
3351             }
3352
3353             internal Exception Exception
3354             {
3355                 get { return _exception; }
3356             }
3357
3358             internal string CurrentPath
3359             {
3360                 get { return _currentPath; }
3361             }
3362
3363             internal string OriginalPath
3364             {
3365                 get { return _originalPath; }
3366             }
3367
3368             internal Guid ContextGuid
3369             {
3370                 get { return _contextGuid; }
3371             }
3372
3373             internal Guid ParentContextGuid
3374             {
3375                 get { return _parentContextGuid; }
3376             }
3377         }
3378         /// <summary>
3379         /// Fires the WorkflowEvent with an EventType of Exception and WorkflowExceptionInternalEventArgs
3380         /// </summary>
3381         /// <param name="exception">Thrown exception</param>
3382         private void FireWorkflowException(Exception exception, string currentPath, string originalPath, Guid contextGuid, Guid parentContextGuid)
3383         {
3384             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3385             if (null != localWorkflowExecutionEvent)
3386                 localWorkflowExecutionEvent(this, new WorkflowExecutionExceptionEventArgs(exception, currentPath, originalPath, contextGuid, parentContextGuid));
3387         }
3388
3389
3390         internal sealed class WorkflowExecutionTerminatedEventArgs : WorkflowExecutionEventArgs
3391         {
3392             private System.Exception _exception;
3393             private string _error;
3394
3395             internal WorkflowExecutionTerminatedEventArgs(string error)
3396             {
3397                 _error = error;
3398                 _eventType = WorkflowEventInternal.Terminated;
3399             }
3400
3401             internal WorkflowExecutionTerminatedEventArgs(Exception exception)
3402             {
3403                 _exception = exception;
3404                 _eventType = WorkflowEventInternal.Terminated;
3405             }
3406
3407             internal Exception Exception
3408             {
3409                 get { return _exception; }
3410             }
3411
3412             internal string Error
3413             {
3414                 get { return _error; }
3415             }
3416         }
3417         internal sealed class WorkflowExecutionTerminatingEventArgs : WorkflowExecutionEventArgs
3418         {
3419             private System.Exception _exception;
3420             private string _error;
3421
3422             internal WorkflowExecutionTerminatingEventArgs(string error)
3423             {
3424                 _error = error;
3425                 _eventType = WorkflowEventInternal.Terminating;
3426             }
3427
3428             internal WorkflowExecutionTerminatingEventArgs(Exception exception)
3429             {
3430                 if (null == exception)
3431                     throw new ArgumentNullException("exception");
3432
3433                 _exception = exception;
3434                 _eventType = WorkflowEventInternal.Terminating;
3435             }
3436
3437             internal Exception Exception
3438             {
3439                 get { return _exception; }
3440             }
3441
3442             internal string Error
3443             {
3444                 get { return _error; }
3445             }
3446         }
3447         /// <summary>
3448         /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs
3449         /// </summary>
3450         /// <param name="exception">Exception that caused the termination</param>
3451         private void FireWorkflowTerminating(Exception exception)
3452         {
3453             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3454             if (null != localWorkflowExecutionEvent)
3455                 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatingEventArgs(exception));
3456         }
3457         /// <summary>
3458         /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs
3459         /// </summary>
3460         /// <param name="info">Reason for the termination</param>
3461         private void FireWorkflowTerminating(string error)
3462         {
3463             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3464             if (null != localWorkflowExecutionEvent)
3465                 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatingEventArgs(error));
3466         }
3467         /// <summary>
3468         /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs
3469         /// </summary>
3470         /// <param name="exception">Exception that caused the termination</param>
3471         internal void FireWorkflowTerminated(Exception exception)
3472         {
3473             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3474             if (null != localWorkflowExecutionEvent)
3475                 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatedEventArgs(exception));
3476         }
3477         /// <summary>
3478         /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs
3479         /// </summary>
3480         /// <param name="info">Reason for the termination</param>
3481         internal void FireWorkflowTerminated(string error)
3482         {
3483             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3484             if (null != localWorkflowExecutionEvent)
3485                 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatedEventArgs(error));
3486         }
3487
3488
3489
3490         internal class DynamicUpdateEventArgs : WorkflowExecutionEventArgs
3491         {
3492             private IList<WorkflowChangeAction> _changeActions = new List<WorkflowChangeAction>();
3493
3494             internal DynamicUpdateEventArgs(IList<WorkflowChangeAction> changeActions, WorkflowEventInternal eventType)
3495             {
3496                 _changeActions = changeActions;
3497                 _eventType = eventType;
3498             }
3499
3500             internal IList<WorkflowChangeAction> ChangeActions
3501             {
3502                 get { return _changeActions; }
3503             }
3504         }
3505         /// <summary>
3506         /// Signals that a dynamic update is starting.
3507         /// </summary>
3508         private void FireDynamicUpdateBegin(IList<WorkflowChangeAction> changeActions)
3509         {
3510             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3511             if (null != localWorkflowExecutionEvent)
3512                 localWorkflowExecutionEvent(this, new DynamicUpdateEventArgs(changeActions, WorkflowEventInternal.DynamicChangeBegin));
3513         }
3514
3515
3516         /// <summary>
3517         /// Signals that a dynamic update has errored and rolledback.
3518         /// </summary>
3519         private void FireDynamicUpdateRollback(IList<WorkflowChangeAction> changeActions)
3520         {
3521             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3522             if (null != localWorkflowExecutionEvent)
3523                 localWorkflowExecutionEvent(this, new DynamicUpdateEventArgs(changeActions, WorkflowEventInternal.DynamicChangeRollback));
3524         }
3525
3526
3527         /// <summary>
3528         /// Signals that a dynamic update has completed successfully.
3529         /// </summary>
3530         private void FireDynamicUpdateCommit(IList<WorkflowChangeAction> changeActions)
3531         {
3532             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3533             if (null != localWorkflowExecutionEvent)
3534                 localWorkflowExecutionEvent(this, new DynamicUpdateEventArgs(changeActions, WorkflowEventInternal.DynamicChangeCommit));
3535         }
3536
3537         internal class ActivityStatusChangeEventArgs : WorkflowExecutionEventArgs
3538         {
3539             private Activity _activity;
3540
3541             internal ActivityStatusChangeEventArgs(Activity activity)
3542             {
3543                 _activity = activity;
3544                 _eventType = WorkflowEventInternal.ActivityStatusChange;
3545             }
3546
3547             internal Activity Activity
3548             {
3549                 get { return _activity; }
3550             }
3551         }
3552
3553         internal class ActivityExecutingEventArgs : WorkflowExecutionEventArgs
3554         {
3555             private Activity _activity;
3556
3557             internal ActivityExecutingEventArgs(Activity activity)
3558             {
3559                 _activity = activity;
3560                 _eventType = WorkflowEventInternal.ActivityExecuting;
3561             }
3562
3563             internal Activity Activity
3564             {
3565                 get { return _activity; }
3566             }
3567         }
3568         /// <summary>
3569         /// Signals that an activity has changed status.  
3570         /// This event applies to all status change events 
3571         /// for all activities in the workflow.
3572         /// </summary>
3573         private void FireActivityStatusChange(object sender, Activity activity)
3574         {
3575             ActivityStatusChangeEventArgs args = new ActivityStatusChangeEventArgs(activity);
3576
3577             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3578             if (null != localWorkflowExecutionEvent)
3579                 localWorkflowExecutionEvent(sender, args);
3580         }
3581
3582         private void FireActivityExecuting(object sender, Activity activity)
3583         {
3584             ActivityExecutingEventArgs args = new ActivityExecutingEventArgs(activity);
3585
3586             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3587             if (null != localWorkflowExecutionEvent)
3588                 localWorkflowExecutionEvent(sender, args);
3589         }
3590
3591         internal class UserTrackPointEventArgs : WorkflowExecutionEventArgs
3592         {
3593             Activity _activity;
3594             string _key;
3595             object _args;
3596
3597             internal UserTrackPointEventArgs(Activity activity, string key, object args)
3598             {
3599                 if (null == activity)
3600                     throw new ArgumentNullException("activity");
3601
3602                 _activity = activity;
3603                 //
3604                 // args may be null, user code can send non null value
3605                 _args = args;
3606                 _eventType = WorkflowEventInternal.UserTrackPoint;
3607                 _key = key;
3608             }
3609
3610             internal Activity Activity
3611             {
3612                 get { return _activity; }
3613             }
3614
3615             internal string Key
3616             {
3617                 get { return _key; }
3618             }
3619
3620             internal object Args
3621             {
3622                 get { return _args; }
3623             }
3624         }
3625
3626         private void FireUserTrackPoint(Activity activity, string key, object args)
3627         {
3628             EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent;
3629             if (null != localWorkflowExecutionEvent)
3630                 localWorkflowExecutionEvent(this, new UserTrackPointEventArgs(activity, key, args));
3631         }
3632
3633
3634         #endregion Internal Events
3635     }
3636
3637     internal class ScheduleWork : IDisposable
3638     {
3639         internal class ScheduleInfo
3640         {
3641             public bool scheduleWork;
3642             public WorkflowExecutor executor;
3643             public bool suppress;
3644             public ScheduleInfo(WorkflowExecutor executor, bool suppress)
3645             {
3646                 this.suppress = suppress;
3647                 scheduleWork = false;
3648                 this.executor = executor;
3649             }
3650         }
3651         [ThreadStatic]
3652         protected static ScheduleInfo scheduleInfo;
3653         protected ScheduleInfo oldValue;
3654
3655         public ScheduleWork(WorkflowExecutor executor)
3656         {
3657             oldValue = scheduleInfo;
3658             scheduleInfo = new ScheduleInfo(executor, false);
3659         }
3660
3661         public ScheduleWork(WorkflowExecutor executor, bool suppress)
3662         {
3663             oldValue = scheduleInfo;
3664             scheduleInfo = new ScheduleInfo(executor, suppress);
3665         }
3666
3667         static public bool NeedsService
3668         {
3669             //           get
3670             //          {
3671             //               Debug.Assert(ScheduleWork.scheduleInfo != null);
3672             //               return ScheduleWork.scheduleInfo.scheduleWork;
3673             //           }
3674             set
3675             {
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;
3679             }
3680         }
3681         static public WorkflowExecutor Executor
3682         {
3683             //           get
3684             //           {
3685             //               Debug.Assert(ScheduleWork.scheduleInfo != null);
3686             //               return ScheduleWork.scheduleInfo.executor;
3687             //           }
3688             set
3689             {
3690                 Debug.Assert(ScheduleWork.scheduleInfo != null);
3691                 ScheduleWork.scheduleInfo.executor = value;
3692             }
3693         }
3694         #region IDisposable Members
3695
3696         public virtual void Dispose()
3697         {
3698             if ((scheduleInfo.scheduleWork) && (!scheduleInfo.suppress))
3699             {
3700                 scheduleInfo.executor.RequestHostingService();
3701             }
3702             scheduleInfo = oldValue;
3703         }
3704
3705         #endregion
3706     }
3707 }