Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Activities / System / Activities / Runtime / ActivityExecutor.cs
blob417960427bfc30cc142d10ff973cd735255bf300
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Activities.Runtime
7 using System;
8 using System.Activities.Debugger;
9 using System.Activities.DynamicUpdate;
10 using System.Activities.Hosting;
11 using System.Activities.Tracking;
12 using System.Collections.Generic;
13 using System.Collections.ObjectModel;
14 using System.Diagnostics.CodeAnalysis;
15 using System.Globalization;
16 using System.Runtime;
17 using System.Runtime.Diagnostics;
18 using System.Runtime.DurableInstancing;
19 using System.Runtime.Serialization;
20 using System.Security;
21 using System.Threading;
22 using System.Transactions;
24 [DataContract(Name = XD.Executor.Name, Namespace = XD.Runtime.Namespace)]
25 class ActivityExecutor : IEnlistmentNotification
27 static ReadOnlyCollection<BookmarkInfo> emptyBookmarkInfoCollection;
29 BookmarkManager bookmarkManager;
31 BookmarkScopeManager bookmarkScopeManager;
33 DebugController debugController;
34 bool hasRaisedWorkflowStarted;
36 Guid instanceId;
37 bool instanceIdSet;
39 Activity rootElement;
40 Dictionary<ActivityInstance, AsyncOperationContext> activeOperations;
41 WorkflowInstance host;
43 ActivityInstanceMap instanceMap;
44 MappableObjectManager mappableObjectManager;
46 bool hasTrackedStarted;
48 long nextTrackingRecordNumber;
50 ActivityInstance rootInstance;
51 List<ActivityInstance> executingSecondaryRootInstances;
53 Scheduler scheduler;
55 Exception completionException;
57 bool shouldRaiseMainBodyComplete;
59 long lastInstanceId;
61 LocationEnvironment rootEnvironment;
63 IDictionary<string, object> workflowOutputs;
65 Bookmark mainRootCompleteBookmark;
67 // This field reflects our best guess at our future completion state.
68 // We set it when the main root completes but might revise the value
69 // depending on what actions are taken (like CancelRootActivity being
70 // called).
71 ActivityInstanceState executionState;
73 Queue<PersistenceWaiter> persistenceWaiters;
75 Quack<TransactionContextWaiter> transactionContextWaiters;
76 RuntimeTransactionData runtimeTransaction;
78 bool isAbortPending;
79 bool isDisposed;
80 bool shouldPauseOnCanPersist;
82 bool isTerminatePending;
83 Exception terminationPendingException;
85 int noPersistCount;
87 SymbolResolver symbolResolver;
89 bool throwDuringSerialization;
91 CodeActivityContext cachedResolutionContext;
92 Location ignorableResultLocation;
94 // work item pools (for performance)
95 Pool<EmptyWorkItem> emptyWorkItemPool;
96 Pool<ExecuteActivityWorkItem> executeActivityWorkItemPool;
97 Pool<ExecuteSynchronousExpressionWorkItem> executeSynchronousExpressionWorkItemPool;
98 Pool<CompletionCallbackWrapper.CompletionWorkItem> completionWorkItemPool;
99 Pool<ResolveNextArgumentWorkItem> resolveNextArgumentWorkItemPool;
101 // context pools (for performance)
102 Pool<CodeActivityContext> codeActivityContextPool;
103 Pool<NativeActivityContext> nativeActivityContextPool;
105 // root handles (for default Tx, Correlation, etc)
106 ExecutionPropertyManager rootPropertyManager;
108 // This list keeps track of handles that are created and initialized.
109 List<Handle> handles;
111 bool persistExceptions;
112 bool havePersistExceptionsValue;
114 public ActivityExecutor(WorkflowInstance host)
116 Fx.Assert(host != null, "There must be a host.");
118 this.host = host;
119 this.WorkflowIdentity = host.DefinitionIdentity;
121 this.bookmarkManager = new BookmarkManager();
122 this.scheduler = new Scheduler(new Scheduler.Callbacks(this));
125 public Pool<EmptyWorkItem> EmptyWorkItemPool
129 if (this.emptyWorkItemPool == null)
131 this.emptyWorkItemPool = new PoolOfEmptyWorkItems();
134 return this.emptyWorkItemPool;
138 Pool<ExecuteActivityWorkItem> ExecuteActivityWorkItemPool
142 if (this.executeActivityWorkItemPool == null)
144 this.executeActivityWorkItemPool = new PoolOfExecuteActivityWorkItems();
147 return this.executeActivityWorkItemPool;
151 public Pool<ExecuteSynchronousExpressionWorkItem> ExecuteSynchronousExpressionWorkItemPool
155 if (this.executeSynchronousExpressionWorkItemPool == null)
157 this.executeSynchronousExpressionWorkItemPool = new PoolOfExecuteSynchronousExpressionWorkItems();
160 return this.executeSynchronousExpressionWorkItemPool;
164 public Pool<CompletionCallbackWrapper.CompletionWorkItem> CompletionWorkItemPool
168 if (this.completionWorkItemPool == null)
170 this.completionWorkItemPool = new PoolOfCompletionWorkItems();
173 return this.completionWorkItemPool;
177 public Pool<CodeActivityContext> CodeActivityContextPool
181 if (this.codeActivityContextPool == null)
183 this.codeActivityContextPool = new PoolOfCodeActivityContexts();
186 return this.codeActivityContextPool;
190 public Pool<NativeActivityContext> NativeActivityContextPool
194 if (this.nativeActivityContextPool == null)
196 this.nativeActivityContextPool = new PoolOfNativeActivityContexts();
199 return this.nativeActivityContextPool;
203 public Pool<ResolveNextArgumentWorkItem> ResolveNextArgumentWorkItemPool
207 if (this.resolveNextArgumentWorkItemPool == null)
209 this.resolveNextArgumentWorkItemPool = new PoolOfResolveNextArgumentWorkItems();
212 return this.resolveNextArgumentWorkItemPool;
216 public Activity RootActivity
220 return this.rootElement;
224 public bool IsInitialized
228 return this.host != null;
232 public bool HasPendingTrackingRecords
236 return this.host.HasTrackingParticipant && this.host.TrackingProvider.HasPendingRecords;
240 public bool ShouldTrack
244 return this.host.HasTrackingParticipant && this.host.TrackingProvider.ShouldTrack;
248 public bool ShouldTrackBookmarkResumptionRecords
252 return this.host.HasTrackingParticipant && this.host.TrackingProvider.ShouldTrackBookmarkResumptionRecords;
256 public bool ShouldTrackActivityScheduledRecords
260 return this.host.HasTrackingParticipant && this.host.TrackingProvider.ShouldTrackActivityScheduledRecords;
264 public bool ShouldTrackActivityStateRecords
268 return this.host.HasTrackingParticipant && this.host.TrackingProvider.ShouldTrackActivityStateRecords;
272 public bool ShouldTrackActivityStateRecordsExecutingState
276 return this.host.HasTrackingParticipant && this.host.TrackingProvider.ShouldTrackActivityStateRecordsExecutingState;
280 public bool ShouldTrackActivityStateRecordsClosedState
284 return this.host.HasTrackingParticipant && this.host.TrackingProvider.ShouldTrackActivityStateRecordsClosedState;
288 public bool ShouldTrackCancelRequestedRecords
292 return this.host.HasTrackingParticipant && this.host.TrackingProvider.ShouldTrackCancelRequestedRecords;
296 public bool ShouldTrackFaultPropagationRecords
300 return this.host.HasTrackingParticipant && this.host.TrackingProvider.ShouldTrackFaultPropagationRecords;
304 public SymbolResolver SymbolResolver
308 if (this.symbolResolver == null)
312 this.symbolResolver = this.host.GetExtension<SymbolResolver>();
314 catch (Exception e)
316 if (Fx.IsFatal(e))
318 throw;
320 throw FxTrace.Exception.AsError(new CallbackException(SR.CallbackExceptionFromHostGetExtension(this.WorkflowInstanceId), e));
324 return this.symbolResolver;
328 // This only gets accessed by root activities which are resolving arguments. Since that
329 // could at most be the real root and any secondary roots it doesn't seem necessary
330 // to cache the empty environment.
331 public LocationEnvironment EmptyEnvironment
335 return new LocationEnvironment(this, null);
339 public ActivityInstanceState State
343 if ((this.executingSecondaryRootInstances != null && this.executingSecondaryRootInstances.Count > 0) ||
344 (this.rootInstance != null && !this.rootInstance.IsCompleted))
346 // As long as some root is executing we need to return executing
347 return ActivityInstanceState.Executing;
349 else
351 return this.executionState;
356 [DataMember(EmitDefaultValue = false)]
357 public WorkflowIdentity WorkflowIdentity
359 get;
360 internal set;
363 [DataMember]
364 public Guid WorkflowInstanceId
368 if (!this.instanceIdSet)
370 WorkflowInstanceId = this.host.Id;
371 if (!this.instanceIdSet)
373 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.EmptyIdReturnedFromHost(this.host.GetType())));
377 return this.instanceId;
379 // Internal visibility for partial trust serialization purposes only.
380 internal set
382 this.instanceId = value;
383 this.instanceIdSet = value != Guid.Empty;
387 public Exception TerminationException
391 return this.completionException;
395 public bool IsRunning
399 return !this.isDisposed && this.scheduler.IsRunning;
403 public bool IsPersistable
407 return this.noPersistCount == 0;
411 public bool IsAbortPending
415 return this.isAbortPending;
419 public bool IsIdle
423 return this.isDisposed || this.scheduler.IsIdle;
427 public bool IsTerminatePending
431 return this.isTerminatePending;
435 public bool KeysAllowed
439 return this.host.SupportsInstanceKeys;
443 public IDictionary<string, object> WorkflowOutputs
447 return this.workflowOutputs;
451 internal BookmarkScopeManager BookmarkScopeManager
455 if (this.bookmarkScopeManager == null)
457 this.bookmarkScopeManager = new BookmarkScopeManager();
460 return this.bookmarkScopeManager;
464 internal BookmarkScopeManager RawBookmarkScopeManager
468 return this.bookmarkScopeManager;
472 internal BookmarkManager RawBookmarkManager
476 return this.bookmarkManager;
480 internal MappableObjectManager MappableObjectManager
484 if (this.mappableObjectManager == null)
486 this.mappableObjectManager = new MappableObjectManager();
489 return this.mappableObjectManager;
493 public bool RequiresTransactionContextWaiterExists
497 return this.transactionContextWaiters != null && this.transactionContextWaiters.Count > 0 && this.transactionContextWaiters[0].IsRequires;
501 public bool HasRuntimeTransaction
503 get { return this.runtimeTransaction != null; }
506 public Transaction CurrentTransaction
510 if (this.runtimeTransaction != null)
512 return this.runtimeTransaction.ClonedTransaction;
514 else
516 return null;
521 static ReadOnlyCollection<BookmarkInfo> EmptyBookmarkInfoCollection
525 if (emptyBookmarkInfoCollection == null)
527 emptyBookmarkInfoCollection = new ReadOnlyCollection<BookmarkInfo>(new List<BookmarkInfo>(0));
530 return emptyBookmarkInfoCollection;
534 [DataMember(Name = XD.Executor.BookmarkManager, EmitDefaultValue = false)]
535 internal BookmarkManager SerializedBookmarkManager
537 get { return this.bookmarkManager; }
538 set { this.bookmarkManager = value; }
541 [DataMember(Name = XD.Executor.BookmarkScopeManager, EmitDefaultValue = false)]
542 internal BookmarkScopeManager SerializedBookmarkScopeManager
544 get { return this.bookmarkScopeManager; }
545 set { this.bookmarkScopeManager = value; }
548 [DataMember(EmitDefaultValue = false, Name = "hasTrackedStarted")]
549 internal bool SerializedHasTrackedStarted
551 get { return this.hasTrackedStarted; }
552 set { this.hasTrackedStarted = value; }
555 [DataMember(EmitDefaultValue = false, Name = "nextTrackingRecordNumber")]
556 internal long SerializedNextTrackingRecordNumber
558 get { return this.nextTrackingRecordNumber; }
559 set { this.nextTrackingRecordNumber = value; }
562 [DataMember(Name = XD.Executor.RootInstance, EmitDefaultValue = false)]
563 internal ActivityInstance SerializedRootInstance
565 get { return this.rootInstance; }
566 set { this.rootInstance = value; }
569 [DataMember(Name = XD.Executor.SchedulerMember, EmitDefaultValue = false)]
570 internal Scheduler SerializedScheduler
572 get { return this.scheduler; }
573 set { this.scheduler = value; }
576 [DataMember(Name = XD.Executor.ShouldRaiseMainBodyComplete, EmitDefaultValue = false)]
577 internal bool SerializedShouldRaiseMainBodyComplete
579 get { return this.shouldRaiseMainBodyComplete; }
580 set { this.shouldRaiseMainBodyComplete = value; }
583 [DataMember(Name = XD.Executor.LastInstanceId, EmitDefaultValue = false)]
584 internal long SerializedLastInstanceId
586 get { return this.lastInstanceId; }
587 set { this.lastInstanceId = value; }
590 [DataMember(Name = XD.Executor.RootEnvironment, EmitDefaultValue = false)]
591 internal LocationEnvironment SerializedRootEnvironment
593 get { return this.rootEnvironment; }
594 set { this.rootEnvironment = value; }
597 [DataMember(Name = XD.Executor.WorkflowOutputs, EmitDefaultValue = false)]
598 internal IDictionary<string, object> SerializedWorkflowOutputs
600 get { return this.workflowOutputs; }
601 set { this.workflowOutputs = value; }
604 [DataMember(Name = XD.Executor.MainRootCompleteBookmark, EmitDefaultValue = false)]
605 internal Bookmark SerializedMainRootCompleteBookmark
607 get { return this.mainRootCompleteBookmark; }
608 set { this.mainRootCompleteBookmark = value; }
611 [DataMember(Name = XD.Executor.ExecutionState, EmitDefaultValue = false)]
612 internal ActivityInstanceState SerializedExecutionState
614 get { return this.executionState; }
615 set { this.executionState = value; }
618 [DataMember(EmitDefaultValue = false, Name = "handles")]
619 internal List<Handle> SerializedHandles
621 get { return this.handles; }
622 set { this.handles = value; }
625 internal bool PersistExceptions
629 if (!havePersistExceptionsValue)
631 // If we have an ExceptionPersistenceExtension, set our cached "persistExceptions" value to its
632 // PersistExceptions property. If we don't have the extension, set the cached value to true.
633 ExceptionPersistenceExtension extension = this.host.GetExtension<ExceptionPersistenceExtension>();
634 if (extension != null)
636 this.persistExceptions = extension.PersistExceptions;
638 else
640 this.persistExceptions = true;
643 this.havePersistExceptionsValue = true;
645 return this.persistExceptions;
649 [DataMember(Name = XD.Executor.CompletionException, EmitDefaultValue = false)]
650 internal Exception SerializedCompletionException
654 if (this.PersistExceptions)
656 return this.completionException;
658 else
660 return null;
665 this.completionException = value;
669 [DataMember(Name = XD.Executor.TransactionContextWaiters, EmitDefaultValue = false)]
670 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "Used by serialization")]
671 internal TransactionContextWaiter[] SerializedTransactionContextWaiters
675 if (this.transactionContextWaiters != null && this.transactionContextWaiters.Count > 0)
677 return this.transactionContextWaiters.ToArray();
679 else
681 return null;
686 Fx.Assert(value != null, "We don't serialize out null.");
687 this.transactionContextWaiters = new Quack<TransactionContextWaiter>(value);
691 [DataMember(Name = XD.Executor.PersistenceWaiters, EmitDefaultValue = false)]
692 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "Used by serialization")]
693 internal Queue<PersistenceWaiter> SerializedPersistenceWaiters
697 if (this.persistenceWaiters == null || this.persistenceWaiters.Count == 0)
699 return null;
701 else
703 return this.persistenceWaiters;
708 Fx.Assert(value != null, "We don't serialize out null.");
709 this.persistenceWaiters = value;
713 [DataMember(Name = XD.Executor.SecondaryRootInstances, EmitDefaultValue = false)]
714 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "Used by serialization")]
715 internal List<ActivityInstance> SerializedExecutingSecondaryRootInstances
719 if (this.executingSecondaryRootInstances != null && this.executingSecondaryRootInstances.Count > 0)
721 return this.executingSecondaryRootInstances;
723 else
725 return null;
730 Fx.Assert(value != null, "We don't serialize out null.");
731 this.executingSecondaryRootInstances = value;
735 [DataMember(Name = XD.Executor.MappableObjectManager, EmitDefaultValue = false)]
736 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "Used by serialization")]
737 internal MappableObjectManager SerializedMappableObjectManager
741 if (this.mappableObjectManager == null || this.mappableObjectManager.Count == 0)
743 return null;
746 return this.mappableObjectManager;
751 Fx.Assert(value != null, "value from serialization should never be null");
752 this.mappableObjectManager = value;
756 // map from activity names to (active) associated activity instances
757 [DataMember(Name = XD.Executor.ActivityInstanceMap, EmitDefaultValue = false)]
758 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "called from serialization")]
759 internal ActivityInstanceMap SerializedProgramMapping
763 ThrowIfNonSerializable();
765 if (this.instanceMap == null && !this.isDisposed)
767 this.instanceMap = new ActivityInstanceMap();
769 this.rootInstance.FillInstanceMap(this.instanceMap);
770 this.scheduler.FillInstanceMap(this.instanceMap);
772 if (this.executingSecondaryRootInstances != null && this.executingSecondaryRootInstances.Count > 0)
774 foreach (ActivityInstance secondaryRoot in this.executingSecondaryRootInstances)
776 secondaryRoot.FillInstanceMap(this.instanceMap);
778 LocationEnvironment environment = secondaryRoot.Environment;
780 if (secondaryRoot.IsEnvironmentOwner)
782 environment = environment.Parent;
785 while (environment != null)
787 if (environment.HasOwnerCompleted)
789 this.instanceMap.AddEntry(environment, true);
792 environment = environment.Parent;
798 return this.instanceMap;
803 Fx.Assert(value != null, "value from serialization should never be null");
804 this.instanceMap = value;
808 // may be null
809 internal ExecutionPropertyManager RootPropertyManager
813 return this.rootPropertyManager;
817 [DataMember(Name = XD.ActivityInstance.PropertyManager, EmitDefaultValue = false)]
818 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode, Justification = "Called from Serialization")]
819 internal ExecutionPropertyManager SerializedPropertyManager
823 return this.rootPropertyManager;
827 Fx.Assert(value != null, "We don't emit the default value so this should never be null.");
828 this.rootPropertyManager = value;
829 this.rootPropertyManager.OnDeserialized(null, null, null, this);
833 public void ThrowIfNonSerializable()
835 if (this.throwDuringSerialization)
837 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.StateCannotBeSerialized(this.WorkflowInstanceId)));
841 public void MakeNonSerializable()
843 this.throwDuringSerialization = true;
846 public IList<ActivityBlockingUpdate> GetActivitiesBlockingUpdate(DynamicUpdateMap updateMap)
848 Fx.Assert(updateMap != null, "UpdateMap must not be null.");
849 Collection<ActivityBlockingUpdate> result = null;
850 this.instanceMap.GetActivitiesBlockingUpdate(updateMap, this.executingSecondaryRootInstances, ref result);
851 return result;
854 public void UpdateInstancePhase1(DynamicUpdateMap updateMap, Activity targetDefinition, ref Collection<ActivityBlockingUpdate> updateErrors)
856 Fx.Assert(updateMap != null, "UpdateMap must not be null.");
857 this.instanceMap.UpdateRawInstance(updateMap, targetDefinition, this.executingSecondaryRootInstances, ref updateErrors);
860 public void UpdateInstancePhase2(DynamicUpdateMap updateMap, ref Collection<ActivityBlockingUpdate> updateErrors)
862 this.instanceMap.UpdateInstanceByActivityParticipation(this, updateMap, ref updateErrors);
865 internal List<Handle> Handles
867 get { return this.handles; }
870 // evaluate an argument/variable expression using fast-path optimizations
871 public void ExecuteInResolutionContextUntyped(ActivityInstance parentInstance, ActivityWithResult expressionActivity, long instanceId, Location resultLocation)
873 if (this.cachedResolutionContext == null)
875 this.cachedResolutionContext = new CodeActivityContext(parentInstance, this);
878 this.cachedResolutionContext.Reinitialize(parentInstance, this, expressionActivity, instanceId);
881 this.ignorableResultLocation = resultLocation;
882 resultLocation.Value = expressionActivity.InternalExecuteInResolutionContextUntyped(this.cachedResolutionContext);
884 finally
886 if (!expressionActivity.UseOldFastPath)
888 // The old fast path allows WorkflowDataContexts to escape up one level, because
889 // the resolution context uses the parent's ActivityInstance. We support that for
890 // back-compat, but don't allow it on new fast-path activities.
891 this.cachedResolutionContext.DisposeDataContext();
894 this.cachedResolutionContext.Dispose();
895 this.ignorableResultLocation = null;
899 // evaluate an argument/variable expression using fast-path optimizations
900 public T ExecuteInResolutionContext<T>(ActivityInstance parentInstance, Activity<T> expressionActivity)
902 Fx.Assert(expressionActivity.UseOldFastPath, "New fast path should be scheduled via ExecuteSynchronousExpressionWorkItem, which calls the Untyped overload");
904 if (this.cachedResolutionContext == null)
906 this.cachedResolutionContext = new CodeActivityContext(parentInstance, this);
909 this.cachedResolutionContext.Reinitialize(parentInstance, this, expressionActivity, parentInstance.InternalId);
910 T result;
913 result = expressionActivity.InternalExecuteInResolutionContext(this.cachedResolutionContext);
915 finally
917 this.cachedResolutionContext.Dispose();
919 return result;
922 internal void ExecuteSynchronousWorkItem(WorkItem workItem)
924 workItem.Release(this);
927 bool result = workItem.Execute(this, bookmarkManager);
928 Fx.AssertAndThrow(result, "Synchronous work item should not yield the scheduler");
930 finally
932 workItem.Dispose(this);
936 internal void ExitNoPersistForExceptionPropagation()
938 if (!this.PersistExceptions)
940 ExitNoPersist();
944 // This is called by RuntimeArgument.GetLocation (via ActivityContext.GetIgnorableResultLocation)
945 // when the user tries to access the Result argument on an activity being run with SkipArgumentResolution.
946 internal Location GetIgnorableResultLocation(RuntimeArgument resultArgument)
948 Fx.Assert(resultArgument.Owner == this.cachedResolutionContext.Activity, "GetIgnorableResultLocation should only be called for activity in resolution context");
949 Fx.Assert(this.ignorableResultLocation != null, "ResultLocation should have been passed in to ExecuteInResolutionContext");
951 return this.ignorableResultLocation;
954 // Whether it is being debugged.
955 bool IsDebugged()
957 if (this.debugController == null)
959 #if DEBUG
960 if (Fx.StealthDebugger)
962 return false;
964 #endif
965 if (System.Diagnostics.Debugger.IsAttached)
967 this.debugController = new DebugController(this.host);
970 return this.debugController != null;
973 public void DebugActivityCompleted(ActivityInstance instance)
975 if (this.debugController != null) // Don't use IsDebugged() for perf reason.
977 this.debugController.ActivityCompleted(instance);
981 public void AddTrackingRecord(TrackingRecord record)
983 Fx.Assert(this.host.TrackingProvider != null, "We should only add records if we have a tracking provider.");
985 this.host.TrackingProvider.AddRecord(record);
988 public bool ShouldTrackActivity(string name)
990 Fx.Assert(this.host.TrackingProvider != null, "We should only add records if we have a tracking provider.");
991 return this.host.TrackingProvider.ShouldTrackActivity(name);
994 public IAsyncResult BeginTrackPendingRecords(AsyncCallback callback, object state)
996 Fx.Assert(this.host.TrackingProvider != null, "We should only try to track if we have a tracking provider.");
997 return this.host.BeginFlushTrackingRecordsInternal(callback, state);
1000 public void EndTrackPendingRecords(IAsyncResult result)
1002 Fx.Assert(this.host.TrackingProvider != null, "We should only try to track if we have a tracking provider.");
1003 this.host.EndFlushTrackingRecordsInternal(result);
1006 internal IDictionary<string, LocationInfo> GatherMappableVariables()
1008 if (this.mappableObjectManager != null)
1010 return this.MappableObjectManager.GatherMappableVariables();
1012 return null;
1015 internal void OnSchedulerThreadAcquired()
1017 if (this.IsDebugged() && !this.hasRaisedWorkflowStarted)
1019 this.hasRaisedWorkflowStarted = true;
1020 this.debugController.WorkflowStarted();
1024 public void Dispose()
1026 Dispose(true);
1029 void Dispose(bool aborting)
1031 if (!this.isDisposed)
1033 if (this.debugController != null) // Don't use IsDebugged() because it may create debugController unnecessarily.
1035 this.debugController.WorkflowCompleted();
1036 this.debugController = null;
1039 if (this.activeOperations != null && this.activeOperations.Count > 0)
1041 Fx.Assert(aborting, "shouldn't get here in the g----ful close case");
1042 Abort(new OperationCanceledException());
1044 else
1046 this.scheduler.ClearAllWorkItems(this);
1048 if (!aborting)
1050 this.scheduler = null;
1051 this.bookmarkManager = null;
1052 this.lastInstanceId = 0;
1053 this.rootInstance = null;
1056 this.isDisposed = true;
1061 // Called from an arbitrary thread
1062 public void PauseWhenPersistable()
1064 this.shouldPauseOnCanPersist = true;
1067 public void EnterNoPersist()
1069 this.noPersistCount++;
1071 if (TD.EnterNoPersistBlockIsEnabled())
1073 TD.EnterNoPersistBlock();
1077 public void ExitNoPersist()
1079 this.noPersistCount--;
1081 if (TD.ExitNoPersistBlockIsEnabled())
1083 TD.ExitNoPersistBlock();
1086 if (this.shouldPauseOnCanPersist && this.IsPersistable)
1088 // shouldPauseOnCanPersist is reset at the next pause
1089 // notification
1090 this.scheduler.Pause();
1094 void IEnlistmentNotification.Commit(Enlistment enlistment)
1096 // Because of ordering we might get this notification after we've already
1097 // determined the outcome
1099 // Get a local copy of this.runtimeTransaction because it is possible for
1100 // this.runtimeTransaction to be nulled out between the time we check for null
1101 // and the time we try to lock it.
1102 RuntimeTransactionData localRuntimeTransaction = this.runtimeTransaction;
1104 if (localRuntimeTransaction != null)
1106 AsyncWaitHandle completionEvent = null;
1108 lock (localRuntimeTransaction)
1110 completionEvent = localRuntimeTransaction.CompletionEvent;
1112 localRuntimeTransaction.TransactionStatus = TransactionStatus.Committed;
1115 enlistment.Done();
1117 if (completionEvent != null)
1119 completionEvent.Set();
1122 else
1124 enlistment.Done();
1128 void IEnlistmentNotification.InDoubt(Enlistment enlistment)
1130 ((IEnlistmentNotification)this).Rollback(enlistment);
1133 //Note - There is a scenario in the TransactedReceiveScope while dealing with server side WCF dispatcher created transactions,
1134 //the activity instance will end up calling BeginCommit before finishing up its execution. By this we allow the executing TransactedReceiveScope activity to
1135 //complete and the executor is "free" to respond to this Prepare notification as part of the commit processing of that server side transaction
1136 void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
1138 // Because of ordering we might get this notification after we've already
1139 // determined the outcome
1141 // Get a local copy of this.runtimeTransaction because it is possible for
1142 // this.runtimeTransaction to be nulled out between the time we check for null
1143 // and the time we try to lock it.
1144 RuntimeTransactionData localRuntimeTransaction = this.runtimeTransaction;
1146 if (localRuntimeTransaction != null)
1148 bool callPrepared = false;
1150 lock (localRuntimeTransaction)
1152 if (localRuntimeTransaction.HasPrepared)
1154 callPrepared = true;
1156 else
1158 localRuntimeTransaction.PendingPreparingEnlistment = preparingEnlistment;
1162 if (callPrepared)
1164 preparingEnlistment.Prepared();
1167 else
1169 preparingEnlistment.Prepared();
1173 void IEnlistmentNotification.Rollback(Enlistment enlistment)
1175 // Because of ordering we might get this notification after we've already
1176 // determined the outcome
1178 // Get a local copy of this.runtimeTransaction because it is possible for
1179 // this.runtimeTransaction to be nulled out between the time we check for null
1180 // and the time we try to lock it.
1181 RuntimeTransactionData localRuntimeTransaction = this.runtimeTransaction;
1183 if (localRuntimeTransaction != null)
1185 AsyncWaitHandle completionEvent = null;
1187 lock (localRuntimeTransaction)
1189 completionEvent = localRuntimeTransaction.CompletionEvent;
1191 localRuntimeTransaction.TransactionStatus = TransactionStatus.Aborted;
1194 enlistment.Done();
1196 if (completionEvent != null)
1198 completionEvent.Set();
1201 else
1203 enlistment.Done();
1207 public void RequestTransactionContext(ActivityInstance instance, bool isRequires, RuntimeTransactionHandle handle, Action<NativeActivityTransactionContext, object> callback, object state)
1209 if (isRequires)
1211 EnterNoPersist();
1214 if (this.transactionContextWaiters == null)
1216 this.transactionContextWaiters = new Quack<TransactionContextWaiter>();
1219 TransactionContextWaiter waiter = new TransactionContextWaiter(instance, isRequires, handle, new TransactionContextWaiterCallbackWrapper(callback, instance), state);
1221 if (isRequires)
1223 Fx.Assert(this.transactionContextWaiters.Count == 0 || !this.transactionContextWaiters[0].IsRequires, "Either we don't have any waiters or the first one better not be IsRequires == true");
1225 this.transactionContextWaiters.PushFront(waiter);
1227 else
1229 this.transactionContextWaiters.Enqueue(waiter);
1232 instance.IncrementBusyCount();
1233 instance.WaitingForTransactionContext = true;
1236 public void SetTransaction(RuntimeTransactionHandle handle, Transaction transaction, ActivityInstance isolationScope, ActivityInstance transactionOwner)
1238 this.runtimeTransaction = new RuntimeTransactionData(handle, transaction, isolationScope);
1239 EnterNoPersist();
1241 // no more work to do for a host-declared transaction
1242 if (transactionOwner == null)
1244 return;
1247 Exception abortException = null;
1251 transaction.EnlistVolatile(this, EnlistmentOptions.EnlistDuringPrepareRequired);
1253 catch (Exception e)
1255 if (Fx.IsFatal(e))
1257 throw;
1260 abortException = e;
1263 if (abortException != null)
1265 AbortWorkflowInstance(abortException);
1267 else
1269 if (TD.RuntimeTransactionSetIsEnabled())
1271 Fx.Assert(transactionOwner != null, "isolationScope and transactionOwner are either both null or both non-null");
1272 TD.RuntimeTransactionSet(transactionOwner.Activity.GetType().ToString(), transactionOwner.Activity.DisplayName, transactionOwner.Id, isolationScope.Activity.GetType().ToString(), isolationScope.Activity.DisplayName, isolationScope.Id);
1277 public void CompleteTransaction(RuntimeTransactionHandle handle, BookmarkCallback callback, ActivityInstance callbackOwner)
1279 if (callback != null)
1281 Bookmark bookmark = this.bookmarkManager.CreateBookmark(callback, callbackOwner, BookmarkOptions.None);
1282 ActivityExecutionWorkItem workItem;
1284 ActivityInstance isolationScope = null;
1286 if (this.runtimeTransaction != null)
1288 isolationScope = this.runtimeTransaction.IsolationScope;
1291 this.bookmarkManager.TryGenerateWorkItem(this, false, ref bookmark, null, isolationScope, out workItem);
1292 this.scheduler.EnqueueWork(workItem);
1295 if (this.runtimeTransaction != null && this.runtimeTransaction.TransactionHandle == handle)
1297 this.runtimeTransaction.ShouldScheduleCompletion = true;
1299 if (TD.RuntimeTransactionCompletionRequestedIsEnabled())
1301 TD.RuntimeTransactionCompletionRequested(callbackOwner.Activity.GetType().ToString(), callbackOwner.Activity.DisplayName, callbackOwner.Id);
1306 void SchedulePendingCancelation()
1308 if (this.runtimeTransaction.IsRootCancelPending)
1310 if (!this.rootInstance.IsCancellationRequested && !this.rootInstance.IsCompleted)
1312 this.rootInstance.IsCancellationRequested = true;
1313 this.scheduler.PushWork(new CancelActivityWorkItem(this.rootInstance));
1316 this.runtimeTransaction.IsRootCancelPending = false;
1320 public EmptyWorkItem CreateEmptyWorkItem(ActivityInstance instance)
1322 EmptyWorkItem workItem = this.EmptyWorkItemPool.Acquire();
1323 workItem.Initialize(instance);
1325 return workItem;
1328 public bool IsCompletingTransaction(ActivityInstance instance)
1330 if (this.runtimeTransaction != null && this.runtimeTransaction.IsolationScope == instance)
1332 // We add an empty work item to keep the instance alive
1333 this.scheduler.PushWork(CreateEmptyWorkItem(instance));
1335 // This will schedule the appopriate work item at the end of this work item
1336 this.runtimeTransaction.ShouldScheduleCompletion = true;
1338 if (TD.RuntimeTransactionCompletionRequestedIsEnabled())
1340 TD.RuntimeTransactionCompletionRequested(instance.Activity.GetType().ToString(), instance.Activity.DisplayName, instance.Id);
1343 return true;
1346 return false;
1349 public void TerminateSpecialExecutionBlocks(ActivityInstance terminatedInstance, Exception terminationReason)
1351 if (this.runtimeTransaction != null && this.runtimeTransaction.IsolationScope == terminatedInstance)
1353 Exception abortException = null;
1357 this.runtimeTransaction.Rollback(terminationReason);
1359 catch (Exception e)
1361 if (Fx.IsFatal(e))
1363 throw;
1366 abortException = e;
1369 if (abortException != null)
1371 // It is okay for us to call AbortWorkflowInstance even if we are already
1372 // aborting the instance since it is an async call (IE - we asking the host
1373 // to re-enter the instance to abandon it.
1374 AbortWorkflowInstance(abortException);
1377 SchedulePendingCancelation();
1379 ExitNoPersist();
1381 if (this.runtimeTransaction.TransactionHandle.AbortInstanceOnTransactionFailure)
1383 AbortWorkflowInstance(terminationReason);
1386 this.runtimeTransaction = null;
1390 // Returns true if we actually performed the abort and false if we had already been disposed
1391 bool Abort(Exception terminationException, bool isTerminate)
1393 if (!this.isDisposed)
1395 if (!this.rootInstance.IsCompleted)
1397 this.rootInstance.Abort(this, this.bookmarkManager, terminationException, isTerminate);
1399 // the Abort walk won't catch host-registered properties
1400 if (this.rootPropertyManager != null)
1402 if (isTerminate)
1404 HandleInitializationContext context = new HandleInitializationContext(this, null);
1405 foreach (ExecutionPropertyManager.ExecutionProperty executionProperty in this.rootPropertyManager.Properties.Values)
1407 Handle handle = executionProperty.Property as Handle;
1408 if (handle != null)
1410 handle.Uninitialize(context);
1413 context.Dispose();
1416 this.rootPropertyManager.UnregisterProperties(null, null, true);
1420 if (this.executingSecondaryRootInstances != null)
1422 // We have to walk this list backwards because the abort
1423 // path removes from this collection.
1424 for (int i = this.executingSecondaryRootInstances.Count - 1; i >= 0; i--)
1426 ActivityInstance secondaryRootInstance = this.executingSecondaryRootInstances[i];
1428 Fx.Assert(!secondaryRootInstance.IsCompleted, "We should not have any complete instances in our list.");
1430 secondaryRootInstance.Abort(this, this.bookmarkManager, terminationException, isTerminate);
1432 Fx.Assert(this.executingSecondaryRootInstances.Count == i, "We are always working from the back and we should have removed the item we just aborted.");
1436 // This must happen after we abort each activity. This allows us to utilize code paths
1437 // which schedule work items.
1438 this.scheduler.ClearAllWorkItems(this);
1440 if (isTerminate)
1442 // Regardless of the previous state, a termination implies setting the
1443 // completion exception and completing in the Faulted state.
1444 this.completionException = terminationException;
1445 this.executionState = ActivityInstanceState.Faulted;
1448 this.Dispose();
1450 return true;
1453 return false;
1456 // Returns true if tracing was transfered
1457 bool TryTraceResume(out Guid oldActivityId)
1459 if (FxTrace.Trace.ShouldTraceToTraceSource(TraceEventLevel.Informational))
1461 oldActivityId = DiagnosticTraceBase.ActivityId;
1462 FxTrace.Trace.SetAndTraceTransfer(this.WorkflowInstanceId, true);
1464 if (TD.WorkflowActivityResumeIsEnabled())
1466 TD.WorkflowActivityResume(this.WorkflowInstanceId);
1469 return true;
1471 else
1473 oldActivityId = Guid.Empty;
1474 return false;
1478 // Returns true if tracing was transfered
1479 bool TryTraceStart(out Guid oldActivityId)
1481 if (FxTrace.Trace.ShouldTraceToTraceSource(TraceEventLevel.Informational))
1483 oldActivityId = DiagnosticTraceBase.ActivityId;
1484 FxTrace.Trace.SetAndTraceTransfer(this.WorkflowInstanceId, true);
1486 if (TD.WorkflowActivityStartIsEnabled())
1488 TD.WorkflowActivityStart(this.WorkflowInstanceId);
1491 return true;
1493 else
1495 oldActivityId = Guid.Empty;
1496 return false;
1500 void TraceSuspend(bool hasBeenResumed, Guid oldActivityId)
1502 if (hasBeenResumed)
1504 if (TD.WorkflowActivitySuspendIsEnabled())
1506 TD.WorkflowActivitySuspend(this.WorkflowInstanceId);
1509 DiagnosticTraceBase.ActivityId = oldActivityId;
1513 public bool Abort(Exception reason)
1515 Guid oldActivityId;
1516 bool hasTracedResume = TryTraceResume(out oldActivityId);
1518 bool abortResult = Abort(reason, false);
1520 TraceSuspend(hasTracedResume, oldActivityId);
1522 return abortResult;
1525 // It must be okay for the runtime to be processing other
1526 // work on a different thread when this is called. See
1527 // the comments in the method for justifications.
1528 public void AbortWorkflowInstance(Exception reason)
1530 // 1) This flag is only ever set to true
1531 this.isAbortPending = true;
1533 // 2) This causes a couple of fields to be set
1534 this.host.Abort(reason);
1537 // 3) The host expects this to come from an unknown thread
1538 this.host.OnRequestAbort(reason);
1540 catch (Exception e)
1542 if (Fx.IsFatal(e))
1544 throw;
1546 throw FxTrace.Exception.AsError(new CallbackException(SR.CallbackExceptionFromHostAbort(this.WorkflowInstanceId), e));
1550 public void ScheduleTerminate(Exception reason)
1552 this.isTerminatePending = true;
1553 this.terminationPendingException = reason;
1556 public void Terminate(Exception reason)
1558 Fx.Assert(!this.isDisposed, "We should not have been able to get here if we are disposed and Abort makes choices based on isDisposed");
1560 Guid oldActivityId;
1561 bool hasTracedResume = TryTraceResume(out oldActivityId);
1563 Abort(reason, true);
1565 TraceSuspend(hasTracedResume, oldActivityId);
1568 public void CancelRootActivity()
1570 if (this.rootInstance.State == ActivityInstanceState.Executing)
1572 if (!this.rootInstance.IsCancellationRequested)
1574 Guid oldActivityId;
1575 bool hasTracedResume = TryTraceResume(out oldActivityId);
1577 bool trackCancelRequested = true;
1579 if (this.runtimeTransaction != null && this.runtimeTransaction.IsolationScope != null)
1581 if (this.runtimeTransaction.IsRootCancelPending)
1583 trackCancelRequested = false;
1586 this.runtimeTransaction.IsRootCancelPending = true;
1588 else
1590 this.rootInstance.IsCancellationRequested = true;
1592 if (this.rootInstance.HasNotExecuted)
1594 this.scheduler.PushWork(CreateEmptyWorkItem(this.rootInstance));
1596 else
1598 this.scheduler.PushWork(new CancelActivityWorkItem(this.rootInstance));
1602 if (this.ShouldTrackCancelRequestedRecords && trackCancelRequested)
1604 AddTrackingRecord(new CancelRequestedRecord(this.WorkflowInstanceId, null, this.rootInstance));
1607 TraceSuspend(hasTracedResume, oldActivityId);
1610 else if (this.rootInstance.State != ActivityInstanceState.Closed)
1612 // We've been asked to cancel the instance and the root
1613 // completed in a canceled or faulted state. By our rules
1614 // this means that the instance has been canceled. A real
1615 // world example if the case of UnhandledExceptionAction.Cancel
1616 // on a workflow whose root activity threw an exception. The
1617 // expected completion state is Canceled and NOT Faulted.
1618 this.executionState = ActivityInstanceState.Canceled;
1619 this.completionException = null;
1623 public void CancelActivity(ActivityInstance activityInstance)
1625 Fx.Assert(activityInstance != null, "The instance must not be null.");
1627 // Cancel is a no-op if the activity is complete or cancel has already been requested
1628 if (activityInstance.State != ActivityInstanceState.Executing || activityInstance.IsCancellationRequested)
1630 return;
1633 // Set that we have requested cancel. This is our only guard against scheduling
1634 // ActivityInstance.Cancel multiple times.
1635 activityInstance.IsCancellationRequested = true;
1637 if (activityInstance.HasNotExecuted)
1639 this.scheduler.PushWork(CreateEmptyWorkItem(activityInstance));
1641 else
1643 this.scheduler.PushWork(new CancelActivityWorkItem(activityInstance));
1646 if (this.ShouldTrackCancelRequestedRecords)
1648 AddTrackingRecord(new CancelRequestedRecord(this.WorkflowInstanceId, activityInstance.Parent, activityInstance));
1652 void PropagateException(WorkItem workItem)
1654 ActivityInstance exceptionSource = workItem.ActivityInstance;
1655 Exception exception = workItem.ExceptionToPropagate;
1657 ActivityInstance exceptionPropagator = exceptionSource;
1658 FaultBookmark targetBookmark = null;
1660 // If we are not supposed to persist exceptions, call EnterNoPersist so that we don't persist while we are
1661 // propagating the exception.
1662 // We call ExitNoPersist when we abort an activit or when we call a fault callback. But we may end up
1663 // re-propagating and thus calling EnterNoPersist again.
1664 // We also do an exit if the workflow is aborted or the exception ends up being unhandled.
1665 if (!this.PersistExceptions)
1667 EnterNoPersist();
1669 while (exceptionPropagator != null && targetBookmark == null)
1671 if (!exceptionPropagator.IsCompleted)
1673 if (this.runtimeTransaction != null && this.runtimeTransaction.IsolationScope == exceptionPropagator)
1675 // We are propagating the exception across the isolation scope
1676 this.scheduler.PushWork(new AbortActivityWorkItem(this, exceptionPropagator, exception, CreateActivityInstanceReference(workItem.OriginalExceptionSource, exceptionPropagator)));
1678 // Because we are aborting the transaction we reset the ShouldScheduleCompletion flag
1679 this.runtimeTransaction.ShouldScheduleCompletion = false;
1680 workItem.ExceptionPropagated();
1681 return;
1685 if (exceptionPropagator.IsCancellationRequested)
1687 // Regardless of whether it is already completed or not we need
1688 // to honor the workflow abort
1690 this.AbortWorkflowInstance(new InvalidOperationException(SR.CannotPropagateExceptionWhileCanceling(exceptionSource.Activity.DisplayName, exceptionSource.Id), exception));
1691 workItem.ExceptionPropagated();
1692 ExitNoPersistForExceptionPropagation();
1693 return;
1696 if (exceptionPropagator.FaultBookmark != null)
1698 // This will cause us to break out of the loop
1699 targetBookmark = exceptionPropagator.FaultBookmark;
1701 else
1703 exceptionPropagator = exceptionPropagator.Parent;
1707 if (targetBookmark != null)
1709 if (this.ShouldTrackFaultPropagationRecords)
1711 AddTrackingRecord(new FaultPropagationRecord(this.WorkflowInstanceId,
1712 workItem.OriginalExceptionSource,
1713 exceptionPropagator.Parent,
1714 exceptionSource == workItem.OriginalExceptionSource,
1715 exception));
1718 this.scheduler.PushWork(targetBookmark.GenerateWorkItem(exception, exceptionPropagator, CreateActivityInstanceReference(workItem.OriginalExceptionSource, exceptionPropagator.Parent)));
1719 workItem.ExceptionPropagated();
1721 else
1723 if (this.ShouldTrackFaultPropagationRecords)
1725 AddTrackingRecord(new FaultPropagationRecord(this.WorkflowInstanceId,
1726 workItem.OriginalExceptionSource,
1727 null,
1728 exceptionSource == workItem.OriginalExceptionSource,
1729 exception));
1734 internal ActivityInstanceReference CreateActivityInstanceReference(ActivityInstance toReference, ActivityInstance referenceOwner)
1736 ActivityInstanceReference reference = new ActivityInstanceReference(toReference);
1738 if (this.instanceMap != null)
1740 this.instanceMap.AddEntry(reference);
1743 referenceOwner.AddActivityReference(reference);
1745 return reference;
1748 internal void RethrowException(ActivityInstance fromInstance, FaultContext context)
1750 this.scheduler.PushWork(new RethrowExceptionWorkItem(fromInstance, context.Exception, context.Source));
1753 internal void OnDeserialized(Activity workflow, WorkflowInstance workflowInstance)
1755 Fx.Assert(workflow != null, "The program must be non-null");
1756 Fx.Assert(workflowInstance != null, "The host must be non-null");
1758 if (!object.Equals(workflowInstance.DefinitionIdentity, this.WorkflowIdentity))
1760 throw FxTrace.Exception.AsError(new VersionMismatchException(workflowInstance.DefinitionIdentity, this.WorkflowIdentity));
1763 this.rootElement = workflow;
1764 this.host = workflowInstance;
1766 if (!this.instanceIdSet)
1768 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.EmptyGuidOnDeserializedInstance));
1770 if (this.host.Id != this.instanceId)
1772 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.HostIdDoesNotMatchInstance(this.host.Id, this.instanceId)));
1775 if (this.host.HasTrackingParticipant)
1777 this.host.TrackingProvider.OnDeserialized(this.nextTrackingRecordNumber);
1778 this.host.OnDeserialized(this.hasTrackedStarted);
1781 // hookup our callback to the scheduler
1782 if (this.scheduler != null)
1784 this.scheduler.OnDeserialized(new Scheduler.Callbacks(this));
1787 if (this.rootInstance != null)
1789 Fx.Assert(this.instanceMap != null, "We always have an InstanceMap.");
1790 this.instanceMap.LoadActivityTree(workflow, this.rootInstance, this.executingSecondaryRootInstances, this);
1792 // We need to make sure that any "dangling" secondary root environments
1793 // get OnDeserialized called.
1794 if (this.executingSecondaryRootInstances != null)
1796 Fx.Assert(this.executingSecondaryRootInstances.Count > 0, "We don't serialize out an empty list.");
1798 for (int i = 0; i < this.executingSecondaryRootInstances.Count; i++)
1800 ActivityInstance secondaryRoot = this.executingSecondaryRootInstances[i];
1801 LocationEnvironment environment = secondaryRoot.Environment.Parent;
1803 if (environment != null)
1805 environment.OnDeserialized(this, secondaryRoot);
1810 else
1812 this.isDisposed = true;
1816 public T GetExtension<T>()
1817 where T : class
1819 T extension = null;
1822 extension = this.host.GetExtension<T>();
1824 catch (Exception e)
1826 if (Fx.IsFatal(e))
1828 throw;
1830 throw FxTrace.Exception.AsError(new CallbackException(SR.CallbackExceptionFromHostGetExtension(this.WorkflowInstanceId), e));
1833 return extension;
1836 internal Scheduler.RequestedAction TryExecuteNonEmptyWorkItem(WorkItem workItem)
1838 Exception setupOrCleanupException = null;
1839 ActivityInstance propertyManagerOwner = workItem.PropertyManagerOwner;
1842 if (propertyManagerOwner != null && propertyManagerOwner.PropertyManager != null)
1846 propertyManagerOwner.PropertyManager.SetupWorkflowThread();
1848 catch (Exception e)
1850 if (Fx.IsFatal(e))
1852 throw;
1855 setupOrCleanupException = e;
1859 if (setupOrCleanupException == null)
1861 if (!workItem.Execute(this, this.bookmarkManager))
1863 return Scheduler.YieldSilently;
1867 finally
1869 // We might be multi-threaded when we execute code in
1870 // this finally block. The work item might have gone
1871 // async and may already have called back into FinishWorkItem.
1872 if (propertyManagerOwner != null && propertyManagerOwner.PropertyManager != null)
1874 // This throws only fatal exceptions
1875 propertyManagerOwner.PropertyManager.CleanupWorkflowThread(ref setupOrCleanupException);
1878 if (setupOrCleanupException != null)
1880 // This API must allow the runtime to be
1881 // multi-threaded when it is called.
1882 AbortWorkflowInstance(new OperationCanceledException(SR.SetupOrCleanupWorkflowThreadThrew, setupOrCleanupException));
1886 if (setupOrCleanupException != null)
1888 // We already aborted the instance in the finally block so
1889 // now we just need to return early.
1890 return Scheduler.Continue;
1892 return null;
1895 // callback from scheduler to process a work item
1896 internal Scheduler.RequestedAction OnExecuteWorkItem(WorkItem workItem)
1898 workItem.Release(this);
1900 // thunk out early if the work item is no longer valid (that is, we're not in the Executing state)
1901 if (!workItem.IsValid)
1903 return Scheduler.Continue;
1906 if (!workItem.IsEmpty)
1908 // The try/catch/finally block used in executing a workItem prevents ryujit from performing
1909 // some optimizations. Moving the functionality back into this method may cause a performance
1910 // regression.
1911 var result = TryExecuteNonEmptyWorkItem(workItem);
1912 if (result != null)
1914 return result;
1918 if (workItem.WorkflowAbortException != null)
1920 AbortWorkflowInstance(new OperationCanceledException(SR.WorkItemAbortedInstance, workItem.WorkflowAbortException));
1921 return Scheduler.Continue;
1924 // We only check this in the sync path because there are no ways of changing the keys collections from the work items that can
1925 // go async. There's an assert to this effect in FinishWorkItem.
1926 if (this.bookmarkScopeManager != null && this.bookmarkScopeManager.HasKeysToUpdate)
1928 if (!workItem.FlushBookmarkScopeKeys(this))
1930 return Scheduler.YieldSilently;
1933 if (workItem.WorkflowAbortException != null)
1935 AbortWorkflowInstance(new OperationCanceledException(SR.WorkItemAbortedInstance, workItem.WorkflowAbortException));
1936 return Scheduler.Continue;
1940 workItem.PostProcess(this);
1942 if (workItem.ExceptionToPropagate != null)
1944 PropagateException(workItem);
1947 if (this.HasPendingTrackingRecords)
1949 if (!workItem.FlushTracking(this))
1951 return Scheduler.YieldSilently;
1954 if (workItem.WorkflowAbortException != null)
1956 AbortWorkflowInstance(new OperationCanceledException(SR.TrackingRelatedWorkflowAbort, workItem.WorkflowAbortException));
1957 return Scheduler.Continue;
1961 ScheduleRuntimeWorkItems();
1963 if (workItem.ExceptionToPropagate != null)
1965 ExitNoPersistForExceptionPropagation();
1966 return Scheduler.CreateNotifyUnhandledExceptionAction(workItem.ExceptionToPropagate, workItem.OriginalExceptionSource);
1969 return Scheduler.Continue;
1972 internal IAsyncResult BeginAssociateKeys(ICollection<InstanceKey> keysToAssociate, AsyncCallback callback, object state)
1974 return new AssociateKeysAsyncResult(this, keysToAssociate, callback, state);
1977 internal void EndAssociateKeys(IAsyncResult result)
1979 AssociateKeysAsyncResult.End(result);
1982 internal void DisassociateKeys(ICollection<InstanceKey> keysToDisassociate)
1984 this.host.OnDisassociateKeys(keysToDisassociate);
1987 internal void FinishWorkItem(WorkItem workItem)
1989 Scheduler.RequestedAction resumptionAction = Scheduler.Continue;
1993 Fx.Assert(this.bookmarkScopeManager == null || !this.bookmarkScopeManager.HasKeysToUpdate,
1994 "FinishWorkItem should be called after FlushBookmarkScopeKeys, or by a WorkItem that could not possibly generate keys.");
1996 if (workItem.WorkflowAbortException != null)
1998 // We resume the scheduler even after abort to make sure that
1999 // the proper events are raised.
2000 AbortWorkflowInstance(new OperationCanceledException(SR.WorkItemAbortedInstance, workItem.WorkflowAbortException));
2002 else
2004 workItem.PostProcess(this);
2006 if (workItem.ExceptionToPropagate != null)
2008 PropagateException(workItem);
2011 if (this.HasPendingTrackingRecords)
2013 if (!workItem.FlushTracking(this))
2015 // We exit early here and will come back in at
2016 // FinishWorkItemAfterTracking
2017 resumptionAction = Scheduler.YieldSilently;
2018 return;
2022 if (workItem.WorkflowAbortException != null)
2024 // We resume the scheduler even after abort to make sure that
2025 // the proper events are raised.
2026 AbortWorkflowInstance(new OperationCanceledException(SR.TrackingRelatedWorkflowAbort, workItem.WorkflowAbortException));
2028 else
2030 ScheduleRuntimeWorkItems();
2032 if (workItem.ExceptionToPropagate != null)
2034 ExitNoPersistForExceptionPropagation();
2035 resumptionAction = Scheduler.CreateNotifyUnhandledExceptionAction(workItem.ExceptionToPropagate, workItem.OriginalExceptionSource);
2040 finally
2042 if (resumptionAction != Scheduler.YieldSilently)
2044 workItem.Dispose(this);
2048 Fx.Assert(resumptionAction != Scheduler.YieldSilently, "should not reach this section if we've yielded earlier");
2049 this.scheduler.InternalResume(resumptionAction);
2052 internal void FinishWorkItemAfterTracking(WorkItem workItem)
2054 Scheduler.RequestedAction resumptionAction = Scheduler.Continue;
2058 if (workItem.WorkflowAbortException != null)
2060 // We resume the scheduler even after abort to make sure that
2061 // the proper events are raised.
2062 AbortWorkflowInstance(new OperationCanceledException(SR.TrackingRelatedWorkflowAbort, workItem.WorkflowAbortException));
2064 else
2066 ScheduleRuntimeWorkItems();
2068 if (workItem.ExceptionToPropagate != null)
2070 ExitNoPersistForExceptionPropagation();
2071 resumptionAction = Scheduler.CreateNotifyUnhandledExceptionAction(workItem.ExceptionToPropagate, workItem.OriginalExceptionSource);
2075 finally
2077 workItem.Dispose(this);
2080 this.scheduler.InternalResume(resumptionAction);
2083 void ScheduleRuntimeWorkItems()
2085 if (this.runtimeTransaction != null && this.runtimeTransaction.ShouldScheduleCompletion)
2087 this.scheduler.PushWork(new CompleteTransactionWorkItem(this.runtimeTransaction.IsolationScope));
2088 return;
2091 if (this.persistenceWaiters != null && this.persistenceWaiters.Count > 0 &&
2092 this.IsPersistable)
2094 PersistenceWaiter waiter = this.persistenceWaiters.Dequeue();
2096 while (waiter != null && waiter.WaitingInstance.IsCompleted)
2098 // We just skip completed instance so we don't have to deal
2099 // with the housekeeping are arbitrary removal from our
2100 // queue
2102 if (this.persistenceWaiters.Count == 0)
2104 waiter = null;
2106 else
2108 waiter = this.persistenceWaiters.Dequeue();
2112 if (waiter != null)
2114 this.scheduler.PushWork(waiter.CreateWorkItem());
2115 return;
2120 internal void AbortActivityInstance(ActivityInstance instance, Exception reason)
2122 instance.Abort(this, this.bookmarkManager, reason, true);
2124 if (instance.CompletionBookmark != null)
2126 instance.CompletionBookmark.CheckForCancelation();
2128 else if (instance.Parent != null)
2130 instance.CompletionBookmark = new CompletionBookmark();
2133 ScheduleCompletionBookmark(instance);
2136 internal Exception CompleteActivityInstance(ActivityInstance targetInstance)
2138 Exception exceptionToPropagate = null;
2140 // 1. Handle any root related work
2141 HandleRootCompletion(targetInstance);
2143 // 2. Schedule the completion bookmark
2144 // We MUST schedule the completion bookmark before
2145 // we dispose the environment because we take this
2146 // opportunity to gather up any output values.
2147 ScheduleCompletionBookmark(targetInstance);
2149 if (!targetInstance.HasNotExecuted)
2151 DebugActivityCompleted(targetInstance);
2154 // 3. Cleanup environmental resources (properties, handles, mapped locations)
2157 if (targetInstance.PropertyManager != null)
2159 targetInstance.PropertyManager.UnregisterProperties(targetInstance, targetInstance.Activity.MemberOf);
2162 if (IsSecondaryRoot(targetInstance))
2164 // We need to appropriately remove references, dispose
2165 // environments, and remove instance map entries for
2166 // all environments in this chain
2167 LocationEnvironment environment = targetInstance.Environment;
2169 if (targetInstance.IsEnvironmentOwner)
2171 environment.RemoveReference(true);
2173 if (environment.ShouldDispose)
2175 // Unintialize all handles declared in this environment.
2176 environment.UninitializeHandles(targetInstance);
2178 environment.Dispose();
2181 environment = environment.Parent;
2184 while (environment != null)
2186 environment.RemoveReference(false);
2188 if (environment.ShouldDispose)
2190 // Unintialize all handles declared in this environment.
2191 environment.UninitializeHandles(targetInstance);
2193 environment.Dispose();
2195 // This also implies that the owner is complete so we should
2196 // remove it from the map
2197 if (this.instanceMap != null)
2199 this.instanceMap.RemoveEntry(environment);
2203 environment = environment.Parent;
2206 else if (targetInstance.IsEnvironmentOwner)
2208 targetInstance.Environment.RemoveReference(true);
2210 if (targetInstance.Environment.ShouldDispose)
2212 // Unintialize all handles declared in this environment.
2213 targetInstance.Environment.UninitializeHandles(targetInstance);
2215 targetInstance.Environment.Dispose();
2217 else if (this.instanceMap != null)
2219 // Someone else is referencing this environment
2220 // Note that we don't use TryAdd since no-one else should have
2221 // added it before.
2222 this.instanceMap.AddEntry(targetInstance.Environment);
2226 catch (Exception e)
2228 if (Fx.IsFatal(e))
2230 throw;
2233 exceptionToPropagate = e;
2236 // 4. Cleanup remaining instance related resources (bookmarks, program mapping)
2237 targetInstance.MarkAsComplete(this.bookmarkScopeManager, this.bookmarkManager);
2239 // 5. Track our final state
2240 targetInstance.FinalizeState(this, exceptionToPropagate != null);
2242 return exceptionToPropagate;
2245 internal bool TryGetPendingOperation(ActivityInstance instance, out AsyncOperationContext asyncContext)
2247 if (this.activeOperations != null)
2249 return this.activeOperations.TryGetValue(instance, out asyncContext);
2251 else
2253 asyncContext = null;
2254 return false;
2258 internal void CancelPendingOperation(ActivityInstance instance)
2260 AsyncOperationContext asyncContext;
2261 if (TryGetPendingOperation(instance, out asyncContext))
2263 if (asyncContext.IsStillActive)
2265 asyncContext.CancelOperation();
2270 internal void HandleRootCompletion(ActivityInstance completedInstance)
2272 if (completedInstance.Parent == null)
2274 if (completedInstance == this.rootInstance)
2276 this.shouldRaiseMainBodyComplete = true;
2278 Fx.Assert(this.executionState == ActivityInstanceState.Executing, "We shouldn't have a guess at our completion state yet.");
2280 // We start by assuming our completion state will match the root instance.
2281 this.executionState = this.rootInstance.State;
2282 this.rootEnvironment = this.rootInstance.Environment;
2284 else
2286 Fx.Assert(this.executingSecondaryRootInstances.Contains(completedInstance), "An instance which is not the main root and doesn't have an execution parent must be an executing secondary root.");
2287 this.executingSecondaryRootInstances.Remove(completedInstance);
2290 // We just had a root complete, let's see if we're all the way done
2291 // and should gather outputs from the root. Note that we wait until
2292 // everything completes in case the root environment was detached.
2293 if (this.rootInstance.IsCompleted
2294 && (this.executingSecondaryRootInstances == null || this.executingSecondaryRootInstances.Count == 0))
2296 GatherRootOutputs();
2298 // uninitialize any host-provided handles
2299 if (this.rootPropertyManager != null)
2301 // and uninitialize host-provided handles
2302 HandleInitializationContext context = new HandleInitializationContext(this, null);
2303 foreach (ExecutionPropertyManager.ExecutionProperty executionProperty in this.rootPropertyManager.Properties.Values)
2305 Handle handle = executionProperty.Property as Handle;
2306 if (handle != null)
2308 handle.Uninitialize(context);
2311 context.Dispose();
2313 // unregister any properties that were registered
2314 this.rootPropertyManager.UnregisterProperties(null, null);
2320 bool IsSecondaryRoot(ActivityInstance instance)
2322 return instance.Parent == null && instance != this.rootInstance;
2325 void GatherRootOutputs()
2327 Fx.Assert(this.workflowOutputs == null, "We should only get workflow outputs when we actually complete which should only happen once.");
2328 Fx.Assert(ActivityUtilities.IsCompletedState(this.rootInstance.State), "We should only gather outputs when in a completed state.");
2329 Fx.Assert(this.rootEnvironment != null, "We should have set the root environment");
2331 // We only gather outputs for Closed - not for canceled or faulted
2332 if (this.rootInstance.State == ActivityInstanceState.Closed)
2334 // We use rootElement here instead of this.rootInstance.Activity
2335 // because we don't always reload the root instance (like if it
2336 // was complete when we last persisted).
2337 IList<RuntimeArgument> rootArguments = this.rootElement.RuntimeArguments;
2339 for (int i = 0; i < rootArguments.Count; i++)
2341 RuntimeArgument argument = rootArguments[i];
2343 if (ArgumentDirectionHelper.IsOut(argument.Direction))
2345 if (this.workflowOutputs == null)
2347 this.workflowOutputs = new Dictionary<string, object>();
2350 Location location = this.rootEnvironment.GetSpecificLocation(argument.BoundArgument.Id);
2351 if (location == null)
2353 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.NoOutputLocationWasFound(argument.Name)));
2355 this.workflowOutputs.Add(argument.Name, location.Value);
2360 // GatherRootOutputs only ever gets called once so we can null it out the root environment now.
2361 this.rootEnvironment = null;
2364 internal void NotifyUnhandledException(Exception exception, ActivityInstance source)
2368 this.host.NotifyUnhandledException(exception, source.Activity, source.Id);
2370 catch (Exception e)
2372 if (Fx.IsFatal(e))
2374 throw;
2376 this.AbortWorkflowInstance(e);
2380 internal void OnSchedulerIdle()
2382 // If we're terminating we'll call terminate here and
2383 // then do the normal notification for the host.
2384 if (this.isTerminatePending)
2386 Fx.Assert(this.terminationPendingException != null, "Should have set terminationPendingException at the same time that we set isTerminatePending = true");
2387 this.Terminate(this.terminationPendingException);
2388 this.isTerminatePending = false;
2391 if (this.IsIdle)
2393 if (this.transactionContextWaiters != null && this.transactionContextWaiters.Count > 0)
2395 if (this.IsPersistable || (this.transactionContextWaiters[0].IsRequires && this.noPersistCount == 1))
2397 TransactionContextWaiter waiter = this.transactionContextWaiters.Dequeue();
2399 waiter.WaitingInstance.DecrementBusyCount();
2400 waiter.WaitingInstance.WaitingForTransactionContext = false;
2402 ScheduleItem(new TransactionContextWorkItem(waiter));
2404 MarkSchedulerRunning();
2405 ResumeScheduler();
2407 return;
2411 if (this.shouldRaiseMainBodyComplete)
2413 this.shouldRaiseMainBodyComplete = false;
2414 if (this.mainRootCompleteBookmark != null)
2416 BookmarkResumptionResult resumptionResult = this.TryResumeUserBookmark(this.mainRootCompleteBookmark, this.rootInstance.State, false);
2417 this.mainRootCompleteBookmark = null;
2418 if (resumptionResult == BookmarkResumptionResult.Success)
2420 this.MarkSchedulerRunning();
2421 this.ResumeScheduler();
2422 return;
2426 if (this.executingSecondaryRootInstances == null || this.executingSecondaryRootInstances.Count == 0)
2428 // if we got to this point we're completely done from the executor's point of view.
2429 // outputs have been gathered, no more work is happening. Clear out some fields to shrink our
2430 // "completed instance" persistence size
2431 Dispose(false);
2436 if (this.shouldPauseOnCanPersist && this.IsPersistable)
2438 this.shouldPauseOnCanPersist = false;
2443 this.host.NotifyPaused();
2445 catch (Exception e)
2447 if (Fx.IsFatal(e))
2449 throw;
2451 this.AbortWorkflowInstance(e);
2455 public void Open(SynchronizationContext synchronizationContext)
2457 this.scheduler.Open(synchronizationContext);
2460 public void PauseScheduler()
2462 // Since we don't require calls to WorkflowInstanceControl.Pause to be synchronized
2463 // by the caller, we need to check for null here
2464 Scheduler localScheduler = this.scheduler;
2466 if (localScheduler != null)
2468 localScheduler.Pause();
2472 public object PrepareForSerialization()
2474 if (this.host.HasTrackingParticipant)
2476 this.nextTrackingRecordNumber = this.host.TrackingProvider.NextTrackingRecordNumber;
2477 this.hasTrackedStarted = this.host.HasTrackedStarted;
2479 return this;
2482 public void RequestPersist(Bookmark onPersistBookmark, ActivityInstance requestingInstance)
2484 if (this.persistenceWaiters == null)
2486 this.persistenceWaiters = new Queue<PersistenceWaiter>();
2489 this.persistenceWaiters.Enqueue(new PersistenceWaiter(onPersistBookmark, requestingInstance));
2492 void ScheduleCompletionBookmark(ActivityInstance completedInstance)
2494 if (completedInstance.CompletionBookmark != null)
2496 this.scheduler.PushWork(completedInstance.CompletionBookmark.GenerateWorkItem(completedInstance, this));
2498 else if (completedInstance.Parent != null)
2500 // Variable defaults and argument expressions always have a parent
2501 // and never have a CompletionBookmark
2502 if (completedInstance.State != ActivityInstanceState.Closed && completedInstance.Parent.HasNotExecuted)
2504 completedInstance.Parent.SetInitializationIncomplete();
2507 this.scheduler.PushWork(CreateEmptyWorkItem(completedInstance.Parent));
2511 // This method is called by WorkflowInstance - these are bookmark resumptions
2512 // originated by the host
2513 internal BookmarkResumptionResult TryResumeHostBookmark(Bookmark bookmark, object value)
2515 Guid oldActivityId;
2516 bool hasTracedResume = TryTraceResume(out oldActivityId);
2518 BookmarkResumptionResult result = TryResumeUserBookmark(bookmark, value, true);
2520 TraceSuspend(hasTracedResume, oldActivityId);
2522 return result;
2525 internal BookmarkResumptionResult TryResumeUserBookmark(Bookmark bookmark, object value, bool isExternal)
2527 if (this.isDisposed)
2529 return BookmarkResumptionResult.NotFound;
2532 ActivityInstance isolationInstance = null;
2534 if (this.runtimeTransaction != null)
2536 isolationInstance = this.runtimeTransaction.IsolationScope;
2539 ActivityExecutionWorkItem resumeExecutionWorkItem;
2541 BookmarkResumptionResult result = this.bookmarkManager.TryGenerateWorkItem(this, isExternal, ref bookmark, value, isolationInstance, out resumeExecutionWorkItem);
2543 if (result == BookmarkResumptionResult.Success)
2545 this.scheduler.EnqueueWork(resumeExecutionWorkItem);
2547 if (this.ShouldTrackBookmarkResumptionRecords)
2549 AddTrackingRecord(new BookmarkResumptionRecord(this.WorkflowInstanceId, bookmark, resumeExecutionWorkItem.ActivityInstance, value));
2552 else if (result == BookmarkResumptionResult.NotReady)
2554 // We had the bookmark but this is not an appropriate time to resume it
2555 // so we won't do anything here
2557 else if (bookmark == Bookmark.AsyncOperationCompletionBookmark)
2559 Fx.Assert(result == BookmarkResumptionResult.NotFound, "This BookmarkNotFound is actually a well-known bookmark.");
2561 AsyncOperationContext.CompleteData data = (AsyncOperationContext.CompleteData)value;
2563 data.CompleteOperation();
2565 result = BookmarkResumptionResult.Success;
2568 return result;
2571 internal ReadOnlyCollection<BookmarkInfo> GetAllBookmarks()
2573 List<BookmarkInfo> bookmarks = CollectExternalBookmarks();
2575 if (bookmarks != null)
2577 return new ReadOnlyCollection<BookmarkInfo>(bookmarks);
2579 else
2581 return EmptyBookmarkInfoCollection;
2585 List<BookmarkInfo> CollectExternalBookmarks()
2587 List<BookmarkInfo> bookmarks = null;
2589 if (this.bookmarkManager != null && this.bookmarkManager.HasBookmarks)
2591 bookmarks = new List<BookmarkInfo>();
2593 this.bookmarkManager.PopulateBookmarkInfo(bookmarks);
2596 if (this.bookmarkScopeManager != null)
2598 this.bookmarkScopeManager.PopulateBookmarkInfo(ref bookmarks);
2601 if (bookmarks == null || bookmarks.Count == 0)
2603 return null;
2605 else
2607 return bookmarks;
2611 internal ReadOnlyCollection<BookmarkInfo> GetBookmarks(BookmarkScope scope)
2613 if (this.bookmarkScopeManager == null)
2615 return EmptyBookmarkInfoCollection;
2617 else
2619 ReadOnlyCollection<BookmarkInfo> bookmarks = this.bookmarkScopeManager.GetBookmarks(scope);
2621 if (bookmarks == null)
2623 return EmptyBookmarkInfoCollection;
2625 else
2627 return bookmarks;
2632 internal IAsyncResult BeginResumeBookmark(Bookmark bookmark, object value, TimeSpan timeout, AsyncCallback callback, object state)
2634 return this.host.OnBeginResumeBookmark(bookmark, value, timeout, callback, state);
2637 internal BookmarkResumptionResult EndResumeBookmark(IAsyncResult result)
2639 return this.host.OnEndResumeBookmark(result);
2642 // This is only called by WorkflowInstance so it behaves like TryResumeUserBookmark with must
2643 // run work item set to true
2644 internal BookmarkResumptionResult TryResumeBookmark(Bookmark bookmark, object value, BookmarkScope scope)
2646 // We have to perform all of this work with tracing set up
2647 // since we might initialize a sub-instance while generating
2648 // the work item.
2649 Guid oldActivityId;
2650 bool hasTracedResume = TryTraceResume(out oldActivityId);
2652 ActivityInstance isolationInstance = null;
2654 if (this.runtimeTransaction != null)
2656 isolationInstance = this.runtimeTransaction.IsolationScope;
2659 bool hasOperations = this.activeOperations != null && this.activeOperations.Count > 0;
2661 ActivityExecutionWorkItem resumeExecutionWorkItem;
2662 BookmarkResumptionResult result = this.BookmarkScopeManager.TryGenerateWorkItem(this, ref bookmark, scope, value, isolationInstance, hasOperations || this.bookmarkManager.HasBookmarks, out resumeExecutionWorkItem);
2664 if (result == BookmarkResumptionResult.Success)
2666 this.scheduler.EnqueueWork(resumeExecutionWorkItem);
2668 if (this.ShouldTrackBookmarkResumptionRecords)
2670 AddTrackingRecord(new BookmarkResumptionRecord(this.WorkflowInstanceId, bookmark, resumeExecutionWorkItem.ActivityInstance, value));
2674 TraceSuspend(hasTracedResume, oldActivityId);
2676 return result;
2679 public void MarkSchedulerRunning()
2681 this.scheduler.MarkRunning();
2684 public void Run()
2686 ResumeScheduler();
2689 void ResumeScheduler()
2691 this.scheduler.Resume();
2694 internal void ScheduleItem(WorkItem workItem)
2696 this.scheduler.PushWork(workItem);
2699 public void ScheduleRootActivity(Activity activity, IDictionary<string, object> argumentValueOverrides, IList<Handle> hostProperties)
2701 Fx.Assert(this.rootInstance == null, "ScheduleRootActivity should only be called once");
2703 if (hostProperties != null && hostProperties.Count > 0)
2705 Dictionary<string, ExecutionPropertyManager.ExecutionProperty> rootProperties = new Dictionary<string, ExecutionPropertyManager.ExecutionProperty>(hostProperties.Count);
2706 HandleInitializationContext context = new HandleInitializationContext(this, null);
2707 for (int i = 0; i < hostProperties.Count; i++)
2709 Handle handle = hostProperties[i];
2710 handle.Initialize(context);
2711 rootProperties.Add(handle.ExecutionPropertyName, new ExecutionPropertyManager.ExecutionProperty(handle.ExecutionPropertyName, handle, null));
2713 context.Dispose();
2715 this.rootPropertyManager = new ExecutionPropertyManager(null, rootProperties);
2718 Guid oldActivityId;
2719 bool hasTracedStart = TryTraceStart(out oldActivityId);
2721 // Create and initialize the root instance
2722 this.rootInstance = new ActivityInstance(activity)
2724 PropertyManager = this.rootPropertyManager
2726 this.rootElement = activity;
2728 Fx.Assert(this.lastInstanceId == 0, "We should only hit this path once");
2729 this.lastInstanceId++;
2731 bool requiresSymbolResolution = this.rootInstance.Initialize(null, this.instanceMap, null, this.lastInstanceId, this);
2733 if (TD.ActivityScheduledIsEnabled())
2735 TraceActivityScheduled(null, activity, this.rootInstance.Id);
2738 // Add the work item for executing the root
2739 this.scheduler.PushWork(new ExecuteRootWorkItem(this.rootInstance, requiresSymbolResolution, argumentValueOverrides));
2741 TraceSuspend(hasTracedStart, oldActivityId);
2744 public void RegisterMainRootCompleteCallback(Bookmark bookmark)
2746 this.mainRootCompleteBookmark = bookmark;
2749 public ActivityInstance ScheduleSecondaryRootActivity(Activity activity, LocationEnvironment environment)
2751 ActivityInstance secondaryRoot = ScheduleActivity(activity, null, null, null, environment);
2753 while (environment != null)
2755 environment.AddReference();
2756 environment = environment.Parent;
2759 if (this.executingSecondaryRootInstances == null)
2761 this.executingSecondaryRootInstances = new List<ActivityInstance>();
2764 this.executingSecondaryRootInstances.Add(secondaryRoot);
2766 return secondaryRoot;
2769 public ActivityInstance ScheduleActivity(Activity activity, ActivityInstance parent,
2770 CompletionBookmark completionBookmark, FaultBookmark faultBookmark, LocationEnvironment parentEnvironment)
2772 return ScheduleActivity(activity, parent, completionBookmark, faultBookmark, parentEnvironment, null, null);
2775 public ActivityInstance ScheduleDelegate(ActivityDelegate activityDelegate, IDictionary<string, object> inputParameters, ActivityInstance parent, LocationEnvironment executionEnvironment,
2776 CompletionBookmark completionBookmark, FaultBookmark faultBookmark)
2778 Fx.Assert(activityDelegate.Owner != null, "activityDelegate must have an owner");
2779 Fx.Assert(parent != null, "activityDelegate should have a parent activity instance");
2781 ActivityInstance handlerInstance;
2783 if (activityDelegate.Handler == null)
2785 handlerInstance = ActivityInstance.CreateCompletedInstance(new EmptyDelegateActivity());
2786 handlerInstance.CompletionBookmark = completionBookmark;
2787 ScheduleCompletionBookmark(handlerInstance);
2789 else
2791 handlerInstance = CreateUninitalizedActivityInstance(activityDelegate.Handler, parent, completionBookmark, faultBookmark);
2792 bool requiresSymbolResolution = handlerInstance.Initialize(parent, this.instanceMap, executionEnvironment, this.lastInstanceId, this, activityDelegate.RuntimeDelegateArguments.Count);
2794 IList<RuntimeDelegateArgument> activityDelegateParameters = activityDelegate.RuntimeDelegateArguments;
2795 for (int i = 0; i < activityDelegateParameters.Count; i++)
2797 RuntimeDelegateArgument runtimeArgument = activityDelegateParameters[i];
2799 if (runtimeArgument.BoundArgument != null)
2801 string delegateParameterName = runtimeArgument.Name;
2803 // Populate argument location. Set it's value in the activity handler's
2804 // instance environment only if it is a DelegateInArgument.
2805 Location newLocation = runtimeArgument.BoundArgument.CreateLocation();
2806 handlerInstance.Environment.Declare(runtimeArgument.BoundArgument, newLocation, handlerInstance);
2808 if (ArgumentDirectionHelper.IsIn(runtimeArgument.Direction))
2810 if (inputParameters != null && inputParameters.Count > 0)
2812 newLocation.Value = inputParameters[delegateParameterName];
2818 if (TD.ActivityScheduledIsEnabled())
2820 TraceActivityScheduled(parent, activityDelegate.Handler, handlerInstance.Id);
2823 if (this.ShouldTrackActivityScheduledRecords)
2825 AddTrackingRecord(new ActivityScheduledRecord(this.WorkflowInstanceId, parent, handlerInstance));
2828 ScheduleBody(handlerInstance, requiresSymbolResolution, null, null);
2831 return handlerInstance;
2834 void TraceActivityScheduled(ActivityInstance parent, Activity activity, string scheduledInstanceId)
2836 Fx.Assert(TD.ActivityScheduledIsEnabled(), "This should be checked before calling this helper.");
2838 if (parent != null)
2840 TD.ActivityScheduled(parent.Activity.GetType().ToString(), parent.Activity.DisplayName, parent.Id, activity.GetType().ToString(), activity.DisplayName, scheduledInstanceId);
2842 else
2844 TD.ActivityScheduled(string.Empty, string.Empty, string.Empty, activity.GetType().ToString(), activity.DisplayName, scheduledInstanceId);
2848 ActivityInstance CreateUninitalizedActivityInstance(Activity activity, ActivityInstance parent, CompletionBookmark completionBookmark, FaultBookmark faultBookmark)
2850 Fx.Assert(activity.IsMetadataCached, "Metadata must be cached for us to process this activity.");
2852 // 1. Create a new activity instance and setup bookmark callbacks
2853 ActivityInstance activityInstance = new ActivityInstance(activity);
2855 if (parent != null)
2857 // add a bookmarks to complete at activity.Close/Fault time
2858 activityInstance.CompletionBookmark = completionBookmark;
2859 activityInstance.FaultBookmark = faultBookmark;
2860 parent.AddChild(activityInstance);
2863 // 2. Setup parent and environment machinery, and add to instance's program mapping for persistence (if necessary)
2864 IncrementLastInstanceId();
2866 return activityInstance;
2869 void IncrementLastInstanceId()
2871 if (this.lastInstanceId == long.MaxValue)
2873 throw FxTrace.Exception.AsError(new NotSupportedException(SR.OutOfInstanceIds));
2875 this.lastInstanceId++;
2878 ActivityInstance ScheduleActivity(Activity activity, ActivityInstance parent,
2879 CompletionBookmark completionBookmark, FaultBookmark faultBookmark, LocationEnvironment parentEnvironment,
2880 IDictionary<string, object> argumentValueOverrides, Location resultLocation)
2882 ActivityInstance activityInstance = CreateUninitalizedActivityInstance(activity, parent, completionBookmark, faultBookmark);
2883 bool requiresSymbolResolution = activityInstance.Initialize(parent, this.instanceMap, parentEnvironment, this.lastInstanceId, this);
2885 if (TD.ActivityScheduledIsEnabled())
2887 TraceActivityScheduled(parent, activity, activityInstance.Id);
2890 if (this.ShouldTrackActivityScheduledRecords)
2892 AddTrackingRecord(new ActivityScheduledRecord(this.WorkflowInstanceId, parent, activityInstance));
2895 ScheduleBody(activityInstance, requiresSymbolResolution, argumentValueOverrides, resultLocation);
2897 return activityInstance;
2900 internal void ScheduleExpression(ActivityWithResult activity, ActivityInstance parent, LocationEnvironment parentEnvironment, Location resultLocation, ResolveNextArgumentWorkItem nextArgumentWorkItem)
2902 Fx.Assert(resultLocation != null, "We should always schedule expressions with a result location.");
2904 if (!activity.IsMetadataCached || activity.CacheId != parent.Activity.CacheId)
2906 throw FxTrace.Exception.Argument("activity", SR.ActivityNotPartOfThisTree(activity.DisplayName, parent.Activity.DisplayName));
2909 if (activity.SkipArgumentResolution)
2913 Fx.Assert(!activity.UseOldFastPath || parent.SubState == ActivityInstance.Substate.Executing,
2914 "OldFastPath activities should have been handled by the Populate methods, unless this is a dynamic update");
2916 IncrementLastInstanceId();
2918 ScheduleExpression(activity, parent, resultLocation, nextArgumentWorkItem, this.lastInstanceId);
2920 else
2922 if (nextArgumentWorkItem != null)
2924 ScheduleItem(nextArgumentWorkItem);
2926 ScheduleActivity(activity, parent, null, null, parentEnvironment, null, resultLocation.CreateReference(true));
2930 void ScheduleExpression(ActivityWithResult activity, ActivityInstance parent, Location resultLocation, ResolveNextArgumentWorkItem nextArgumentWorkItem, long instanceId)
2932 if (TD.ActivityScheduledIsEnabled())
2934 TraceActivityScheduled(parent, activity, instanceId.ToString(CultureInfo.InvariantCulture));
2937 if (this.ShouldTrackActivityScheduledRecords)
2939 AddTrackingRecord(new ActivityScheduledRecord(this.WorkflowInstanceId, parent, new ActivityInfo(activity, instanceId)));
2942 ExecuteSynchronousExpressionWorkItem workItem = this.ExecuteSynchronousExpressionWorkItemPool.Acquire();
2943 workItem.Initialize(parent, activity, this.lastInstanceId, resultLocation, nextArgumentWorkItem);
2944 if (this.instanceMap != null)
2946 this.instanceMap.AddEntry(workItem);
2948 ScheduleItem(workItem);
2951 internal void ScheduleExpressionFaultPropagation(Activity activity, long instanceId, ActivityInstance parent, Exception exception)
2953 ActivityInstance instance = new ActivityInstance(activity);
2954 instance.Initialize(parent, this.instanceMap, parent.Environment, instanceId, this);
2956 if (!parent.HasPendingWork)
2958 // Force the parent to stay alive, and to attempt to execute its body if the fault is handled
2959 ScheduleItem(CreateEmptyWorkItem(parent));
2961 PropagateExceptionWorkItem workItem = new PropagateExceptionWorkItem(exception, instance);
2962 ScheduleItem(workItem);
2964 parent.SetInitializationIncomplete();
2967 // Argument and variables resolution for root activity is defered to execution time
2968 // invocation of this method means that we're ready to schedule Activity.Execute()
2969 internal void ScheduleBody(ActivityInstance activityInstance, bool requiresSymbolResolution,
2970 IDictionary<string, object> argumentValueOverrides, Location resultLocation)
2972 if (resultLocation == null)
2974 ExecuteActivityWorkItem workItem = this.ExecuteActivityWorkItemPool.Acquire();
2975 workItem.Initialize(activityInstance, requiresSymbolResolution, argumentValueOverrides);
2977 this.scheduler.PushWork(workItem);
2979 else
2981 this.scheduler.PushWork(new ExecuteExpressionWorkItem(activityInstance, requiresSymbolResolution, argumentValueOverrides, resultLocation));
2985 public NoPersistProperty CreateNoPersistProperty()
2987 return new NoPersistProperty(this);
2990 public AsyncOperationContext SetupAsyncOperationBlock(ActivityInstance owningActivity)
2992 if (this.activeOperations != null && this.activeOperations.ContainsKey(owningActivity))
2994 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.OnlyOneOperationPerActivity));
2997 this.EnterNoPersist();
2999 AsyncOperationContext context = new AsyncOperationContext(this, owningActivity);
3001 if (this.activeOperations == null)
3003 this.activeOperations = new Dictionary<ActivityInstance, AsyncOperationContext>();
3006 this.activeOperations.Add(owningActivity, context);
3008 return context;
3011 // Must always be called from a workflow thread
3012 public void CompleteOperation(ActivityInstance owningInstance, BookmarkCallback callback, object state)
3014 Fx.Assert(callback != null, "Use the other overload if callback is null.");
3016 CompleteAsyncOperationWorkItem workItem = new CompleteAsyncOperationWorkItem(
3017 new BookmarkCallbackWrapper(callback, owningInstance),
3018 this.bookmarkManager.GenerateTempBookmark(),
3019 state);
3020 CompleteOperation(workItem);
3023 // Must always be called from a workflow thread
3024 public void CompleteOperation(WorkItem asyncCompletionWorkItem)
3026 this.scheduler.EnqueueWork(asyncCompletionWorkItem);
3027 CompleteOperation(asyncCompletionWorkItem.ActivityInstance, false);
3030 // Must always be called from a workflow thread
3031 public void CompleteOperation(ActivityInstance owningInstance)
3033 CompleteOperation(owningInstance, true);
3036 void CompleteOperation(ActivityInstance owningInstance, bool exitNoPersist)
3038 Fx.Assert(owningInstance != null, "Cannot be called with a null instance.");
3039 Fx.Assert(this.activeOperations.ContainsKey(owningInstance), "The owning instance must be in the list if we've gotten here.");
3041 this.activeOperations.Remove(owningInstance);
3043 owningInstance.DecrementBusyCount();
3045 if (exitNoPersist)
3047 this.ExitNoPersist();
3051 internal void AddHandle(Handle handleToAdd)
3053 if (this.handles == null)
3055 this.handles = new List<Handle>();
3057 this.handles.Add(handleToAdd);
3060 [DataContract]
3061 internal class PersistenceWaiter
3063 Bookmark onPersistBookmark;
3064 ActivityInstance waitingInstance;
3066 public PersistenceWaiter(Bookmark onPersist, ActivityInstance waitingInstance)
3068 this.OnPersistBookmark = onPersist;
3069 this.WaitingInstance = waitingInstance;
3072 public Bookmark OnPersistBookmark
3076 return this.onPersistBookmark;
3078 private set
3080 this.onPersistBookmark = value;
3084 public ActivityInstance WaitingInstance
3088 return this.waitingInstance;
3090 private set
3092 this.waitingInstance = value;
3096 [DataMember(Name = "OnPersistBookmark")]
3097 internal Bookmark SerializedOnPersistBookmark
3099 get { return this.OnPersistBookmark; }
3100 set { this.OnPersistBookmark = value; }
3103 [DataMember(Name = "WaitingInstance")]
3104 internal ActivityInstance SerializedWaitingInstance
3106 get { return this.WaitingInstance; }
3107 set { this.WaitingInstance = value; }
3110 public WorkItem CreateWorkItem()
3112 return new PersistWorkItem(this);
3115 [DataContract]
3116 internal class PersistWorkItem : WorkItem
3118 PersistenceWaiter waiter;
3120 public PersistWorkItem(PersistenceWaiter waiter)
3121 : base(waiter.WaitingInstance)
3123 this.waiter = waiter;
3126 public override bool IsValid
3130 return true;
3134 public override ActivityInstance PropertyManagerOwner
3138 // Persist should not pick up user transaction / identity.
3139 return null;
3143 [DataMember(Name = "waiter")]
3144 internal PersistenceWaiter SerializedWaiter
3146 get { return this.waiter; }
3147 set { this.waiter = value; }
3150 public override void TraceCompleted()
3152 TraceRuntimeWorkItemCompleted();
3155 public override void TraceScheduled()
3157 TraceRuntimeWorkItemScheduled();
3160 public override void TraceStarting()
3162 TraceRuntimeWorkItemStarting();
3165 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
3167 if (executor.TryResumeUserBookmark(this.waiter.OnPersistBookmark, null, false) != BookmarkResumptionResult.Success)
3169 Fx.Assert("This should always be resumable.");
3172 IAsyncResult result = null;
3176 result = executor.host.OnBeginPersist(Fx.ThunkCallback(new AsyncCallback(OnPersistComplete)), executor);
3178 if (result.CompletedSynchronously)
3180 executor.host.OnEndPersist(result);
3183 catch (Exception e)
3185 if (Fx.IsFatal(e))
3187 throw;
3190 this.workflowAbortException = e;
3193 return result == null || result.CompletedSynchronously;
3196 void OnPersistComplete(IAsyncResult result)
3198 if (result.CompletedSynchronously)
3200 return;
3203 ActivityExecutor executor = (ActivityExecutor)result.AsyncState;
3207 executor.host.OnEndPersist(result);
3209 catch (Exception e)
3211 if (Fx.IsFatal(e))
3213 throw;
3216 this.workflowAbortException = e;
3219 executor.FinishWorkItem(this);
3222 public override void PostProcess(ActivityExecutor executor)
3224 if (this.ExceptionToPropagate != null)
3226 executor.AbortActivityInstance(this.waiter.WaitingInstance, this.ExceptionToPropagate);
3232 [DataContract]
3233 internal class AbortActivityWorkItem : WorkItem
3235 Exception reason;
3236 ActivityInstanceReference originalSource;
3238 ActivityExecutor executor;
3240 public AbortActivityWorkItem(ActivityExecutor executor, ActivityInstance activityInstance, Exception reason, ActivityInstanceReference originalSource)
3241 : base(activityInstance)
3243 this.reason = reason;
3244 this.originalSource = originalSource;
3246 this.IsEmpty = true;
3247 this.executor = executor;
3250 public override ActivityInstance OriginalExceptionSource
3254 return this.originalSource.ActivityInstance;
3258 public override bool IsValid
3262 return this.ActivityInstance.State == ActivityInstanceState.Executing;
3266 public override ActivityInstance PropertyManagerOwner
3270 Fx.Assert("This is never called.");
3272 return null;
3276 [DataMember(Name = "reason")]
3277 internal Exception SerializedReason
3279 get { return this.reason; }
3280 set { this.reason = value; }
3283 [DataMember(Name = "originalSource")]
3284 internal ActivityInstanceReference SerializedOriginalSource
3286 get { return this.originalSource; }
3287 set { this.originalSource = value; }
3290 public override void TraceCompleted()
3292 TraceRuntimeWorkItemCompleted();
3295 public override void TraceScheduled()
3297 TraceRuntimeWorkItemScheduled();
3300 public override void TraceStarting()
3302 TraceRuntimeWorkItemStarting();
3305 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
3307 Fx.Assert("This is never called");
3309 return true;
3312 public override void PostProcess(ActivityExecutor executor)
3314 executor.AbortActivityInstance(this.ActivityInstance, this.reason);
3316 // We always repropagate the exception from here
3317 this.ExceptionToPropagate = this.reason;
3319 // Tell the executor to decrement its NoPersistCount, if necessary.
3320 executor.ExitNoPersistForExceptionPropagation();
3324 [DataContract]
3325 internal class CompleteAsyncOperationWorkItem : BookmarkWorkItem
3327 public CompleteAsyncOperationWorkItem(BookmarkCallbackWrapper wrapper, Bookmark bookmark, object value)
3328 : base(wrapper, bookmark, value)
3330 this.ExitNoPersistRequired = true;
3334 [DataContract]
3335 internal class CancelActivityWorkItem : ActivityExecutionWorkItem
3337 public CancelActivityWorkItem(ActivityInstance activityInstance)
3338 : base(activityInstance)
3342 public override void TraceCompleted()
3344 if (TD.CompleteCancelActivityWorkItemIsEnabled())
3346 TD.CompleteCancelActivityWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
3350 public override void TraceScheduled()
3352 if (TD.ScheduleCancelActivityWorkItemIsEnabled())
3354 TD.ScheduleCancelActivityWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
3358 public override void TraceStarting()
3360 if (TD.StartCancelActivityWorkItemIsEnabled())
3362 TD.StartCancelActivityWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
3366 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
3370 this.ActivityInstance.Cancel(executor, bookmarkManager);
3372 catch (Exception e)
3374 if (Fx.IsFatal(e))
3376 throw;
3379 this.ExceptionToPropagate = e;
3382 return true;
3386 [DataContract]
3387 internal class ExecuteActivityWorkItem : ActivityExecutionWorkItem
3389 bool requiresSymbolResolution;
3390 IDictionary<string, object> argumentValueOverrides;
3392 // Called by the pool.
3393 public ExecuteActivityWorkItem()
3395 this.IsPooled = true;
3398 // Called by non-pool subclasses.
3399 protected ExecuteActivityWorkItem(ActivityInstance activityInstance, bool requiresSymbolResolution, IDictionary<string, object> argumentValueOverrides)
3400 : base(activityInstance)
3402 this.requiresSymbolResolution = requiresSymbolResolution;
3403 this.argumentValueOverrides = argumentValueOverrides;
3406 [DataMember(EmitDefaultValue = false, Name = "requiresSymbolResolution")]
3407 internal bool SerializedRequiresSymbolResolution
3409 get { return this.requiresSymbolResolution; }
3410 set { this.requiresSymbolResolution = value; }
3413 [DataMember(EmitDefaultValue = false, Name = "argumentValueOverrides")]
3414 internal IDictionary<string, object> SerializedArgumentValueOverrides
3416 get { return this.argumentValueOverrides; }
3417 set { this.argumentValueOverrides = value; }
3420 public void Initialize(ActivityInstance activityInstance, bool requiresSymbolResolution, IDictionary<string, object> argumentValueOverrides)
3422 base.Reinitialize(activityInstance);
3423 this.requiresSymbolResolution = requiresSymbolResolution;
3424 this.argumentValueOverrides = argumentValueOverrides;
3427 protected override void ReleaseToPool(ActivityExecutor executor)
3429 base.ClearForReuse();
3430 this.requiresSymbolResolution = false;
3431 this.argumentValueOverrides = null;
3433 executor.ExecuteActivityWorkItemPool.Release(this);
3436 public override void TraceScheduled()
3438 if (TD.ScheduleExecuteActivityWorkItemIsEnabled())
3440 TD.ScheduleExecuteActivityWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
3444 public override void TraceStarting()
3446 if (TD.StartExecuteActivityWorkItemIsEnabled())
3448 TD.StartExecuteActivityWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
3452 public override void TraceCompleted()
3454 if (TD.CompleteExecuteActivityWorkItemIsEnabled())
3456 TD.CompleteExecuteActivityWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
3460 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
3462 return ExecuteBody(executor, bookmarkManager, null);
3465 protected bool ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)
3469 if (this.requiresSymbolResolution)
3471 if (!this.ActivityInstance.ResolveArguments(executor, this.argumentValueOverrides, resultLocation))
3473 return true;
3476 if (!this.ActivityInstance.ResolveVariables(executor))
3478 return true;
3481 // We want to do this if there was no symbol resolution or if ResolveVariables completed
3482 // synchronously.
3483 this.ActivityInstance.SetInitializedSubstate(executor);
3485 if (executor.IsDebugged())
3487 executor.debugController.ActivityStarted(this.ActivityInstance);
3490 this.ActivityInstance.Execute(executor, bookmarkManager);
3492 catch (Exception e)
3494 if (Fx.IsFatal(e))
3496 throw;
3499 this.ExceptionToPropagate = e;
3502 return true;
3506 [DataContract]
3507 internal class ExecuteRootWorkItem : ExecuteActivityWorkItem
3509 public ExecuteRootWorkItem(ActivityInstance activityInstance, bool requiresSymbolResolution, IDictionary<string, object> argumentValueOverrides)
3510 : base(activityInstance, requiresSymbolResolution, argumentValueOverrides)
3514 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
3516 if (executor.ShouldTrackActivityScheduledRecords)
3518 executor.AddTrackingRecord(
3519 new ActivityScheduledRecord(
3520 executor.WorkflowInstanceId,
3521 null,
3522 this.ActivityInstance));
3525 return ExecuteBody(executor, bookmarkManager, null);
3529 [DataContract]
3530 internal class ExecuteExpressionWorkItem : ExecuteActivityWorkItem
3532 Location resultLocation;
3534 public ExecuteExpressionWorkItem(ActivityInstance activityInstance, bool requiresSymbolResolution, IDictionary<string, object> argumentValueOverrides, Location resultLocation)
3535 : base(activityInstance, requiresSymbolResolution, argumentValueOverrides)
3537 Fx.Assert(resultLocation != null, "We should only use this work item when we are resolving arguments/variables and therefore have a result location.");
3538 this.resultLocation = resultLocation;
3541 [DataMember(Name = "resultLocation")]
3542 internal Location SerializedResultLocation
3544 get { return this.resultLocation; }
3545 set { this.resultLocation = value; }
3548 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
3550 return ExecuteBody(executor, bookmarkManager, resultLocation);
3554 [DataContract]
3555 internal class PropagateExceptionWorkItem : ActivityExecutionWorkItem
3557 Exception exception;
3559 public PropagateExceptionWorkItem(Exception exception, ActivityInstance activityInstance)
3560 : base(activityInstance)
3562 Fx.Assert(exception != null, "We must not have a null exception.");
3564 this.exception = exception;
3565 this.IsEmpty = true;
3568 [DataMember(EmitDefaultValue = false, Name = "exception")]
3569 internal Exception SerializedException
3571 get { return this.exception; }
3572 set { this.exception = value; }
3575 public override void TraceScheduled()
3577 TraceRuntimeWorkItemScheduled();
3580 public override void TraceStarting()
3582 TraceRuntimeWorkItemStarting();
3585 public override void TraceCompleted()
3587 TraceRuntimeWorkItemCompleted();
3590 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
3592 Fx.Assert("This shouldn't be called because we are empty.");
3594 return false;
3597 public override void PostProcess(ActivityExecutor executor)
3599 ExceptionToPropagate = this.exception;
3603 [DataContract]
3604 internal class RethrowExceptionWorkItem : WorkItem
3606 Exception exception;
3607 ActivityInstanceReference source;
3609 public RethrowExceptionWorkItem(ActivityInstance activityInstance, Exception exception, ActivityInstanceReference source)
3610 : base(activityInstance)
3612 this.exception = exception;
3613 this.source = source;
3614 this.IsEmpty = true;
3617 public override bool IsValid
3621 return this.ActivityInstance.State == ActivityInstanceState.Executing;
3625 public override ActivityInstance PropertyManagerOwner
3629 Fx.Assert("This is never called.");
3631 return null;
3635 public override ActivityInstance OriginalExceptionSource
3639 return this.source.ActivityInstance;
3643 [DataMember(Name = "exception")]
3644 internal Exception SerializedException
3646 get { return this.exception; }
3647 set { this.exception = value; }
3650 [DataMember(Name = "source")]
3651 internal ActivityInstanceReference SerializedSource
3653 get { return this.source; }
3654 set { this.source = value; }
3657 public override void TraceCompleted()
3659 TraceRuntimeWorkItemCompleted();
3662 public override void TraceScheduled()
3664 TraceRuntimeWorkItemScheduled();
3667 public override void TraceStarting()
3669 TraceRuntimeWorkItemStarting();
3672 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
3674 Fx.Assert("This shouldn't be called because we are IsEmpty = true.");
3676 return true;
3679 public override void PostProcess(ActivityExecutor executor)
3681 executor.AbortActivityInstance(this.ActivityInstance, this.ExceptionToPropagate);
3682 this.ExceptionToPropagate = this.exception;
3686 [DataContract]
3687 internal class TransactionContextWaiter
3689 public TransactionContextWaiter(ActivityInstance instance, bool isRequires, RuntimeTransactionHandle handle, TransactionContextWaiterCallbackWrapper callbackWrapper, object state)
3691 Fx.Assert(instance != null, "Must have an instance.");
3692 Fx.Assert(handle != null, "Must have a handle.");
3693 Fx.Assert(callbackWrapper != null, "Must have a callbackWrapper");
3695 this.WaitingInstance = instance;
3696 this.IsRequires = isRequires;
3697 this.Handle = handle;
3698 this.State = state;
3699 this.CallbackWrapper = callbackWrapper;
3702 ActivityInstance waitingInstance;
3703 public ActivityInstance WaitingInstance
3707 return this.waitingInstance;
3709 private set
3711 this.waitingInstance = value;
3715 bool isRequires;
3716 public bool IsRequires
3720 return this.isRequires;
3722 private set
3724 this.isRequires = value;
3728 RuntimeTransactionHandle handle;
3729 public RuntimeTransactionHandle Handle
3733 return this.handle;
3735 private set
3737 this.handle = value;
3741 object state;
3742 public object State
3746 return this.state;
3748 private set
3750 this.state = value;
3754 TransactionContextWaiterCallbackWrapper callbackWrapper;
3755 public TransactionContextWaiterCallbackWrapper CallbackWrapper
3759 return this.callbackWrapper;
3761 private set
3763 this.callbackWrapper = value;
3767 [DataMember(Name = "WaitingInstance")]
3768 internal ActivityInstance SerializedWaitingInstance
3770 get { return this.WaitingInstance; }
3771 set { this.WaitingInstance = value; }
3774 [DataMember(EmitDefaultValue = false, Name = "IsRequires")]
3775 internal bool SerializedIsRequires
3777 get { return this.IsRequires; }
3778 set { this.IsRequires = value; }
3781 [DataMember(Name = "Handle")]
3782 internal RuntimeTransactionHandle SerializedHandle
3784 get { return this.Handle; }
3785 set { this.Handle = value; }
3788 [DataMember(EmitDefaultValue = false, Name = "State")]
3789 internal object SerializedState
3791 get { return this.State; }
3792 set { this.State = value; }
3795 [DataMember(Name = "CallbackWrapper")]
3796 internal TransactionContextWaiterCallbackWrapper SerializedCallbackWrapper
3798 get { return this.CallbackWrapper; }
3799 set { this.CallbackWrapper = value; }
3803 [DataContract]
3804 internal class TransactionContextWaiterCallbackWrapper : CallbackWrapper
3806 static readonly Type callbackType = typeof(Action<NativeActivityTransactionContext, object>);
3807 static readonly Type[] transactionCallbackParameterTypes = new Type[] { typeof(NativeActivityTransactionContext), typeof(object) };
3809 public TransactionContextWaiterCallbackWrapper(Action<NativeActivityTransactionContext, object> action, ActivityInstance owningInstance)
3810 : base(action, owningInstance)
3814 [Fx.Tag.SecurityNote(Critical = "Because we are calling EnsureCallback",
3815 Safe = "Safe because the method needs to be part of an Activity and we are casting to the callback type and it has a very specific signature. The author of the callback is buying into being invoked from PT.")]
3816 [SecuritySafeCritical]
3817 public void Invoke(NativeActivityTransactionContext context, object value)
3819 EnsureCallback(callbackType, transactionCallbackParameterTypes);
3820 Action<NativeActivityTransactionContext, object> callback = (Action<NativeActivityTransactionContext, object>)this.Callback;
3821 callback(context, value);
3825 // This is not DataContract because this is always scheduled in a no-persist zone.
3826 // This work items exits the no persist zone when it is released.
3827 class CompleteTransactionWorkItem : WorkItem
3829 static AsyncCallback persistCompleteCallback;
3830 static AsyncCallback commitCompleteCallback;
3831 static Action<object, TimeoutException> outcomeDeterminedCallback;
3833 RuntimeTransactionData runtimeTransaction;
3834 ActivityExecutor executor;
3836 public CompleteTransactionWorkItem(ActivityInstance instance)
3837 : base(instance)
3839 this.ExitNoPersistRequired = true;
3842 static AsyncCallback PersistCompleteCallback
3846 if (persistCompleteCallback == null)
3848 persistCompleteCallback = Fx.ThunkCallback(new AsyncCallback(OnPersistComplete));
3851 return persistCompleteCallback;
3855 static AsyncCallback CommitCompleteCallback
3859 if (commitCompleteCallback == null)
3861 commitCompleteCallback = Fx.ThunkCallback(new AsyncCallback(OnCommitComplete));
3864 return commitCompleteCallback;
3868 static Action<object, TimeoutException> OutcomeDeterminedCallback
3872 if (outcomeDeterminedCallback == null)
3874 outcomeDeterminedCallback = new Action<object, TimeoutException>(OnOutcomeDetermined);
3877 return outcomeDeterminedCallback;
3881 public override bool IsValid
3885 return true;
3889 public override ActivityInstance PropertyManagerOwner
3893 return null;
3897 public override void TraceCompleted()
3899 TraceRuntimeWorkItemCompleted();
3902 public override void TraceScheduled()
3904 TraceRuntimeWorkItemScheduled();
3907 public override void TraceStarting()
3909 TraceRuntimeWorkItemStarting();
3912 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
3914 this.runtimeTransaction = executor.runtimeTransaction;
3915 this.executor = executor;
3917 // We need to take care of any pending cancelation
3918 this.executor.SchedulePendingCancelation();
3920 bool completeSelf;
3923 // If the transaction is already rolled back, skip the persistence. This allows us to avoid aborting the instance.
3924 completeSelf = CheckTransactionAborted();
3925 if (!completeSelf)
3927 IAsyncResult result = new TransactionalPersistAsyncResult(this.executor, PersistCompleteCallback, this);
3928 if (result.CompletedSynchronously)
3930 completeSelf = FinishPersist(result);
3934 catch (Exception e)
3936 if (Fx.IsFatal(e))
3938 throw;
3941 HandleException(e);
3942 completeSelf = true;
3945 if (completeSelf)
3947 this.executor.runtimeTransaction = null;
3949 TraceTransactionOutcome();
3950 return true;
3953 return false;
3956 void TraceTransactionOutcome()
3958 if (TD.RuntimeTransactionCompleteIsEnabled())
3960 TD.RuntimeTransactionComplete(this.runtimeTransaction.TransactionStatus.ToString());
3964 void HandleException(Exception exception)
3968 this.runtimeTransaction.OriginalTransaction.Rollback(exception);
3970 catch (Exception e)
3972 if (Fx.IsFatal(e))
3974 throw;
3977 this.workflowAbortException = e;
3980 if (this.runtimeTransaction.TransactionHandle.AbortInstanceOnTransactionFailure)
3982 // We might be overwriting a more recent exception from above, but it is
3983 // more important that we tell the user why they failed originally.
3984 this.workflowAbortException = exception;
3986 else
3988 this.ExceptionToPropagate = exception;
3992 static void OnPersistComplete(IAsyncResult result)
3994 if (result.CompletedSynchronously)
3996 return;
3999 CompleteTransactionWorkItem thisPtr = (CompleteTransactionWorkItem)result.AsyncState;
4000 bool completeSelf = true;
4004 completeSelf = thisPtr.FinishPersist(result);
4006 catch (Exception e)
4008 if (Fx.IsFatal(e))
4010 throw;
4013 thisPtr.HandleException(e);
4014 completeSelf = true;
4017 if (completeSelf)
4019 thisPtr.executor.runtimeTransaction = null;
4021 thisPtr.TraceTransactionOutcome();
4023 thisPtr.executor.FinishWorkItem(thisPtr);
4027 bool FinishPersist(IAsyncResult result)
4029 TransactionalPersistAsyncResult.End(result);
4031 return CompleteTransaction();
4034 bool CompleteTransaction()
4036 PreparingEnlistment enlistment = null;
4038 lock (this.runtimeTransaction)
4040 if (this.runtimeTransaction.PendingPreparingEnlistment != null)
4042 enlistment = this.runtimeTransaction.PendingPreparingEnlistment;
4045 this.runtimeTransaction.HasPrepared = true;
4048 if (enlistment != null)
4050 enlistment.Prepared();
4053 Transaction original = this.runtimeTransaction.OriginalTransaction;
4055 DependentTransaction dependentTransaction = original as DependentTransaction;
4056 if (dependentTransaction != null)
4058 dependentTransaction.Complete();
4059 return CheckOutcome();
4061 else
4063 CommittableTransaction committableTransaction = original as CommittableTransaction;
4064 if (committableTransaction != null)
4066 IAsyncResult result = committableTransaction.BeginCommit(CommitCompleteCallback, this);
4068 if (result.CompletedSynchronously)
4070 return FinishCommit(result);
4072 else
4074 return false;
4077 else
4079 return CheckOutcome();
4084 static void OnCommitComplete(IAsyncResult result)
4086 if (result.CompletedSynchronously)
4088 return;
4091 CompleteTransactionWorkItem thisPtr = (CompleteTransactionWorkItem)result.AsyncState;
4092 bool completeSelf = true;
4096 completeSelf = thisPtr.FinishCommit(result);
4098 catch (Exception e)
4100 if (Fx.IsFatal(e))
4102 throw;
4105 thisPtr.HandleException(e);
4106 completeSelf = true;
4109 if (completeSelf)
4111 thisPtr.executor.runtimeTransaction = null;
4113 thisPtr.TraceTransactionOutcome();
4115 thisPtr.executor.FinishWorkItem(thisPtr);
4119 bool FinishCommit(IAsyncResult result)
4121 ((CommittableTransaction)this.runtimeTransaction.OriginalTransaction).EndCommit(result);
4123 return CheckOutcome();
4126 bool CheckOutcome()
4128 AsyncWaitHandle completionEvent = null;
4130 lock (this.runtimeTransaction)
4132 TransactionStatus status = this.runtimeTransaction.TransactionStatus;
4134 if (status == TransactionStatus.Active)
4136 completionEvent = new AsyncWaitHandle();
4137 this.runtimeTransaction.CompletionEvent = completionEvent;
4141 if (completionEvent != null)
4143 if (!completionEvent.WaitAsync(OutcomeDeterminedCallback, this, ActivityDefaults.TransactionCompletionTimeout))
4145 return false;
4149 return FinishCheckOutcome();
4152 static void OnOutcomeDetermined(object state, TimeoutException asyncException)
4154 CompleteTransactionWorkItem thisPtr = (CompleteTransactionWorkItem)state;
4155 bool completeSelf = true;
4157 if (asyncException != null)
4159 thisPtr.HandleException(asyncException);
4161 else
4165 completeSelf = thisPtr.FinishCheckOutcome();
4167 catch (Exception e)
4169 if (Fx.IsFatal(e))
4171 throw;
4174 thisPtr.HandleException(e);
4175 completeSelf = true;
4179 if (completeSelf)
4181 thisPtr.executor.runtimeTransaction = null;
4183 thisPtr.TraceTransactionOutcome();
4185 thisPtr.executor.FinishWorkItem(thisPtr);
4189 bool FinishCheckOutcome()
4191 CheckTransactionAborted();
4192 return true;
4195 bool CheckTransactionAborted()
4199 TransactionHelper.ThrowIfTransactionAbortedOrInDoubt(this.runtimeTransaction.OriginalTransaction);
4200 return false;
4202 catch (TransactionException exception)
4204 if (this.runtimeTransaction.TransactionHandle.AbortInstanceOnTransactionFailure)
4206 this.workflowAbortException = exception;
4208 else
4210 this.ExceptionToPropagate = exception;
4212 return true;
4216 public override void PostProcess(ActivityExecutor executor)
4220 class TransactionalPersistAsyncResult : TransactedAsyncResult
4222 CompleteTransactionWorkItem workItem;
4223 static readonly AsyncCompletion onPersistComplete = new AsyncCompletion(OnPersistComplete);
4224 readonly ActivityExecutor executor;
4226 public TransactionalPersistAsyncResult(ActivityExecutor executor, AsyncCallback callback, object state)
4227 : base(callback, state)
4229 this.executor = executor;
4230 this.workItem = (CompleteTransactionWorkItem)state;
4231 IAsyncResult result = null;
4232 using (PrepareTransactionalCall(this.executor.CurrentTransaction))
4236 result = this.executor.host.OnBeginPersist(PrepareAsyncCompletion(TransactionalPersistAsyncResult.onPersistComplete), this);
4238 catch (Exception e)
4240 if (Fx.IsFatal(e))
4242 throw;
4244 this.workItem.workflowAbortException = e;
4245 throw;
4248 if (SyncContinue(result))
4250 Complete(true);
4254 public static void End(IAsyncResult result)
4256 AsyncResult.End<TransactionalPersistAsyncResult>(result);
4259 static bool OnPersistComplete(IAsyncResult result)
4261 TransactionalPersistAsyncResult thisPtr = (TransactionalPersistAsyncResult)result.AsyncState;
4265 thisPtr.executor.host.OnEndPersist(result);
4267 catch (Exception e)
4269 if (Fx.IsFatal(e))
4271 throw;
4273 thisPtr.workItem.workflowAbortException = e;
4274 throw;
4277 return true;
4282 [DataContract]
4283 internal class TransactionContextWorkItem : ActivityExecutionWorkItem
4285 TransactionContextWaiter waiter;
4287 public TransactionContextWorkItem(TransactionContextWaiter waiter)
4288 : base(waiter.WaitingInstance)
4290 this.waiter = waiter;
4292 if (this.waiter.IsRequires)
4294 this.ExitNoPersistRequired = true;
4298 [DataMember(Name = "waiter")]
4299 internal TransactionContextWaiter SerializedWaiter
4301 get { return this.waiter; }
4302 set { this.waiter = value; }
4305 public override void TraceCompleted()
4307 if (TD.CompleteTransactionContextWorkItemIsEnabled())
4309 TD.CompleteTransactionContextWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
4313 public override void TraceScheduled()
4315 if (TD.ScheduleTransactionContextWorkItemIsEnabled())
4317 TD.ScheduleTransactionContextWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
4321 public override void TraceStarting()
4323 if (TD.StartTransactionContextWorkItemIsEnabled())
4325 TD.StartTransactionContextWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
4329 public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
4331 NativeActivityTransactionContext transactionContext = null;
4335 transactionContext = new NativeActivityTransactionContext(this.ActivityInstance, executor, bookmarkManager, this.waiter.Handle);
4336 waiter.CallbackWrapper.Invoke(transactionContext, this.waiter.State);
4338 catch (Exception e)
4340 if (Fx.IsFatal(e))
4342 throw;
4345 this.ExceptionToPropagate = e;
4347 finally
4349 if (transactionContext != null)
4351 transactionContext.Dispose();
4355 return true;
4359 // This class is not DataContract since we only create instances of it while we
4360 // are in no-persist zones
4361 class RuntimeTransactionData
4363 public RuntimeTransactionData(RuntimeTransactionHandle handle, Transaction transaction, ActivityInstance isolationScope)
4365 this.TransactionHandle = handle;
4366 this.OriginalTransaction = transaction;
4367 this.ClonedTransaction = transaction.Clone();
4368 this.IsolationScope = isolationScope;
4369 this.TransactionStatus = TransactionStatus.Active;
4372 public AsyncWaitHandle CompletionEvent
4374 get;
4375 set;
4378 public PreparingEnlistment PendingPreparingEnlistment
4380 get;
4381 set;
4384 public bool HasPrepared
4386 get;
4387 set;
4390 public bool ShouldScheduleCompletion
4392 get;
4393 set;
4396 public TransactionStatus TransactionStatus
4398 get;
4399 set;
4402 public bool IsRootCancelPending
4404 get;
4405 set;
4408 public RuntimeTransactionHandle TransactionHandle
4410 get;
4411 private set;
4414 public Transaction ClonedTransaction
4416 get;
4417 private set;
4420 public Transaction OriginalTransaction
4422 get;
4423 private set;
4426 public ActivityInstance IsolationScope
4428 get;
4429 private set;
4432 [Fx.Tag.Throws(typeof(Exception), "Doesn't handle any exceptions coming from Rollback.")]
4433 public void Rollback(Exception reason)
4435 Fx.Assert(this.OriginalTransaction != null, "We always have an original transaction.");
4437 this.OriginalTransaction.Rollback(reason);
4441 class AssociateKeysAsyncResult : TransactedAsyncResult
4443 static readonly AsyncCompletion associatedCallback = new AsyncCompletion(OnAssociated);
4445 readonly ActivityExecutor executor;
4447 public AssociateKeysAsyncResult(ActivityExecutor executor, ICollection<InstanceKey> keysToAssociate, AsyncCallback callback, object state)
4448 : base(callback, state)
4450 this.executor = executor;
4452 IAsyncResult result;
4453 using (PrepareTransactionalCall(this.executor.CurrentTransaction))
4455 result = this.executor.host.OnBeginAssociateKeys(keysToAssociate, PrepareAsyncCompletion(associatedCallback), this);
4457 if (SyncContinue(result))
4459 Complete(true);
4463 static bool OnAssociated(IAsyncResult result)
4465 AssociateKeysAsyncResult thisPtr = (AssociateKeysAsyncResult)result.AsyncState;
4466 thisPtr.executor.host.OnEndAssociateKeys(result);
4467 return true;
4470 public static void End(IAsyncResult result)
4472 AsyncResult.End<AssociateKeysAsyncResult>(result);
4476 class PoolOfEmptyWorkItems : Pool<EmptyWorkItem>
4478 protected override EmptyWorkItem CreateNew()
4480 return new EmptyWorkItem();
4484 class PoolOfExecuteActivityWorkItems : Pool<ExecuteActivityWorkItem>
4486 protected override ExecuteActivityWorkItem CreateNew()
4488 return new ExecuteActivityWorkItem();
4492 class PoolOfExecuteSynchronousExpressionWorkItems : Pool<ExecuteSynchronousExpressionWorkItem>
4494 protected override ExecuteSynchronousExpressionWorkItem CreateNew()
4496 return new ExecuteSynchronousExpressionWorkItem();
4500 class PoolOfCompletionWorkItems : Pool<CompletionCallbackWrapper.CompletionWorkItem>
4502 protected override CompletionCallbackWrapper.CompletionWorkItem CreateNew()
4504 return new CompletionCallbackWrapper.CompletionWorkItem();
4508 class PoolOfNativeActivityContexts : Pool<NativeActivityContext>
4510 protected override NativeActivityContext CreateNew()
4512 return new NativeActivityContext();
4516 class PoolOfCodeActivityContexts : Pool<CodeActivityContext>
4518 protected override CodeActivityContext CreateNew()
4520 return new CodeActivityContext();
4524 class PoolOfResolveNextArgumentWorkItems : Pool<ResolveNextArgumentWorkItem>
4526 protected override ResolveNextArgumentWorkItem CreateNew()
4528 return new ResolveNextArgumentWorkItem();
4532 //This is used in ScheduleDelegate when the handler is null. We use this dummy activity to
4533 //set as the 'Activity' of the completed ActivityInstance.
4534 class EmptyDelegateActivity : NativeActivity
4536 internal EmptyDelegateActivity()
4540 protected override void Execute(NativeActivityContext context)
4542 Fx.Assert(false, "This activity should never be executed. It is a dummy activity");