1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Activities
8 using System.Activities.Hosting;
9 using System.Activities.DurableInstancing;
10 using System.Activities.DynamicUpdate;
11 using System.Activities.Persistence;
12 using System.Activities.Runtime;
13 using System.Activities.Tracking;
14 using System.Collections.Generic;
15 using System.Collections.ObjectModel;
16 using System.ComponentModel;
19 using System.Runtime.DurableInstancing;
20 using System.Runtime.Serialization;
21 using System.Threading;
22 using System.Transactions;
23 using System.Xml.Linq;
25 using System.Security;
27 // WorkflowApplication is free-threaded. It is responsible for the correct locking and usage of the ActivityExecutor.
28 // Given that there are two simultaneous users of ActivityExecutor (WorkflowApplication and NativeActivityContext),
29 // it is imperative that WorkflowApplication only calls into ActivityExecutor when there are no activities executing
30 // (and thus no worries about colliding with AEC calls).
32 // SYNCHRONIZATION SCHEME
33 // WorkflowInstance is defined to not be thread safe and to disallow all operations while it is (potentially
34 // asynchronously) running. The WorkflowInstance is in the "running" state between a call to Run and the
35 // subsequent call to either WorkflowInstance NotifyPaused or NotifyUnhandledException.
36 // WorkflowApplication keeps track of a boolean "isBusy" and a list of pending operations. WI is busy whenever
37 // it is servicing an operation or the runtime is in the "running" state.
38 // Enqueue - This enqueues an operation into the pending operation list. If WI is not busy then the operation
39 // can be serviced immediately. This is the only place where "isBusy" flips to true.
40 // OnNotifiedUnhandledException - This method performs some processing and then calls OnNotifiedPaused.
41 // OnNotifiedPaused - This method is only ever called when "isBusy" is true. It first checks to see if there
42 // is other work to be done (prioritization: raise completed, handle an operation, resume execution, raise
43 // idle, stop). This is the only place where "isBusy" flips to false and this only occurs when there is no
44 // other work to be done.
45 // [Force]NotifyOperationComplete - These methods are called by individual operations when they are done
46 // processing. If the operation was notified (IE - actually performed in the eyes of WI) then this is simply
47 // a call to OnNotifiedPaused.
48 // Operation notification - The InstanceOperation class keeps tracks of whether a specified operation was
49 // dispatched by WI or not. If it was dispatched (determined either in Enqueue, FindOperation, or Remove)
50 // then it MUST result in a call to OnNotifiedPaused when complete.
51 [Fx.Tag.XamlVisible(false)]
52 public sealed class WorkflowApplication : WorkflowInstance
54 static AsyncCallback eventFrameCallback;
55 static IdleEventHandler idleHandler;
56 static CompletedEventHandler completedHandler;
57 static UnhandledExceptionEventHandler unhandledExceptionHandler;
58 static Action<object, TimeoutException> waitAsyncCompleteCallback;
59 static readonly WorkflowIdentity unknownIdentity = new WorkflowIdentity();
61 Action<WorkflowApplicationAbortedEventArgs> onAborted;
62 Action<WorkflowApplicationEventArgs> onUnloaded;
63 Action<WorkflowApplicationCompletedEventArgs> onCompleted;
64 Func<WorkflowApplicationUnhandledExceptionEventArgs, UnhandledExceptionAction> onUnhandledException;
65 Func<WorkflowApplicationIdleEventArgs, PersistableIdleAction> onPersistableIdle;
66 Action<WorkflowApplicationIdleEventArgs> onIdle;
68 WorkflowEventData eventData;
70 WorkflowInstanceExtensionManager extensions;
71 PersistencePipeline persistencePipelineInUse;
72 InstanceStore instanceStore;
73 PersistenceManager persistenceManager;
74 WorkflowApplicationState state;
77 Action invokeCompletedCallback;
79 bool instanceIdSet; // Checking for Guid.Empty is expensive.
81 // Tracking for one-time actions per in-memory pulse
85 // Tracking for one-time actions per instance lifetime (these end up being persisted)
86 bool hasRaisedCompleted;
88 Quack<InstanceOperation> pendingOperations;
90 bool hasExecutionOccurredSinceLastIdle;
92 // Count of operations that are about to be enqueued.
93 // We use this when enqueueing multiple operations, to avoid raising
94 // idle on dequeue of the first operation.
95 int pendingUnenqueued;
97 // We use this to keep track of the number of "interesting" things that have happened.
98 // Notifying operations and calling Run on the runtime count as interesting things.
99 // All operations are stamped with the actionCount at the time of being enqueued.
102 // Initial creation data
103 IDictionary<string, object> initialWorkflowArguments;
104 IList<Handle> rootExecutionProperties;
106 IDictionary<XName, InstanceValue> instanceMetadata;
108 public WorkflowApplication(Activity workflowDefinition)
109 : this(workflowDefinition, (WorkflowIdentity)null)
113 public WorkflowApplication(Activity workflowDefinition, IDictionary<string, object> inputs)
114 : this(workflowDefinition, inputs, (WorkflowIdentity)null)
118 public WorkflowApplication(Activity workflowDefinition, WorkflowIdentity definitionIdentity)
119 : base(workflowDefinition, definitionIdentity)
121 this.pendingOperations = new Quack<InstanceOperation>();
122 Fx.Assert(this.state == WorkflowApplicationState.Paused, "We always start out paused (the default)");
125 public WorkflowApplication(Activity workflowDefinition, IDictionary<string, object> inputs, WorkflowIdentity definitionIdentity)
126 : this(workflowDefinition, definitionIdentity)
130 throw FxTrace.Exception.ArgumentNull("inputs");
132 this.initialWorkflowArguments = inputs;
135 WorkflowApplication(Activity workflowDefinition, IDictionary<string, object> inputs, IList<Handle> executionProperties)
136 : this(workflowDefinition)
138 this.initialWorkflowArguments = inputs;
139 this.rootExecutionProperties = executionProperties;
142 public InstanceStore InstanceStore
146 return this.instanceStore;
151 this.instanceStore = value;
155 public WorkflowInstanceExtensionManager Extensions
159 if (this.extensions == null)
161 this.extensions = new WorkflowInstanceExtensionManager();
164 this.extensions.MakeReadOnly();
167 return this.extensions;
171 public Action<WorkflowApplicationAbortedEventArgs> Aborted
175 return this.onAborted;
179 ThrowIfMulticast(value);
180 this.onAborted = value;
184 public Action<WorkflowApplicationEventArgs> Unloaded
188 return this.onUnloaded;
192 ThrowIfMulticast(value);
193 this.onUnloaded = value;
197 public Action<WorkflowApplicationCompletedEventArgs> Completed
201 return this.onCompleted;
205 ThrowIfMulticast(value);
206 this.onCompleted = value;
210 public Func<WorkflowApplicationUnhandledExceptionEventArgs, UnhandledExceptionAction> OnUnhandledException
214 return this.onUnhandledException;
218 ThrowIfMulticast(value);
219 this.onUnhandledException = value;
223 public Action<WorkflowApplicationIdleEventArgs> Idle
231 ThrowIfMulticast(value);
236 public Func<WorkflowApplicationIdleEventArgs, PersistableIdleAction> PersistableIdle
240 return this.onPersistableIdle;
244 ThrowIfMulticast(value);
245 this.onPersistableIdle = value;
249 public override Guid Id
253 if (!this.instanceIdSet)
255 lock (this.pendingOperations)
257 if (!this.instanceIdSet)
259 this.instanceId = Guid.NewGuid();
260 this.instanceIdSet = true;
264 return this.instanceId;
268 protected internal override bool SupportsInstanceKeys
276 static AsyncCallback EventFrameCallback
280 if (eventFrameCallback == null)
282 eventFrameCallback = Fx.ThunkCallback(new AsyncCallback(EventFrame));
285 return eventFrameCallback;
289 WorkflowEventData EventData
293 if (this.eventData == null)
295 this.eventData = new WorkflowEventData(this);
298 return this.eventData;
302 bool HasPersistenceProvider
306 return this.persistenceManager != null;
314 return this.isInHandler && this.handlerThreadId == Thread.CurrentThread.ManagedThreadId;
318 bool IsInTerminalState
322 return this.state == WorkflowApplicationState.Unloaded || this.state == WorkflowApplicationState.Aborted;
326 public void AddInitialInstanceValues(IDictionary<XName, object> writeOnlyValues)
330 if (writeOnlyValues != null)
332 if (this.instanceMetadata == null)
334 this.instanceMetadata = new Dictionary<XName, InstanceValue>(writeOnlyValues.Count);
337 foreach (KeyValuePair<XName, object> pair in writeOnlyValues)
339 // We use the indexer so that we can replace keys that already exist
340 this.instanceMetadata[pair.Key] = new InstanceValue(pair.Value, InstanceValueOptions.Optional | InstanceValueOptions.WriteOnly);
345 // host-facing access to our cascading ExtensionManager resolution. Used by WorkflowApplicationEventArgs
346 internal IEnumerable<T> InternalGetExtensions<T>() where T : class
348 return base.GetExtensions<T>();
352 static void EventFrame(IAsyncResult result)
354 if (result.CompletedSynchronously)
359 WorkflowEventData data = (WorkflowEventData)result.AsyncState;
360 WorkflowApplication thisPtr = data.Instance;
366 Exception abortException = null;
370 // The "false" is to notify that we are not still [....]
371 done = data.NextCallback(result, thisPtr, false);
383 if (abortException != null)
385 thisPtr.AbortInstance(abortException, true);
392 thisPtr.OnNotifyPaused();
397 bool ShouldRaiseComplete(WorkflowInstanceState state)
399 return state == WorkflowInstanceState.Complete && !this.hasRaisedCompleted;
402 void Enqueue(InstanceOperation operation)
404 Enqueue(operation, false);
407 void Enqueue(InstanceOperation operation, bool push)
409 lock (this.pendingOperations)
411 operation.ActionId = this.actionCount;
415 // If base.IsReadOnly == false, we can't call the Controller yet because WorkflowInstance is not initialized.
416 // But that's okay; if the instance isn't initialized then the scheduler's not running yet, so no need to pause it.
417 if (operation.InterruptsScheduler && base.IsReadOnly)
419 this.Controller.RequestPause();
422 AddToPending(operation, push);
426 // first make sure we're ready to run
427 if (operation.RequiresInitialized)
432 if (!operation.CanRun(this) && !this.IsInTerminalState)
434 AddToPending(operation, push);
438 // Action: Notifying an operation
441 // We've essentially just notified this
442 // operation that it is free to do its
449 operation.Notified = true;
457 void IncrementPendingUnenqueud()
459 lock (this.pendingOperations)
461 this.pendingUnenqueued++;
465 void DecrementPendingUnenqueud()
467 lock (this.pendingOperations)
469 this.pendingUnenqueued--;
473 void AddToPending(InstanceOperation operation, bool push)
477 // We're already initialized
478 operation.RequiresInitialized = false;
483 this.pendingOperations.PushFront(operation);
487 this.pendingOperations.Enqueue(operation);
490 operation.OnEnqueued();
493 bool Remove(InstanceOperation operation)
495 lock (this.pendingOperations)
497 return this.pendingOperations.Remove(operation);
501 bool WaitForTurn(InstanceOperation operation, TimeSpan timeout)
504 return this.WaitForTurnNoEnqueue(operation, timeout);
507 bool WaitForTurnNoEnqueue(InstanceOperation operation, TimeSpan timeout)
509 if (!operation.WaitForTurn(timeout))
511 if (Remove(operation))
513 throw FxTrace.Exception.AsError(new TimeoutException(SR.TimeoutOnOperation(timeout)));
519 bool WaitForTurnAsync(InstanceOperation operation, TimeSpan timeout, Action<object, TimeoutException> callback, object state)
521 return WaitForTurnAsync(operation, false, timeout, callback, state);
524 bool WaitForTurnAsync(InstanceOperation operation, bool push, TimeSpan timeout, Action<object, TimeoutException> callback, object state)
526 Enqueue(operation, push);
528 return this.WaitForTurnNoEnqueueAsync(operation, timeout, callback, state);
531 bool WaitForTurnNoEnqueueAsync(InstanceOperation operation, TimeSpan timeout, Action<object, TimeoutException> callback, object state)
533 if (waitAsyncCompleteCallback == null)
535 waitAsyncCompleteCallback = new Action<object, TimeoutException>(OnWaitAsyncComplete);
537 return operation.WaitForTurnAsync(timeout, waitAsyncCompleteCallback, new WaitForTurnData(callback, state, operation, this));
540 static void OnWaitAsyncComplete(object state, TimeoutException exception)
542 WaitForTurnData data = (WaitForTurnData)state;
544 if (!data.Instance.Remove(data.Operation))
549 data.Callback(data.State, exception);
552 // For when we know that the operation is non-null
553 // and notified (like in async paths)
554 void ForceNotifyOperationComplete()
559 void NotifyOperationComplete(InstanceOperation operation)
561 if (operation != null && operation.Notified)
567 InstanceOperation FindOperation()
569 if (this.pendingOperations.Count > 0)
571 // Special case the first one
572 InstanceOperation temp = this.pendingOperations[0];
574 if (temp.RequiresInitialized)
579 // Even if we can't run this operation we want to notify
580 // it if all the operations are invalid. This will cause
581 // the Validate* method to throw to the caller.
582 if (temp.CanRun(this) || this.IsInTerminalState)
584 // Action: Notifying an operation
587 temp.Notified = true;
588 this.pendingOperations.Dequeue();
593 for (int i = 0; i < this.pendingOperations.Count; i++)
595 temp = this.pendingOperations[i];
597 if (temp.RequiresInitialized)
602 if (temp.CanRun(this))
604 // Action: Notifying an operation
607 temp.Notified = true;
608 this.pendingOperations.Remove(i);
618 // assumes that we're called under the pendingOperations lock
619 void EnsureInitialized()
621 if (!base.IsReadOnly)
623 // For newly created workflows (e.g. not the Load() case), we need to initialize now
624 base.RegisterExtensionManager(this.extensions);
625 base.Initialize(this.initialWorkflowArguments, this.rootExecutionProperties);
627 // make sure we have a persistence manager if necessary
628 if (this.persistenceManager == null && this.instanceStore != null)
630 Fx.Assert(this.Id != Guid.Empty, "should have a valid Id at this point");
631 this.persistenceManager = new PersistenceManager(this.instanceStore, GetInstanceMetadata(), this.Id);
636 protected override void OnNotifyPaused()
638 Fx.Assert(this.isBusy, "We're always busy when we get this notification.");
640 WorkflowInstanceState? localInstanceState = null;
643 localInstanceState = this.Controller.State;
645 WorkflowApplicationState localApplicationState = this.state;
647 bool stillSync = true;
651 if (localInstanceState.HasValue && ShouldRaiseComplete(localInstanceState.Value))
653 Exception abortException = null;
657 // We're about to notify the world that this instance is completed
658 // so let's make it official.
659 this.hasRaisedCompleted = true;
661 if (completedHandler == null)
663 completedHandler = new CompletedEventHandler();
665 stillSync = completedHandler.Run(this);
677 if (abortException != null)
679 AbortInstance(abortException, true);
684 InstanceOperation toRun = null;
686 bool shouldRaiseIdleNow;
688 lock (this.pendingOperations)
690 toRun = FindOperation();
692 // Cache the state in local variables to ensure that none
693 // of the decision points in the ensuing "if" statement flip
694 // when control gets out of the lock.
695 shouldRunNow = (localInstanceState.HasValue && localInstanceState == WorkflowInstanceState.Runnable && localApplicationState == WorkflowApplicationState.Runnable);
696 shouldRaiseIdleNow = this.hasExecutionOccurredSinceLastIdle &&
697 localInstanceState.HasValue && localInstanceState == WorkflowInstanceState.Idle &&
698 !this.hasRaisedCompleted && this.pendingUnenqueued == 0;
700 if (toRun == null && !shouldRunNow && !shouldRaiseIdleNow)
712 else if (shouldRaiseIdleNow)
714 this.hasExecutionOccurredSinceLastIdle = false;
716 Fx.Assert(this.isBusy, "we must be busy if we're raising idle");
718 Exception abortException = null;
722 if (idleHandler == null)
724 idleHandler = new IdleEventHandler();
726 stillSync = idleHandler.Run(this);
738 if (abortException != null)
740 AbortInstance(abortException, true);
743 else if (shouldRunNow)
745 this.hasExecutionOccurredSinceLastIdle = true;
747 // Action: Running the scheduler
750 this.Controller.Run();
757 // used by WorkflowInvoker in the InvokeAsync case
758 internal void GetCompletionStatus(out Exception terminationException, out bool cancelled)
760 IDictionary<string, object> dummyOutputs;
761 ActivityInstanceState completionState = this.Controller.GetCompletionState(out dummyOutputs, out terminationException);
762 Fx.Assert(completionState != ActivityInstanceState.Executing, "Activity cannot be executing when this method is called");
763 cancelled = (completionState == ActivityInstanceState.Canceled);
766 protected internal override void OnRequestAbort(Exception reason)
768 this.AbortInstance(reason, false);
773 Abort(SR.DefaultAbortReason);
776 public void Abort(string reason)
778 this.Abort(reason, null);
781 void Abort(string reason, Exception innerException)
783 // This is pretty loose check, but it is okay if we
784 // go down the abort path multiple times
785 if (this.state != WorkflowApplicationState.Aborted)
787 AbortInstance(new WorkflowApplicationAbortedException(reason, innerException), false);
791 void AbortPersistence()
793 if (this.persistenceManager != null)
795 this.persistenceManager.Abort();
798 PersistencePipeline currentPersistencePipeline = this.persistencePipelineInUse;
799 if (currentPersistencePipeline != null)
801 currentPersistencePipeline.Abort();
805 void AbortInstance(Exception reason, bool isWorkflowThread)
807 this.state = WorkflowApplicationState.Aborted;
809 // Need to ensure that either components see the Aborted state, this method sees the components, or both.
810 Thread.MemoryBarrier();
812 // We do this outside of the lock since persistence
813 // might currently be blocking access to the lock.
816 if (isWorkflowThread)
818 if (!this.hasCalledAbort)
820 this.hasCalledAbort = true;
821 this.Controller.Abort(reason);
823 // We should get off this thread because we're unsure of its state
824 ScheduleTrackAndRaiseAborted(reason);
829 bool completeSelf = true;
830 InstanceOperation operation = null;
834 operation = new InstanceOperation();
836 completeSelf = WaitForTurnAsync(operation, true, ActivityDefaults.AcquireLockTimeout, new Action<object, TimeoutException>(OnAbortWaitComplete), reason);
840 if (!this.hasCalledAbort)
842 this.hasCalledAbort = true;
843 this.Controller.Abort(reason);
845 // We need to get off this thread so we don't block the caller
847 ScheduleTrackAndRaiseAborted(reason);
855 NotifyOperationComplete(operation);
861 void OnAbortWaitComplete(object state, TimeoutException exception)
863 if (exception != null)
865 // We ---- this exception because we were simply doing our
866 // best to get the lock. Note that we won't proceed without
867 // the lock because we may have already succeeded on another
868 // thread. Technically this abort call has failed.
873 bool shouldRaise = false;
874 Exception reason = (Exception)state;
878 if (!this.hasCalledAbort)
881 this.hasCalledAbort = true;
882 this.Controller.Abort(reason);
887 ForceNotifyOperationComplete();
892 // We call this from this thread because we've already
893 // had a thread switch
894 TrackAndRaiseAborted(reason);
898 void ScheduleTrackAndRaiseAborted(Exception reason)
900 if (this.Controller.HasPendingTrackingRecords || this.Aborted != null)
902 ActionItem.Schedule(new Action<object>(TrackAndRaiseAborted), reason);
906 // This is only ever called from an appropriate thread (not the thread
907 // that called abort unless it was an internal abort).
908 // This method is called without the lock. We still provide single threaded
909 // guarantees to the Controller because:
910 // * No other call can ever enter the executor again once the state has
911 // switched to Aborted
912 // * If this was an internal abort then the thread was fast pathing its
913 // way out of the runtime and won't conflict
914 void TrackAndRaiseAborted(object state)
916 Exception reason = (Exception)state;
918 if (this.Controller.HasPendingTrackingRecords)
922 IAsyncResult result = this.Controller.BeginFlushTrackingRecords(ActivityDefaults.TrackingTimeout, Fx.ThunkCallback(new AsyncCallback(OnAbortTrackingComplete)), reason);
924 if (result.CompletedSynchronously)
926 this.Controller.EndFlushTrackingRecords(result);
940 // We ---- any exception here because we are on the abort path
941 // and are doing a best effort to track this record.
945 RaiseAborted(reason);
948 void OnAbortTrackingComplete(IAsyncResult result)
950 if (result.CompletedSynchronously)
955 Exception reason = (Exception)result.AsyncState;
959 this.Controller.EndFlushTrackingRecords(result);
968 // We ---- any exception here because we are on the abort path
969 // and are doing a best effort to track this record.
972 RaiseAborted(reason);
975 void RaiseAborted(Exception reason)
977 if (this.invokeCompletedCallback == null)
979 Action<WorkflowApplicationAbortedEventArgs> abortedHandler = this.Aborted;
981 if (abortedHandler != null)
985 this.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
986 this.isInHandler = true;
988 abortedHandler(new WorkflowApplicationAbortedEventArgs(this, reason));
992 this.isInHandler = false;
998 this.invokeCompletedCallback();
1001 if (TD.WorkflowInstanceAbortedIsEnabled())
1003 TD.WorkflowInstanceAborted(this.Id.ToString(), reason);
1007 public void Terminate(string reason)
1009 Terminate(reason, ActivityDefaults.AcquireLockTimeout);
1012 public void Terminate(Exception reason)
1014 Terminate(reason, ActivityDefaults.AcquireLockTimeout);
1017 public void Terminate(string reason, TimeSpan timeout)
1019 if (string.IsNullOrEmpty(reason))
1021 throw FxTrace.Exception.ArgumentNullOrEmpty("reason");
1024 Terminate(new WorkflowApplicationTerminatedException(reason, this.Id), timeout);
1027 public void Terminate(Exception reason, TimeSpan timeout)
1031 throw FxTrace.Exception.ArgumentNull("reason");
1034 ThrowIfHandlerThread();
1036 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1038 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1039 InstanceOperation operation = null;
1043 operation = new InstanceOperation();
1045 WaitForTurn(operation, timeoutHelper.RemainingTime());
1047 ValidateStateForTerminate();
1049 TerminateCore(reason);
1051 this.Controller.FlushTrackingRecords(timeoutHelper.RemainingTime());
1055 NotifyOperationComplete(operation);
1059 void TerminateCore(Exception reason)
1061 this.Controller.Terminate(reason);
1064 public IAsyncResult BeginTerminate(string reason, AsyncCallback callback, object state)
1066 return BeginTerminate(reason, ActivityDefaults.AcquireLockTimeout, callback, state);
1069 public IAsyncResult BeginTerminate(Exception reason, AsyncCallback callback, object state)
1071 return BeginTerminate(reason, ActivityDefaults.AcquireLockTimeout, callback, state);
1074 public IAsyncResult BeginTerminate(string reason, TimeSpan timeout, AsyncCallback callback, object state)
1076 if (string.IsNullOrEmpty(reason))
1078 throw FxTrace.Exception.ArgumentNullOrEmpty("reason");
1081 return BeginTerminate(new WorkflowApplicationTerminatedException(reason, this.Id), timeout, callback, state);
1084 public IAsyncResult BeginTerminate(Exception reason, TimeSpan timeout, AsyncCallback callback, object state)
1088 throw FxTrace.Exception.ArgumentNull("reason");
1091 ThrowIfHandlerThread();
1093 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1095 return TerminateAsyncResult.Create(this, reason, timeout, callback, state);
1098 public void EndTerminate(IAsyncResult result)
1100 TerminateAsyncResult.End(result);
1103 // called from the [....] and async paths
1106 // We only actually do any work if we haven't completed and we aren't
1108 if (!this.hasRaisedCompleted && this.state != WorkflowApplicationState.Unloaded)
1110 this.Controller.ScheduleCancel();
1112 // This is a loose check, but worst case scenario we call
1113 // an extra, unnecessary Run
1114 if (!this.hasCalledRun && !this.hasRaisedCompleted)
1121 public void Cancel()
1123 Cancel(ActivityDefaults.AcquireLockTimeout);
1126 public void Cancel(TimeSpan timeout)
1128 ThrowIfHandlerThread();
1130 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1132 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1134 InstanceOperation operation = null;
1138 operation = new InstanceOperation();
1140 WaitForTurn(operation, timeoutHelper.RemainingTime());
1142 ValidateStateForCancel();
1146 this.Controller.FlushTrackingRecords(timeoutHelper.RemainingTime());
1150 NotifyOperationComplete(operation);
1154 public IAsyncResult BeginCancel(AsyncCallback callback, object state)
1156 return BeginCancel(ActivityDefaults.AcquireLockTimeout, callback, state);
1159 public IAsyncResult BeginCancel(TimeSpan timeout, AsyncCallback callback, object state)
1161 ThrowIfHandlerThread();
1163 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1165 return CancelAsyncResult.Create(this, timeout, callback, state);
1168 public void EndCancel(IAsyncResult result)
1170 CancelAsyncResult.End(result);
1173 // called on the Invoke path, this will go away when WorkflowInvoker implements WorkflowInstance directly
1174 static WorkflowApplication CreateInstance(Activity activity, IDictionary<string, object> inputs, WorkflowInstanceExtensionManager extensions, SynchronizationContext syncContext, Action invokeCompletedCallback)
1176 // 1) Create the workflow instance
1177 Transaction ambientTransaction = Transaction.Current;
1178 List<Handle> workflowExecutionProperties = null;
1180 if (ambientTransaction != null)
1182 // no need for a NoPersistHandle since the ActivityExecutor performs a no-persist zone
1183 // as part of the RuntimeTransactionHandle processing
1184 workflowExecutionProperties = new List<Handle>(1)
1186 new RuntimeTransactionHandle(ambientTransaction)
1190 WorkflowApplication instance = new WorkflowApplication(activity, inputs, workflowExecutionProperties)
1192 SynchronizationContext = syncContext
1195 bool success = false;
1199 // 2) Take the executor lock before allowing extensions to be added
1200 instance.isBusy = true;
1202 // 3) Add extensions
1203 if (extensions != null)
1205 instance.extensions = extensions;
1208 // 4) Setup miscellaneous state
1209 instance.invokeCompletedCallback = invokeCompletedCallback;
1217 instance.isBusy = false;
1224 static void RunInstance(WorkflowApplication instance)
1226 // We still have the lock because we took it in Create
1228 // first make sure we're ready to run
1229 instance.EnsureInitialized();
1231 // Shortcut path for resuming the instance
1234 instance.hasExecutionOccurredSinceLastIdle = true;
1235 instance.Controller.Run();
1238 static WorkflowApplication StartInvoke(Activity activity, IDictionary<string, object> inputs, WorkflowInstanceExtensionManager extensions, SynchronizationContext syncContext, Action invokeCompletedCallback, AsyncInvokeContext invokeContext)
1240 WorkflowApplication instance = CreateInstance(activity, inputs, extensions, syncContext, invokeCompletedCallback);
1241 if (invokeContext != null)
1243 invokeContext.WorkflowApplication = instance;
1245 RunInstance(instance);
1249 internal static IDictionary<string, object> Invoke(Activity activity, IDictionary<string, object> inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
1251 Fx.Assert(activity != null, "Activity must not be null.");
1253 // Create the invoke synchronization context
1254 PumpBasedSynchronizationContext syncContext = new PumpBasedSynchronizationContext(timeout);
1255 WorkflowApplication instance = CreateInstance(activity, inputs, extensions, syncContext, new Action(syncContext.OnInvokeCompleted));
1256 // Wait for completion
1259 RunInstance(instance);
1260 syncContext.DoPump();
1263 catch (TimeoutException)
1265 instance.Abort(SR.AbortingDueToInstanceTimeout);
1269 Exception completionException = null;
1270 IDictionary<string, object> outputs = null;
1272 if (instance.Controller.State == WorkflowInstanceState.Aborted)
1274 completionException = new WorkflowApplicationAbortedException(SR.DefaultAbortReason, instance.Controller.GetAbortReason());
1278 Fx.Assert(instance.Controller.State == WorkflowInstanceState.Complete, "We should only get here when we are completed.");
1280 instance.Controller.GetCompletionState(out outputs, out completionException);
1283 if (completionException != null)
1285 throw FxTrace.Exception.AsError(completionException);
1291 internal static IAsyncResult BeginInvoke(Activity activity, IDictionary<string, object> inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout, SynchronizationContext syncContext, AsyncInvokeContext invokeContext, AsyncCallback callback, object state)
1293 Fx.Assert(activity != null, "The activity must not be null.");
1295 return new InvokeAsyncResult(activity, inputs, extensions, timeout, syncContext, invokeContext, callback, state);
1298 internal static IDictionary<string, object> EndInvoke(IAsyncResult result)
1300 return InvokeAsyncResult.End(result);
1305 Run(ActivityDefaults.AcquireLockTimeout);
1308 public void Run(TimeSpan timeout)
1310 InternalRun(timeout, true);
1313 void InternalRun(TimeSpan timeout, bool isUserRun)
1315 ThrowIfHandlerThread();
1317 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1319 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1320 InstanceOperation operation = null;
1324 operation = new InstanceOperation();
1326 WaitForTurn(operation, timeoutHelper.RemainingTime());
1328 ValidateStateForRun();
1332 // We set this to true here so that idle is raised
1333 // regardless of whether the call to Run resulted
1335 this.hasExecutionOccurredSinceLastIdle = true;
1340 this.Controller.FlushTrackingRecords(timeoutHelper.RemainingTime());
1344 NotifyOperationComplete(operation);
1350 if (!this.hasCalledRun)
1352 this.hasCalledRun = true;
1355 this.state = WorkflowApplicationState.Runnable;
1358 public IAsyncResult BeginRun(AsyncCallback callback, object state)
1360 return BeginRun(ActivityDefaults.AcquireLockTimeout, callback, state);
1363 public IAsyncResult BeginRun(TimeSpan timeout, AsyncCallback callback, object state)
1365 return BeginInternalRun(timeout, true, callback, state);
1368 IAsyncResult BeginInternalRun(TimeSpan timeout, bool isUserRun, AsyncCallback callback, object state)
1370 ThrowIfHandlerThread();
1372 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1374 return RunAsyncResult.Create(this, isUserRun, timeout, callback, state);
1377 public void EndRun(IAsyncResult result)
1379 RunAsyncResult.End(result);
1382 // shared by Load/BeginLoad
1383 bool IsLoadTransactionRequired()
1385 return base.GetExtensions<IPersistencePipelineModule>().Any(module => module.IsLoadTransactionRequired);
1388 void CreatePersistenceManager()
1390 PersistenceManager newManager = new PersistenceManager(this.InstanceStore, GetInstanceMetadata(), this.instanceId);
1391 SetPersistenceManager(newManager);
1394 // shared by Load(WorkflowApplicationInstance)/BeginLoad*
1395 void SetPersistenceManager(PersistenceManager newManager)
1397 Fx.Assert(this.persistenceManager == null, "SetPersistenceManager should only be called once");
1399 // first register our extensions since we'll need them to construct the pipeline
1400 base.RegisterExtensionManager(this.extensions);
1401 this.persistenceManager = newManager;
1404 // shared by Load/BeginLoad
1405 PersistencePipeline ProcessInstanceValues(IDictionary<XName, InstanceValue> values, out object deserializedRuntimeState)
1407 PersistencePipeline result = null;
1408 deserializedRuntimeState = ExtractRuntimeState(values, this.persistenceManager.InstanceId);
1410 if (HasPersistenceModule)
1412 IEnumerable<IPersistencePipelineModule> modules = base.GetExtensions<IPersistencePipelineModule>();
1413 result = new PersistencePipeline(modules);
1414 result.SetLoadedValues(values);
1420 static ActivityExecutor ExtractRuntimeState(IDictionary<XName, InstanceValue> values, Guid instanceId)
1422 InstanceValue value;
1423 if (values.TryGetValue(WorkflowNamespace.Workflow, out value))
1425 ActivityExecutor result = value.Value as ActivityExecutor;
1431 throw FxTrace.Exception.AsError(new InstancePersistenceException(SR.WorkflowInstanceNotFoundInStore(instanceId)));
1434 public static void CreateDefaultInstanceOwner(InstanceStore instanceStore, WorkflowIdentity definitionIdentity, WorkflowIdentityFilter identityFilter)
1436 CreateDefaultInstanceOwner(instanceStore, definitionIdentity, identityFilter, ActivityDefaults.OpenTimeout);
1439 public static void CreateDefaultInstanceOwner(InstanceStore instanceStore, WorkflowIdentity definitionIdentity, WorkflowIdentityFilter identityFilter, TimeSpan timeout)
1441 if (instanceStore == null)
1443 throw FxTrace.Exception.ArgumentNull("instanceStore");
1445 if (instanceStore.DefaultInstanceOwner != null)
1447 throw FxTrace.Exception.Argument("instanceStore", SR.InstanceStoreHasDefaultOwner);
1450 CreateWorkflowOwnerWithIdentityCommand command = GetCreateOwnerCommand(definitionIdentity, identityFilter);
1451 InstanceView commandResult = ExecuteInstanceCommandWithTemporaryHandle(instanceStore, command, timeout);
1452 instanceStore.DefaultInstanceOwner = commandResult.InstanceOwner;
1455 public static IAsyncResult BeginCreateDefaultInstanceOwner(InstanceStore instanceStore, WorkflowIdentity definitionIdentity,
1456 WorkflowIdentityFilter identityFilter, AsyncCallback callback, object state)
1458 return BeginCreateDefaultInstanceOwner(instanceStore, definitionIdentity, identityFilter, ActivityDefaults.OpenTimeout, callback, state);
1461 public static IAsyncResult BeginCreateDefaultInstanceOwner(InstanceStore instanceStore, WorkflowIdentity definitionIdentity,
1462 WorkflowIdentityFilter identityFilter, TimeSpan timeout, AsyncCallback callback, object state)
1464 if (instanceStore == null)
1466 throw FxTrace.Exception.ArgumentNull("instanceStore");
1468 if (instanceStore.DefaultInstanceOwner != null)
1470 throw FxTrace.Exception.Argument("instanceStore", SR.InstanceStoreHasDefaultOwner);
1473 CreateWorkflowOwnerWithIdentityCommand command = GetCreateOwnerCommand(definitionIdentity, identityFilter);
1474 return new InstanceCommandWithTemporaryHandleAsyncResult(instanceStore, command, timeout, callback, state);
1477 public static void EndCreateDefaultInstanceOwner(IAsyncResult asyncResult)
1479 InstanceStore instanceStore;
1480 InstanceView commandResult;
1481 InstanceCommandWithTemporaryHandleAsyncResult.End(asyncResult, out instanceStore, out commandResult);
1482 instanceStore.DefaultInstanceOwner = commandResult.InstanceOwner;
1485 public static void DeleteDefaultInstanceOwner(InstanceStore instanceStore)
1487 DeleteDefaultInstanceOwner(instanceStore, ActivityDefaults.CloseTimeout);
1490 public static void DeleteDefaultInstanceOwner(InstanceStore instanceStore, TimeSpan timeout)
1492 if (instanceStore == null)
1494 throw FxTrace.Exception.ArgumentNull("instanceStore");
1496 if (instanceStore.DefaultInstanceOwner == null)
1501 DeleteWorkflowOwnerCommand command = new DeleteWorkflowOwnerCommand();
1502 ExecuteInstanceCommandWithTemporaryHandle(instanceStore, command, timeout);
1503 instanceStore.DefaultInstanceOwner = null;
1506 public static IAsyncResult BeginDeleteDefaultInstanceOwner(InstanceStore instanceStore, AsyncCallback callback, object state)
1508 return BeginDeleteDefaultInstanceOwner(instanceStore, ActivityDefaults.CloseTimeout, callback, state);
1511 public static IAsyncResult BeginDeleteDefaultInstanceOwner(InstanceStore instanceStore, TimeSpan timeout, AsyncCallback callback, object state)
1513 if (instanceStore == null)
1515 throw FxTrace.Exception.ArgumentNull("instanceStore");
1517 if (instanceStore.DefaultInstanceOwner == null)
1519 return new CompletedAsyncResult(callback, state);
1522 DeleteWorkflowOwnerCommand command = new DeleteWorkflowOwnerCommand();
1523 return new InstanceCommandWithTemporaryHandleAsyncResult(instanceStore, command, timeout, callback, state);
1526 public static void EndDeleteDefaultInstanceOwner(IAsyncResult asyncResult)
1528 InstanceStore instanceStore;
1529 InstanceView commandResult;
1531 if (asyncResult is CompletedAsyncResult)
1533 CompletedAsyncResult.End(asyncResult);
1537 InstanceCommandWithTemporaryHandleAsyncResult.End(asyncResult, out instanceStore, out commandResult);
1538 instanceStore.DefaultInstanceOwner = null;
1541 static InstanceView ExecuteInstanceCommandWithTemporaryHandle(InstanceStore instanceStore, InstancePersistenceCommand command, TimeSpan timeout)
1543 InstanceHandle temporaryHandle = null;
1546 temporaryHandle = instanceStore.CreateInstanceHandle();
1547 return instanceStore.Execute(temporaryHandle, command, timeout);
1551 if (temporaryHandle != null)
1553 temporaryHandle.Free();
1558 static CreateWorkflowOwnerWithIdentityCommand GetCreateOwnerCommand(WorkflowIdentity definitionIdentity, WorkflowIdentityFilter identityFilter)
1560 if (!identityFilter.IsValid())
1562 throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("identityFilter"));
1564 if (definitionIdentity == null && identityFilter != WorkflowIdentityFilter.Any)
1566 // This API isn't useful for null identity, because WFApp only adds a default WorkflowHostType
1567 // to instances with non-null identity.
1568 throw FxTrace.Exception.Argument("definitionIdentity", SR.CannotCreateOwnerWithoutIdentity);
1570 return new CreateWorkflowOwnerWithIdentityCommand
1572 InstanceOwnerMetadata =
1574 { WorkflowNamespace.WorkflowHostType, new InstanceValue(Workflow45Namespace.WorkflowApplication) },
1575 { Workflow45Namespace.DefinitionIdentities, new InstanceValue(new Collection<WorkflowIdentity> { definitionIdentity }) },
1576 { Workflow45Namespace.DefinitionIdentityFilter, new InstanceValue(identityFilter) },
1581 public static WorkflowApplicationInstance GetRunnableInstance(InstanceStore instanceStore)
1583 return GetRunnableInstance(instanceStore, ActivityDefaults.LoadTimeout);
1586 public static WorkflowApplicationInstance GetRunnableInstance(InstanceStore instanceStore, TimeSpan timeout)
1588 if (instanceStore == null)
1590 throw FxTrace.Exception.ArgumentNull("instanceStore");
1592 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1593 if (instanceStore.DefaultInstanceOwner == null)
1595 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetRunnableRequiresOwner));
1598 PersistenceManager newManager = new PersistenceManager(instanceStore, null);
1599 return LoadCore(timeout, true, newManager);
1602 public static IAsyncResult BeginGetRunnableInstance(InstanceStore instanceStore, AsyncCallback callback, object state)
1604 return BeginGetRunnableInstance(instanceStore, ActivityDefaults.LoadTimeout, callback, state);
1607 public static IAsyncResult BeginGetRunnableInstance(InstanceStore instanceStore, TimeSpan timeout, AsyncCallback callback, object state)
1609 if (instanceStore == null)
1611 throw FxTrace.Exception.ArgumentNull("instanceStore");
1613 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1614 if (instanceStore.DefaultInstanceOwner == null)
1616 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.GetRunnableRequiresOwner));
1619 PersistenceManager newManager = new PersistenceManager(instanceStore, null);
1620 return new LoadAsyncResult(null, newManager, true, timeout, callback, state);
1623 public static WorkflowApplicationInstance EndGetRunnableInstance(IAsyncResult asyncResult)
1625 return LoadAsyncResult.EndAndCreateInstance(asyncResult);
1628 public static WorkflowApplicationInstance GetInstance(Guid instanceId, InstanceStore instanceStore)
1630 return GetInstance(instanceId, instanceStore, ActivityDefaults.LoadTimeout);
1633 public static WorkflowApplicationInstance GetInstance(Guid instanceId, InstanceStore instanceStore, TimeSpan timeout)
1635 if (instanceId == Guid.Empty)
1637 throw FxTrace.Exception.ArgumentNullOrEmpty("instanceId");
1639 if (instanceStore == null)
1641 throw FxTrace.Exception.ArgumentNull("instanceStore");
1643 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1645 PersistenceManager newManager = new PersistenceManager(instanceStore, null, instanceId);
1646 return LoadCore(timeout, false, newManager);
1649 public static IAsyncResult BeginGetInstance(Guid instanceId, InstanceStore instanceStore, AsyncCallback callback, object state)
1651 return BeginGetInstance(instanceId, instanceStore, ActivityDefaults.LoadTimeout, callback, state);
1654 public static IAsyncResult BeginGetInstance(Guid instanceId, InstanceStore instanceStore, TimeSpan timeout, AsyncCallback callback, object state)
1656 if (instanceId == Guid.Empty)
1658 throw FxTrace.Exception.ArgumentNullOrEmpty("instanceId");
1660 if (instanceStore == null)
1662 throw FxTrace.Exception.ArgumentNull("instanceStore");
1664 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1666 PersistenceManager newManager = new PersistenceManager(instanceStore, null, instanceId);
1667 return new LoadAsyncResult(null, newManager, false, timeout, callback, state);
1670 public static WorkflowApplicationInstance EndGetInstance(IAsyncResult asyncResult)
1672 return LoadAsyncResult.EndAndCreateInstance(asyncResult);
1675 public void Load(WorkflowApplicationInstance instance)
1677 Load(instance, ActivityDefaults.LoadTimeout);
1680 public void Load(WorkflowApplicationInstance instance, TimeSpan timeout)
1682 Load(instance, null, timeout);
1685 public void Load(WorkflowApplicationInstance instance, DynamicUpdateMap updateMap)
1687 Load(instance, updateMap, ActivityDefaults.LoadTimeout);
1690 public void Load(WorkflowApplicationInstance instance, DynamicUpdateMap updateMap, TimeSpan timeout)
1693 ThrowIfReadOnly(); // only allow a single Load() or Run()
1694 if (instance == null)
1696 throw FxTrace.Exception.ArgumentNull("instance");
1699 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1701 if (this.instanceIdSet)
1703 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationAlreadyHasId));
1705 if (this.initialWorkflowArguments != null)
1707 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotUseInputsWithLoad));
1709 if (this.InstanceStore != null && this.InstanceStore != instance.InstanceStore)
1711 throw FxTrace.Exception.Argument("instance", SR.InstanceStoreDoesntMatchWorkflowApplication);
1714 instance.MarkAsLoaded();
1716 InstanceOperation operation = new InstanceOperation { RequiresInitialized = false };
1720 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1721 WaitForTurn(operation, timeoutHelper.RemainingTime());
1723 ValidateStateForLoad();
1725 this.instanceId = instance.InstanceId;
1726 this.instanceIdSet = true;
1727 if (this.instanceStore == null)
1729 this.instanceStore = instance.InstanceStore;
1732 PersistenceManager newManager = (PersistenceManager)instance.PersistenceManager;
1733 newManager.SetInstanceMetadata(GetInstanceMetadata());
1734 SetPersistenceManager(newManager);
1736 LoadCore(updateMap, timeoutHelper, false, instance.Values);
1740 NotifyOperationComplete(operation);
1744 public void LoadRunnableInstance()
1746 LoadRunnableInstance(ActivityDefaults.LoadTimeout);
1749 public void LoadRunnableInstance(TimeSpan timeout)
1751 ThrowIfReadOnly(); // only allow a single Load() or Run()
1753 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1755 if (this.InstanceStore == null)
1757 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.LoadingWorkflowApplicationRequiresInstanceStore));
1759 if (this.instanceIdSet)
1761 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationAlreadyHasId));
1763 if (this.initialWorkflowArguments != null)
1765 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotUseInputsWithLoad));
1767 if (this.persistenceManager != null)
1769 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.TryLoadRequiresOwner));
1772 InstanceOperation operation = new InstanceOperation { RequiresInitialized = false };
1776 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1777 WaitForTurn(operation, timeoutHelper.RemainingTime());
1779 ValidateStateForLoad();
1781 RegisterExtensionManager(this.extensions);
1782 this.persistenceManager = new PersistenceManager(InstanceStore, GetInstanceMetadata());
1784 if (!this.persistenceManager.IsInitialized)
1786 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.TryLoadRequiresOwner));
1789 LoadCore(null, timeoutHelper, true);
1793 NotifyOperationComplete(operation);
1797 public void Load(Guid instanceId)
1799 Load(instanceId, ActivityDefaults.LoadTimeout);
1802 public void Load(Guid instanceId, TimeSpan timeout)
1805 ThrowIfReadOnly(); // only allow a single Load() or Run()
1806 if (instanceId == Guid.Empty)
1808 throw FxTrace.Exception.ArgumentNullOrEmpty("instanceId");
1811 TimeoutHelper.ThrowIfNegativeArgument(timeout);
1813 if (this.InstanceStore == null)
1815 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.LoadingWorkflowApplicationRequiresInstanceStore));
1817 if (this.instanceIdSet)
1819 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationAlreadyHasId));
1821 if (this.initialWorkflowArguments != null)
1823 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotUseInputsWithLoad));
1826 InstanceOperation operation = new InstanceOperation { RequiresInitialized = false };
1830 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1831 WaitForTurn(operation, timeoutHelper.RemainingTime());
1833 ValidateStateForLoad();
1835 this.instanceId = instanceId;
1836 this.instanceIdSet = true;
1838 CreatePersistenceManager();
1839 LoadCore(null, timeoutHelper, false);
1843 NotifyOperationComplete(operation);
1847 void LoadCore(DynamicUpdateMap updateMap, TimeoutHelper timeoutHelper, bool loadAny, IDictionary<XName, InstanceValue> values = null)
1851 if (!this.persistenceManager.IsInitialized)
1853 this.persistenceManager.Initialize(this.DefinitionIdentity, timeoutHelper.RemainingTime());
1858 Fx.Assert(this.persistenceManager.IsInitialized, "Caller should have initialized Persistence Manager");
1859 Fx.Assert(this.instanceIdSet, "Caller should have set InstanceId");
1862 PersistencePipeline pipeline = null;
1863 WorkflowPersistenceContext context = null;
1864 TransactionScope scope = null;
1865 bool success = false;
1866 Exception abortReasonInnerException = null;
1869 InitializePersistenceContext(IsLoadTransactionRequired(), timeoutHelper, out context, out scope);
1873 values = LoadValues(this.persistenceManager, timeoutHelper, loadAny);
1876 if (this.instanceIdSet)
1878 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationAlreadyHasId));
1881 this.instanceId = this.persistenceManager.InstanceId;
1882 this.instanceIdSet = true;
1885 object deserializedRuntimeState;
1886 pipeline = ProcessInstanceValues(values, out deserializedRuntimeState);
1888 if (pipeline != null)
1892 this.persistencePipelineInUse = pipeline;
1894 // Need to ensure that either we see the Aborted state, AbortInstance sees us, or both.
1895 Thread.MemoryBarrier();
1897 if (this.state == WorkflowApplicationState.Aborted)
1899 throw FxTrace.Exception.AsError(new OperationCanceledException(SR.DefaultAbortReason));
1902 pipeline.EndLoad(pipeline.BeginLoad(timeoutHelper.RemainingTime(), null, null));
1906 this.persistencePipelineInUse = null;
1912 base.Initialize(deserializedRuntimeState, updateMap);
1913 if (updateMap != null)
1915 UpdateInstanceMetadata();
1918 catch (InstanceUpdateException e)
1920 abortReasonInnerException = e;
1923 catch (VersionMismatchException e)
1925 abortReasonInnerException = e;
1933 CompletePersistenceContext(context, scope, success);
1936 this.AbortDueToException(abortReasonInnerException);
1940 if (pipeline != null)
1946 private void AbortDueToException(Exception e)
1948 if (e is InstanceUpdateException)
1950 this.Abort(SR.AbortingDueToDynamicUpdateFailure, e);
1952 else if (e is VersionMismatchException)
1954 this.Abort(SR.AbortingDueToVersionMismatch, e);
1958 this.Abort(SR.AbortingDueToLoadFailure);
1962 static WorkflowApplicationInstance LoadCore(TimeSpan timeout, bool loadAny, PersistenceManager persistenceManager)
1964 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1966 if (!persistenceManager.IsInitialized)
1968 persistenceManager.Initialize(unknownIdentity, timeoutHelper.RemainingTime());
1971 WorkflowPersistenceContext context = null;
1972 TransactionScope scope = null;
1973 WorkflowApplicationInstance result = null;
1976 InitializePersistenceContext(false, timeoutHelper, out context, out scope);
1978 IDictionary<XName, InstanceValue> values = LoadValues(persistenceManager, timeoutHelper, loadAny);
1979 ActivityExecutor deserializedRuntimeState = ExtractRuntimeState(values, persistenceManager.InstanceId);
1980 result = new WorkflowApplicationInstance(persistenceManager, values, deserializedRuntimeState.WorkflowIdentity);
1984 bool success = (result != null);
1985 CompletePersistenceContext(context, scope, success);
1988 persistenceManager.Abort();
1995 static void InitializePersistenceContext(bool isTransactionRequired, TimeoutHelper timeoutHelper,
1996 out WorkflowPersistenceContext context, out TransactionScope scope)
1998 context = new WorkflowPersistenceContext(isTransactionRequired, timeoutHelper.OriginalTimeout);
1999 scope = TransactionHelper.CreateTransactionScope(context.PublicTransaction);
2002 static void CompletePersistenceContext(WorkflowPersistenceContext context, TransactionScope scope, bool success)
2004 // Clean up the transaction scope regardless of failure
2005 TransactionHelper.CompleteTransactionScope(ref scope);
2007 if (context != null)
2020 static IDictionary<XName, InstanceValue> LoadValues(PersistenceManager persistenceManager, TimeoutHelper timeoutHelper, bool loadAny)
2022 IDictionary<XName, InstanceValue> values;
2025 if (!persistenceManager.TryLoad(timeoutHelper.RemainingTime(), out values))
2027 throw FxTrace.Exception.AsError(new InstanceNotReadyException(SR.NoRunnableInstances));
2032 values = persistenceManager.Load(timeoutHelper.RemainingTime());
2038 internal static void DiscardInstance(PersistenceManagerBase persistanceManager, TimeSpan timeout)
2040 PersistenceManager manager = (PersistenceManager)persistanceManager;
2041 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
2042 UnlockInstance(manager, timeoutHelper);
2045 internal static IAsyncResult BeginDiscardInstance(PersistenceManagerBase persistanceManager, TimeSpan timeout, AsyncCallback callback, object state)
2047 PersistenceManager manager = (PersistenceManager)persistanceManager;
2048 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
2049 return new UnlockInstanceAsyncResult(manager, timeoutHelper, callback, state);
2052 internal static void EndDiscardInstance(IAsyncResult asyncResult)
2054 UnlockInstanceAsyncResult.End(asyncResult);
2057 static void UnlockInstance(PersistenceManager persistenceManager, TimeoutHelper timeoutHelper)
2061 if (persistenceManager.OwnerWasCreated)
2063 // if the owner was created by this WorkflowApplication, delete it.
2064 // This implicitly unlocks the instance.
2065 persistenceManager.DeleteOwner(timeoutHelper.RemainingTime());
2069 persistenceManager.Unlock(timeoutHelper.RemainingTime());
2074 persistenceManager.Abort();
2078 internal static IList<ActivityBlockingUpdate> GetActivitiesBlockingUpdate(WorkflowApplicationInstance instance, DynamicUpdateMap updateMap)
2080 object deserializedRuntimeState = ExtractRuntimeState(instance.Values, instance.InstanceId);
2081 return WorkflowInstance.GetActivitiesBlockingUpdate(deserializedRuntimeState, updateMap);
2084 public IAsyncResult BeginLoadRunnableInstance(AsyncCallback callback, object state)
2086 return BeginLoadRunnableInstance(ActivityDefaults.LoadTimeout, callback, state);
2089 public IAsyncResult BeginLoadRunnableInstance(TimeSpan timeout, AsyncCallback callback, object state)
2091 ThrowIfReadOnly(); // only allow a single Load() or Run()
2093 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2095 if (this.InstanceStore == null)
2097 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.LoadingWorkflowApplicationRequiresInstanceStore));
2099 if (this.instanceIdSet)
2101 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationAlreadyHasId));
2103 if (this.initialWorkflowArguments != null)
2105 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotUseInputsWithLoad));
2107 if (this.persistenceManager != null)
2109 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.TryLoadRequiresOwner));
2112 PersistenceManager newManager = new PersistenceManager(InstanceStore, GetInstanceMetadata());
2113 if (!newManager.IsInitialized)
2115 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.TryLoadRequiresOwner));
2118 return new LoadAsyncResult(this, newManager, true, timeout, callback, state);
2121 public IAsyncResult BeginLoad(Guid instanceId, AsyncCallback callback, object state)
2123 return BeginLoad(instanceId, ActivityDefaults.LoadTimeout, callback, state);
2126 public IAsyncResult BeginLoad(Guid instanceId, TimeSpan timeout, AsyncCallback callback, object state)
2129 ThrowIfReadOnly(); // only allow a single Load() or Run()
2130 if (instanceId == Guid.Empty)
2132 throw FxTrace.Exception.ArgumentNullOrEmpty("instanceId");
2135 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2137 if (this.InstanceStore == null)
2139 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.LoadingWorkflowApplicationRequiresInstanceStore));
2141 if (this.instanceIdSet)
2143 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationAlreadyHasId));
2145 if (this.initialWorkflowArguments != null)
2147 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotUseInputsWithLoad));
2150 PersistenceManager newManager = new PersistenceManager(this.InstanceStore, GetInstanceMetadata(), instanceId);
2152 return new LoadAsyncResult(this, newManager, false, timeout, callback, state);
2155 public IAsyncResult BeginLoad(WorkflowApplicationInstance instance, AsyncCallback callback, object state)
2157 return BeginLoad(instance, null, ActivityDefaults.LoadTimeout, callback, state);
2160 public IAsyncResult BeginLoad(WorkflowApplicationInstance instance, TimeSpan timeout,
2161 AsyncCallback callback, object state)
2163 return BeginLoad(instance, null, timeout, callback, state);
2166 public IAsyncResult BeginLoad(WorkflowApplicationInstance instance, DynamicUpdateMap updateMap,
2167 AsyncCallback callback, object state)
2169 return BeginLoad(instance, updateMap, ActivityDefaults.LoadTimeout, callback, state);
2172 public IAsyncResult BeginLoad(WorkflowApplicationInstance instance, DynamicUpdateMap updateMap, TimeSpan timeout,
2173 AsyncCallback callback, object state)
2176 ThrowIfReadOnly(); // only allow a single Load() or Run()
2177 if (instance == null)
2179 throw FxTrace.Exception.ArgumentNull("instance");
2182 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2184 if (this.instanceIdSet)
2186 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationAlreadyHasId));
2188 if (this.initialWorkflowArguments != null)
2190 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotUseInputsWithLoad));
2192 if (this.InstanceStore != null && this.InstanceStore != instance.InstanceStore)
2194 throw FxTrace.Exception.Argument("instance", SR.InstanceStoreDoesntMatchWorkflowApplication);
2197 instance.MarkAsLoaded();
2198 PersistenceManager newManager = (PersistenceManager)instance.PersistenceManager;
2199 newManager.SetInstanceMetadata(GetInstanceMetadata());
2201 return new LoadAsyncResult(this, newManager, instance.Values, updateMap, timeout, callback, state);
2204 public void EndLoad(IAsyncResult result)
2206 LoadAsyncResult.End(result);
2209 public void EndLoadRunnableInstance(IAsyncResult result)
2211 LoadAsyncResult.End(result);
2214 protected override void OnNotifyUnhandledException(Exception exception, Activity exceptionSource, string exceptionSourceInstanceId)
2220 Exception abortException = null;
2224 if (unhandledExceptionHandler == null)
2226 unhandledExceptionHandler = new UnhandledExceptionEventHandler();
2229 done = unhandledExceptionHandler.Run(this, exception, exceptionSource, exceptionSourceInstanceId);
2241 if (abortException != null)
2243 AbortInstance(abortException, true);
2255 IAsyncResult BeginInternalPersist(PersistenceOperation operation, TimeSpan timeout, bool isInternalPersist, AsyncCallback callback, object state)
2257 return new UnloadOrPersistAsyncResult(this, timeout, operation, true, isInternalPersist, callback, state);
2260 void EndInternalPersist(IAsyncResult result)
2262 UnloadOrPersistAsyncResult.End(result);
2265 void TrackPersistence(PersistenceOperation operation)
2267 if (this.Controller.TrackingEnabled)
2269 if (operation == PersistenceOperation.Complete)
2271 this.Controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Deleted, this.DefinitionIdentity));
2273 else if (operation == PersistenceOperation.Unload)
2275 this.Controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Unloaded, this.DefinitionIdentity));
2279 this.Controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Persisted, this.DefinitionIdentity));
2284 void PersistCore(ref TimeoutHelper timeoutHelper, PersistenceOperation operation)
2286 if (HasPersistenceProvider)
2288 if (!this.persistenceManager.IsInitialized)
2290 this.persistenceManager.Initialize(this.DefinitionIdentity, timeoutHelper.RemainingTime());
2292 if (!this.persistenceManager.IsLocked && Transaction.Current != null)
2294 this.persistenceManager.EnsureReadyness(timeoutHelper.RemainingTime());
2297 // Do the tracking before preparing in case the tracking data is being pushed into
2298 // an extension and persisted transactionally with the instance state.
2299 TrackPersistence(operation);
2301 this.Controller.FlushTrackingRecords(timeoutHelper.RemainingTime());
2304 bool success = false;
2305 WorkflowPersistenceContext context = null;
2306 TransactionScope scope = null;
2310 IDictionary<XName, InstanceValue> data = null;
2311 PersistencePipeline pipeline = null;
2312 if (HasPersistenceModule)
2314 IEnumerable<IPersistencePipelineModule> modules = base.GetExtensions<IPersistencePipelineModule>();
2315 pipeline = new PersistencePipeline(modules, PersistenceManager.GenerateInitialData(this));
2318 data = pipeline.Values;
2321 if (HasPersistenceProvider)
2325 data = PersistenceManager.GenerateInitialData(this);
2328 if (context == null)
2330 Fx.Assert(scope == null, "Should not have been able to set up a scope.");
2331 InitializePersistenceContext(pipeline != null && pipeline.IsSaveTransactionRequired, timeoutHelper, out context, out scope);
2334 this.persistenceManager.Save(data, operation, timeoutHelper.RemainingTime());
2337 if (pipeline != null)
2339 if (context == null)
2341 Fx.Assert(scope == null, "Should not have been able to set up a scope if we had no context.");
2342 InitializePersistenceContext(pipeline.IsSaveTransactionRequired, timeoutHelper, out context, out scope);
2347 this.persistencePipelineInUse = pipeline;
2349 // Need to ensure that either we see the Aborted state, AbortInstance sees us, or both.
2350 Thread.MemoryBarrier();
2352 if (this.state == WorkflowApplicationState.Aborted)
2354 throw FxTrace.Exception.AsError(new OperationCanceledException(SR.DefaultAbortReason));
2357 pipeline.EndSave(pipeline.BeginSave(timeoutHelper.RemainingTime(), null, null));
2361 this.persistencePipelineInUse = null;
2369 CompletePersistenceContext(context, scope, success);
2373 if (operation != PersistenceOperation.Save)
2375 // Stop execution if we've given up the instance lock
2376 this.state = WorkflowApplicationState.Paused;
2378 if (TD.WorkflowApplicationUnloadedIsEnabled())
2380 TD.WorkflowApplicationUnloaded(this.Id.ToString());
2385 if (TD.WorkflowApplicationPersistedIsEnabled())
2387 TD.WorkflowApplicationPersisted(this.Id.ToString());
2391 if (operation == PersistenceOperation.Complete || operation == PersistenceOperation.Unload)
2393 // We did a Delete or Unload, so if we have a persistence provider, tell it to delete the owner.
2394 if (HasPersistenceProvider && this.persistenceManager.OwnerWasCreated)
2396 // This will happen to be under the caller's transaction, if there is one.
2398 this.persistenceManager.DeleteOwner(timeoutHelper.RemainingTime());
2407 [Fx.Tag.InheritThrows(From = "Unload")]
2408 public void Persist()
2410 Persist(ActivityDefaults.SaveTimeout);
2413 [Fx.Tag.InheritThrows(From = "Unload")]
2414 public void Persist(TimeSpan timeout)
2416 ThrowIfHandlerThread();
2418 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2420 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
2422 RequiresPersistenceOperation operation = new RequiresPersistenceOperation();
2426 WaitForTurn(operation, timeoutHelper.RemainingTime());
2428 ValidateStateForPersist();
2430 PersistCore(ref timeoutHelper, PersistenceOperation.Save);
2434 NotifyOperationComplete(operation);
2438 [Fx.Tag.InheritThrows(From = "Unload")]
2439 public IAsyncResult BeginPersist(AsyncCallback callback, object state)
2441 return BeginPersist(ActivityDefaults.SaveTimeout, callback, state);
2444 [Fx.Tag.InheritThrows(From = "Unload")]
2445 public IAsyncResult BeginPersist(TimeSpan timeout, AsyncCallback callback, object state)
2447 ThrowIfHandlerThread();
2449 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2451 return new UnloadOrPersistAsyncResult(this, timeout, PersistenceOperation.Save, false, false, callback, state);
2454 [Fx.Tag.InheritThrows(From = "Unload")]
2455 public void EndPersist(IAsyncResult result)
2457 UnloadOrPersistAsyncResult.End(result);
2460 // called from WorkflowApplicationIdleEventArgs
2461 internal ReadOnlyCollection<BookmarkInfo> GetBookmarksForIdle()
2463 return this.Controller.GetBookmarks();
2466 public ReadOnlyCollection<BookmarkInfo> GetBookmarks()
2468 return GetBookmarks(ActivityDefaults.ResumeBookmarkTimeout);
2471 public ReadOnlyCollection<BookmarkInfo> GetBookmarks(TimeSpan timeout)
2473 ThrowIfHandlerThread();
2475 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2477 InstanceOperation operation = new InstanceOperation();
2481 WaitForTurn(operation, timeout);
2483 ValidateStateForGetAllBookmarks();
2485 return this.Controller.GetBookmarks();
2489 NotifyOperationComplete(operation);
2493 protected internal override IAsyncResult OnBeginPersist(AsyncCallback callback, object state)
2495 return this.BeginInternalPersist(PersistenceOperation.Save, ActivityDefaults.InternalSaveTimeout, true, callback, state);
2498 protected internal override void OnEndPersist(IAsyncResult result)
2500 this.EndInternalPersist(result);
2503 protected internal override IAsyncResult OnBeginAssociateKeys(ICollection<InstanceKey> keys, AsyncCallback callback, object state)
2505 throw Fx.AssertAndThrow("WorkflowApplication is sealed with CanUseKeys as false, so WorkflowInstance should not call OnBeginAssociateKeys.");
2508 protected internal override void OnEndAssociateKeys(IAsyncResult result)
2510 throw Fx.AssertAndThrow("WorkflowApplication is sealed with CanUseKeys as false, so WorkflowInstance should not call OnEndAssociateKeys.");
2513 protected internal override void OnDisassociateKeys(ICollection<InstanceKey> keys)
2515 throw Fx.AssertAndThrow("WorkflowApplication is sealed with CanUseKeys as false, so WorkflowInstance should not call OnDisassociateKeys.");
2518 bool AreBookmarksInvalid(out BookmarkResumptionResult result)
2520 if (this.hasRaisedCompleted)
2522 result = BookmarkResumptionResult.NotFound;
2525 else if (this.state == WorkflowApplicationState.Unloaded || this.state == WorkflowApplicationState.Aborted)
2527 result = BookmarkResumptionResult.NotReady;
2531 result = BookmarkResumptionResult.Success;
2535 [Fx.Tag.InheritThrows(From = "ResumeBookmark")]
2536 public BookmarkResumptionResult ResumeBookmark(string bookmarkName, object value)
2538 if (string.IsNullOrEmpty(bookmarkName))
2540 throw FxTrace.Exception.ArgumentNullOrEmpty("bookmarkName");
2543 return ResumeBookmark(new Bookmark(bookmarkName), value);
2546 [Fx.Tag.InheritThrows(From = "ResumeBookmark")]
2547 public BookmarkResumptionResult ResumeBookmark(Bookmark bookmark, object value)
2549 return ResumeBookmark(bookmark, value, ActivityDefaults.ResumeBookmarkTimeout);
2552 [Fx.Tag.InheritThrows(From = "ResumeBookmark")]
2553 public BookmarkResumptionResult ResumeBookmark(string bookmarkName, object value, TimeSpan timeout)
2555 if (string.IsNullOrEmpty(bookmarkName))
2557 throw FxTrace.Exception.ArgumentNullOrEmpty("bookmarkName");
2560 return ResumeBookmark(new Bookmark(bookmarkName), value, timeout);
2563 [Fx.Tag.InheritThrows(From = "BeginResumeBookmark", FromDeclaringType = typeof(WorkflowInstance))]
2564 public BookmarkResumptionResult ResumeBookmark(Bookmark bookmark, object value, TimeSpan timeout)
2566 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2567 ThrowIfHandlerThread();
2568 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
2570 InstanceOperation operation = new RequiresIdleOperation();
2571 BookmarkResumptionResult result;
2572 bool pendedUnenqueued = false;
2576 // This is a loose check, but worst case scenario we call
2577 // an extra, unnecessary Run
2578 if (!this.hasCalledRun)
2580 // Increment the pending unenqueued count so we don't raise idle in the time between
2581 // when the Run completes and when we enqueue our InstanceOperation.
2582 pendedUnenqueued = true;
2583 IncrementPendingUnenqueud();
2585 InternalRun(timeoutHelper.RemainingTime(), false);
2590 InstanceOperation nextOperation = null;
2594 // Need to enqueue and wait for turn as two separate steps, so that
2595 // OnQueued always gets called and we make sure to decrement the pendingUnenqueued counter
2596 WaitForTurn(operation, timeoutHelper.RemainingTime());
2598 if (pendedUnenqueued)
2600 DecrementPendingUnenqueud();
2601 pendedUnenqueued = false;
2604 if (AreBookmarksInvalid(out result))
2609 result = ResumeBookmarkCore(bookmark, value);
2611 if (result == BookmarkResumptionResult.Success)
2613 this.Controller.FlushTrackingRecords(timeoutHelper.RemainingTime());
2615 else if (result == BookmarkResumptionResult.NotReady)
2617 nextOperation = new DeferredRequiresIdleOperation();
2622 NotifyOperationComplete(operation);
2625 operation = nextOperation;
2626 } while (operation != null);
2632 if (pendedUnenqueued)
2634 DecrementPendingUnenqueud();
2639 [Fx.Tag.InheritThrows(From = "ResumeBookmark")]
2640 public IAsyncResult BeginResumeBookmark(string bookmarkName, object value, AsyncCallback callback, object state)
2642 if (string.IsNullOrEmpty(bookmarkName))
2644 throw FxTrace.Exception.ArgumentNullOrEmpty("bookmarkName");
2647 return BeginResumeBookmark(new Bookmark(bookmarkName), value, callback, state);
2650 [Fx.Tag.InheritThrows(From = "ResumeBookmark")]
2651 public IAsyncResult BeginResumeBookmark(string bookmarkName, object value, TimeSpan timeout, AsyncCallback callback, object state)
2653 if (string.IsNullOrEmpty(bookmarkName))
2655 throw FxTrace.Exception.ArgumentNullOrEmpty("bookmarkName");
2658 return BeginResumeBookmark(new Bookmark(bookmarkName), value, timeout, callback, state);
2661 [Fx.Tag.InheritThrows(From = "ResumeBookmark")]
2662 public IAsyncResult BeginResumeBookmark(Bookmark bookmark, object value, AsyncCallback callback, object state)
2664 return BeginResumeBookmark(bookmark, value, ActivityDefaults.ResumeBookmarkTimeout, callback, state);
2667 [Fx.Tag.InheritThrows(From = "ResumeBookmark")]
2668 public IAsyncResult BeginResumeBookmark(Bookmark bookmark, object value, TimeSpan timeout, AsyncCallback callback, object state)
2670 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2671 ThrowIfHandlerThread();
2673 return new ResumeBookmarkAsyncResult(this, bookmark, value, timeout, callback, state);
2676 [Fx.Tag.InheritThrows(From = "ResumeBookmark")]
2677 public BookmarkResumptionResult EndResumeBookmark(IAsyncResult result)
2679 return ResumeBookmarkAsyncResult.End(result);
2682 protected internal override IAsyncResult OnBeginResumeBookmark(Bookmark bookmark, object value, TimeSpan timeout, AsyncCallback callback, object state)
2684 ThrowIfHandlerThread();
2685 return new ResumeBookmarkAsyncResult(this, bookmark, value, true, timeout, callback, state);
2688 protected internal override BookmarkResumptionResult OnEndResumeBookmark(IAsyncResult result)
2690 return ResumeBookmarkAsyncResult.End(result);
2693 BookmarkResumptionResult ResumeBookmarkCore(Bookmark bookmark, object value)
2695 BookmarkResumptionResult result = this.Controller.ScheduleBookmarkResumption(bookmark, value);
2697 if (result == BookmarkResumptionResult.Success)
2705 // Returns true if successful, false otherwise
2706 bool RaiseIdleEvent()
2708 if (TD.WorkflowApplicationIdledIsEnabled())
2710 TD.WorkflowApplicationIdled(this.Id.ToString());
2713 Exception abortException = null;
2717 Action<WorkflowApplicationIdleEventArgs> idleHandler = this.Idle;
2719 if (idleHandler != null)
2721 this.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
2722 this.isInHandler = true;
2724 idleHandler(new WorkflowApplicationIdleEventArgs(this));
2738 this.isInHandler = false;
2741 if (abortException != null)
2743 AbortInstance(abortException, true);
2752 this.state = WorkflowApplicationState.Unloaded;
2754 // don't abort completed instances
2755 if (this.Controller.State != WorkflowInstanceState.Complete)
2757 this.Controller.Abort();
2761 base.DisposeExtensions();
2764 Exception abortException = null;
2768 Action<WorkflowApplicationEventArgs> handler = this.Unloaded;
2770 if (handler != null)
2772 this.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
2773 this.isInHandler = true;
2775 handler(new WorkflowApplicationEventArgs(this));
2789 this.isInHandler = false;
2792 if (abortException != null)
2794 AbortInstance(abortException, true);
2798 [Fx.Tag.Throws(typeof(WorkflowApplicationException), "The WorkflowApplication is in a state for which unloading is not valid. The specific subclass denotes which state the instance is in.")]
2799 [Fx.Tag.Throws(typeof(InstancePersistenceException), "Something went wrong during persistence, but persistence can be retried.")]
2800 [Fx.Tag.Throws(typeof(TimeoutException), "The workflow could not be unloaded within the given timeout.")]
2801 public void Unload()
2803 Unload(ActivityDefaults.SaveTimeout);
2806 [Fx.Tag.InheritThrows(From = "Unload")]
2807 public void Unload(TimeSpan timeout)
2809 ThrowIfHandlerThread();
2811 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2813 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
2815 RequiresPersistenceOperation operation = new RequiresPersistenceOperation();
2819 WaitForTurn(operation, timeoutHelper.RemainingTime());
2821 ValidateStateForUnload();
2822 if (this.state != WorkflowApplicationState.Unloaded) // Unload on unload is a no-op
2824 PersistenceOperation persistenceOperation;
2826 if (this.Controller.State == WorkflowInstanceState.Complete)
2828 persistenceOperation = PersistenceOperation.Complete;
2832 persistenceOperation = PersistenceOperation.Unload;
2835 PersistCore(ref timeoutHelper, persistenceOperation);
2840 NotifyOperationComplete(operation);
2844 [Fx.Tag.InheritThrows(From = "Unload")]
2845 public IAsyncResult BeginUnload(AsyncCallback callback, object state)
2847 return BeginUnload(ActivityDefaults.SaveTimeout, callback, state);
2850 [Fx.Tag.InheritThrows(From = "Unload")]
2851 public IAsyncResult BeginUnload(TimeSpan timeout, AsyncCallback callback, object state)
2853 ThrowIfHandlerThread();
2855 TimeoutHelper.ThrowIfNegativeArgument(timeout);
2857 return new UnloadOrPersistAsyncResult(this, timeout, PersistenceOperation.Unload, false, false, callback, state);
2860 [Fx.Tag.InheritThrows(From = "Unload")]
2861 public void EndUnload(IAsyncResult result)
2863 UnloadOrPersistAsyncResult.End(result);
2866 IDictionary<XName, InstanceValue> GetInstanceMetadata()
2868 if (this.DefinitionIdentity != null)
2870 if (this.instanceMetadata == null)
2872 this.instanceMetadata = new Dictionary<XName, InstanceValue>(2);
2874 if (!this.instanceMetadata.ContainsKey(WorkflowNamespace.WorkflowHostType))
2876 this.instanceMetadata.Add(WorkflowNamespace.WorkflowHostType, new InstanceValue(Workflow45Namespace.WorkflowApplication));
2878 this.instanceMetadata[Workflow45Namespace.DefinitionIdentity] =
2879 new InstanceValue(this.DefinitionIdentity, InstanceValueOptions.Optional);
2881 return this.instanceMetadata;
2884 void UpdateInstanceMetadata()
2886 // Update the metadata to reflect the new identity after a Dynamic Update
2887 this.persistenceManager.SetMutablemetadata(new Dictionary<XName, InstanceValue>
2889 { Workflow45Namespace.DefinitionIdentity, new InstanceValue(this.DefinitionIdentity, InstanceValueOptions.Optional) }
2893 void ThrowIfMulticast(Delegate value)
2895 if (value != null && value.GetInvocationList().Length > 1)
2897 throw FxTrace.Exception.Argument("value", SR.OnlySingleCastDelegatesAllowed);
2901 void ThrowIfAborted()
2903 if (this.state == WorkflowApplicationState.Aborted)
2905 throw FxTrace.Exception.AsError(new WorkflowApplicationAbortedException(SR.WorkflowApplicationAborted(this.Id), this.Id));
2909 void ThrowIfTerminatedOrCompleted()
2911 if (this.hasRaisedCompleted)
2913 Exception completionException;
2914 this.Controller.GetCompletionState(out completionException);
2915 if (completionException != null)
2917 throw FxTrace.Exception.AsError(new WorkflowApplicationTerminatedException(SR.WorkflowApplicationTerminated(this.Id), this.Id, completionException));
2921 throw FxTrace.Exception.AsError(new WorkflowApplicationCompletedException(SR.WorkflowApplicationCompleted(this.Id), this.Id));
2926 void ThrowIfUnloaded()
2928 if (this.state == WorkflowApplicationState.Unloaded)
2930 throw FxTrace.Exception.AsError(new WorkflowApplicationUnloadedException(SR.WorkflowApplicationUnloaded(this.Id), this.Id));
2934 void ThrowIfNoInstanceStore()
2936 if (!HasPersistenceProvider)
2938 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InstanceStoreRequiredToPersist));
2942 void ThrowIfHandlerThread()
2944 if (this.IsHandlerThread)
2946 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotPerformOperationFromHandlerThread));
2950 void ValidateStateForRun()
2952 // WorkflowInstanceException validations
2954 ThrowIfTerminatedOrCompleted();
2958 void ValidateStateForGetAllBookmarks()
2960 // WorkflowInstanceException validations
2962 ThrowIfTerminatedOrCompleted();
2966 void ValidateStateForCancel()
2968 // WorkflowInstanceException validations
2971 // We only validate that we aren't aborted and no-op otherwise.
2972 // This is because the scenario for calling cancel is for it to
2973 // be a best attempt from an unknown thread. The less it throws
2974 // the easier it is to author a host.
2977 void ValidateStateForLoad()
2980 ThrowIfReadOnly(); // only allow a single Load() or Run()
2981 if (this.instanceIdSet)
2983 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationAlreadyHasId));
2987 void ValidateStateForPersist()
2989 // WorkflowInstanceException validations
2991 ThrowIfTerminatedOrCompleted();
2994 // Other validations
2995 ThrowIfNoInstanceStore();
2998 void ValidateStateForUnload()
3000 // WorkflowInstanceException validations
3003 // Other validations
3004 if (this.Controller.State != WorkflowInstanceState.Complete)
3006 ThrowIfNoInstanceStore();
3010 void ValidateStateForTerminate()
3012 // WorkflowInstanceException validations
3014 ThrowIfTerminatedOrCompleted();
3018 enum PersistenceOperation : byte
3025 enum WorkflowApplicationState : byte
3033 internal class SynchronousSynchronizationContext : SynchronizationContext
3035 static SynchronousSynchronizationContext value;
3037 SynchronousSynchronizationContext()
3041 public static SynchronousSynchronizationContext Value
3047 value = new SynchronousSynchronizationContext();
3053 public override void Post(SendOrPostCallback d, object state)
3058 public override void Send(SendOrPostCallback d, object state)
3064 class InvokeAsyncResult : AsyncResult
3066 static Action<object, TimeoutException> waitCompleteCallback;
3067 WorkflowApplication instance;
3068 AsyncWaitHandle completionWaiter;
3069 IDictionary<string, object> outputs;
3070 Exception completionException;
3072 public InvokeAsyncResult(Activity activity, IDictionary<string, object> inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout, SynchronizationContext syncContext, AsyncInvokeContext invokeContext, AsyncCallback callback, object state)
3073 : base(callback, state)
3075 Fx.Assert(activity != null, "Need an activity");
3077 this.completionWaiter = new AsyncWaitHandle();
3078 syncContext = syncContext ?? SynchronousSynchronizationContext.Value;
3080 this.instance = WorkflowApplication.StartInvoke(activity, inputs, extensions, syncContext, new Action(this.OnInvokeComplete), invokeContext);
3082 if (this.completionWaiter.WaitAsync(WaitCompleteCallback, this, timeout))
3084 bool completeSelf = OnWorkflowCompletion();
3088 if (this.completionException != null)
3090 throw FxTrace.Exception.AsError(this.completionException);
3100 static Action<object, TimeoutException> WaitCompleteCallback
3104 if (waitCompleteCallback == null)
3106 waitCompleteCallback = new Action<object, TimeoutException>(OnWaitComplete);
3109 return waitCompleteCallback;
3113 public static IDictionary<string, object> End(IAsyncResult result)
3115 InvokeAsyncResult thisPtr = AsyncResult.End<InvokeAsyncResult>(result);
3116 return thisPtr.outputs;
3119 void OnInvokeComplete()
3121 this.completionWaiter.Set();
3124 static void OnWaitComplete(object state, TimeoutException asyncException)
3126 InvokeAsyncResult thisPtr = (InvokeAsyncResult)state;
3128 if (asyncException != null)
3130 thisPtr.instance.Abort(SR.AbortingDueToInstanceTimeout);
3131 thisPtr.Complete(false, asyncException);
3135 bool completeSelf = true;
3139 completeSelf = thisPtr.OnWorkflowCompletion();
3148 thisPtr.completionException = e;
3153 thisPtr.Complete(false, thisPtr.completionException);
3157 bool OnWorkflowCompletion()
3159 if (this.instance.Controller.State == WorkflowInstanceState.Aborted)
3161 this.completionException = new WorkflowApplicationAbortedException(SR.DefaultAbortReason, this.instance.Controller.GetAbortReason());
3165 Fx.Assert(this.instance.Controller.State == WorkflowInstanceState.Complete, "We should only get here when we are completed.");
3167 this.instance.Controller.GetCompletionState(out this.outputs, out this.completionException);
3175 class ResumeBookmarkAsyncResult : AsyncResult
3177 static AsyncCompletion resumedCallback = new AsyncCompletion(OnResumed);
3178 static Action<object, TimeoutException> waitCompleteCallback = new Action<object, TimeoutException>(OnWaitComplete);
3179 static AsyncCompletion trackingCompleteCallback = new AsyncCompletion(OnTrackingComplete);
3181 WorkflowApplication instance;
3184 BookmarkResumptionResult resumptionResult;
3185 TimeoutHelper timeoutHelper;
3186 bool isFromExtension;
3187 bool pendedUnenqueued;
3189 InstanceOperation currentOperation;
3191 public ResumeBookmarkAsyncResult(WorkflowApplication instance, Bookmark bookmark, object value, TimeSpan timeout, AsyncCallback callback, object state)
3192 : this(instance, bookmark, value, false, timeout, callback, state)
3196 public ResumeBookmarkAsyncResult(WorkflowApplication instance, Bookmark bookmark, object value, bool isFromExtension, TimeSpan timeout, AsyncCallback callback, object state)
3197 : base(callback, state)
3199 this.instance = instance;
3200 this.bookmark = bookmark;
3202 this.isFromExtension = isFromExtension;
3203 this.timeoutHelper = new TimeoutHelper(timeout);
3205 bool completeSelf = false;
3206 bool success = false;
3208 this.OnCompleting = new Action<AsyncResult, Exception>(Finally);
3212 if (!this.instance.hasCalledRun && !this.isFromExtension)
3214 // Increment the pending unenqueued count so we don't raise idle in the time between
3215 // when the Run completes and when we enqueue our InstanceOperation.
3216 this.pendedUnenqueued = true;
3217 this.instance.IncrementPendingUnenqueud();
3219 IAsyncResult result = this.instance.BeginInternalRun(this.timeoutHelper.RemainingTime(), false, PrepareAsyncCompletion(resumedCallback), this);
3220 if (result.CompletedSynchronously)
3222 completeSelf = OnResumed(result);
3227 completeSelf = StartResumptionLoop();
3234 // We only want to call this if we are throwing. Otherwise OnCompleting will take care of it.
3237 Finally(null, null);
3247 public static BookmarkResumptionResult End(IAsyncResult result)
3249 ResumeBookmarkAsyncResult thisPtr = AsyncResult.End<ResumeBookmarkAsyncResult>(result);
3251 return thisPtr.resumptionResult;
3254 void ClearPendedUnenqueued()
3256 if (this.pendedUnenqueued)
3258 this.pendedUnenqueued = false;
3259 this.instance.DecrementPendingUnenqueud();
3263 void NotifyOperationComplete()
3265 InstanceOperation lastOperation = this.currentOperation;
3266 this.currentOperation = null;
3267 this.instance.NotifyOperationComplete(lastOperation);
3270 void Finally(AsyncResult result, Exception completionException)
3272 ClearPendedUnenqueued();
3273 NotifyOperationComplete();
3276 static bool OnResumed(IAsyncResult result)
3278 ResumeBookmarkAsyncResult thisPtr = (ResumeBookmarkAsyncResult)result.AsyncState;
3279 thisPtr.instance.EndRun(result);
3280 return thisPtr.StartResumptionLoop();
3283 bool StartResumptionLoop()
3285 this.currentOperation = new RequiresIdleOperation(this.isFromExtension);
3286 return WaitOnCurrentOperation();
3289 bool WaitOnCurrentOperation()
3291 bool stillSync = true;
3292 bool tryOneMore = true;
3298 Fx.Assert(this.currentOperation != null, "We should always have a current operation here.");
3300 if (this.instance.WaitForTurnAsync(this.currentOperation, this.timeoutHelper.RemainingTime(), waitCompleteCallback, this))
3302 ClearPendedUnenqueued();
3304 if (CheckIfBookmarksAreInvalid())
3310 stillSync = ProcessResumption();
3312 tryOneMore = this.resumptionResult == BookmarkResumptionResult.NotReady;
3324 static void OnWaitComplete(object state, TimeoutException asyncException)
3326 ResumeBookmarkAsyncResult thisPtr = (ResumeBookmarkAsyncResult)state;
3328 if (asyncException != null)
3330 thisPtr.Complete(false, asyncException);
3334 Exception completionException = null;
3335 bool completeSelf = false;
3339 thisPtr.ClearPendedUnenqueued();
3341 if (thisPtr.CheckIfBookmarksAreInvalid())
3343 completeSelf = true;
3347 completeSelf = thisPtr.ProcessResumption();
3349 if (thisPtr.resumptionResult == BookmarkResumptionResult.NotReady)
3351 completeSelf = thisPtr.WaitOnCurrentOperation();
3362 completeSelf = true;
3363 completionException = e;
3368 thisPtr.Complete(false, completionException);
3372 bool CheckIfBookmarksAreInvalid()
3374 if (this.instance.AreBookmarksInvalid(out this.resumptionResult))
3382 bool ProcessResumption()
3384 bool stillSync = true;
3386 this.resumptionResult = this.instance.ResumeBookmarkCore(this.bookmark, this.value);
3388 if (this.resumptionResult == BookmarkResumptionResult.Success)
3390 if (this.instance.Controller.HasPendingTrackingRecords)
3392 IAsyncResult result = this.instance.Controller.BeginFlushTrackingRecords(this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(trackingCompleteCallback), this);
3394 if (result.CompletedSynchronously)
3396 stillSync = OnTrackingComplete(result);
3404 else if (this.resumptionResult == BookmarkResumptionResult.NotReady)
3406 NotifyOperationComplete();
3407 this.currentOperation = new DeferredRequiresIdleOperation();
3413 static bool OnTrackingComplete(IAsyncResult result)
3415 ResumeBookmarkAsyncResult thisPtr = (ResumeBookmarkAsyncResult)result.AsyncState;
3417 thisPtr.instance.Controller.EndFlushTrackingRecords(result);
3423 class UnloadOrPersistAsyncResult : TransactedAsyncResult
3425 static Action<object, TimeoutException> waitCompleteCallback = new Action<object, TimeoutException>(OnWaitComplete);
3426 static AsyncCompletion savedCallback = new AsyncCompletion(OnSaved);
3427 static AsyncCompletion persistedCallback = new AsyncCompletion(OnPersisted);
3428 static AsyncCompletion initializedCallback = new AsyncCompletion(OnProviderInitialized);
3429 static AsyncCompletion readynessEnsuredCallback = new AsyncCompletion(OnProviderReadynessEnsured);
3430 static AsyncCompletion trackingCompleteCallback = new AsyncCompletion(OnTrackingComplete);
3431 static AsyncCompletion deleteOwnerCompleteCallback = new AsyncCompletion(OnOwnerDeleted);
3432 static AsyncCompletion completeContextCallback = new AsyncCompletion(OnCompleteContext);
3433 static Action<AsyncResult, Exception> completeCallback = new Action<AsyncResult, Exception>(OnComplete);
3435 DependentTransaction dependentTransaction;
3436 WorkflowApplication instance;
3438 TimeoutHelper timeoutHelper;
3439 PersistenceOperation operation;
3440 RequiresPersistenceOperation instanceOperation;
3441 WorkflowPersistenceContext context;
3442 IDictionary<XName, InstanceValue> data;
3443 PersistencePipeline pipeline;
3444 bool isInternalPersist;
3446 public UnloadOrPersistAsyncResult(WorkflowApplication instance, TimeSpan timeout, PersistenceOperation operation,
3447 bool isWorkflowThread, bool isInternalPersist, AsyncCallback callback, object state)
3448 : base(callback, state)
3450 this.instance = instance;
3451 this.timeoutHelper = new TimeoutHelper(timeout);
3452 this.operation = operation;
3453 this.isInternalPersist = isInternalPersist;
3454 this.isUnloaded = (operation == PersistenceOperation.Unload || operation == PersistenceOperation.Complete);
3456 this.OnCompleting = UnloadOrPersistAsyncResult.completeCallback;
3459 bool success = false;
3461 // Save off the current transaction in case we have an async operation before we end up creating
3462 // the WorkflowPersistenceContext and create it on another thread. Do a blocking dependent clone that
3463 // we will complete when we are completed.
3465 // This will throw TransactionAbortedException by design, if the transaction is already rolled back.
3466 Transaction currentTransaction = Transaction.Current;
3467 if (currentTransaction != null)
3469 this.dependentTransaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
3474 if (isWorkflowThread)
3476 Fx.Assert(this.instance.Controller.IsPersistable, "The runtime won't schedule this work item unless we've passed the guard");
3478 // We're an internal persistence on the workflow thread which means
3479 // that we are passed the guard already, we have the lock, and we know
3480 // we aren't detached.
3482 completeSelf = InitializeProvider();
3487 this.instanceOperation = new RequiresPersistenceOperation();
3490 if (this.instance.WaitForTurnAsync(this.instanceOperation, this.timeoutHelper.RemainingTime(), waitCompleteCallback, this))
3492 completeSelf = ValidateState();
3496 completeSelf = false;
3504 NotifyOperationComplete();
3511 // If we had an exception, we need to complete the dependent transaction.
3514 if (this.dependentTransaction != null)
3516 this.dependentTransaction.Complete();
3527 bool ValidateState()
3529 bool alreadyUnloaded = false;
3530 if (this.operation == PersistenceOperation.Unload)
3532 this.instance.ValidateStateForUnload();
3533 alreadyUnloaded = this.instance.state == WorkflowApplicationState.Unloaded;
3537 this.instance.ValidateStateForPersist();
3540 if (alreadyUnloaded)
3546 return InitializeProvider();
3550 static void OnWaitComplete(object state, TimeoutException asyncException)
3552 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)state;
3553 if (asyncException != null)
3555 thisPtr.Complete(false, asyncException);
3560 Exception completionException = null;
3564 completeSelf = thisPtr.ValidateState();
3573 completionException = e;
3574 completeSelf = true;
3579 thisPtr.Complete(false, completionException);
3583 bool InitializeProvider()
3585 // We finally have the lock and are passed the guard. Let's update our operation if this is an Unload.
3586 if (this.operation == PersistenceOperation.Unload && this.instance.Controller.State == WorkflowInstanceState.Complete)
3588 this.operation = PersistenceOperation.Complete;
3591 if (this.instance.HasPersistenceProvider && !this.instance.persistenceManager.IsInitialized)
3593 IAsyncResult result = this.instance.persistenceManager.BeginInitialize(this.instance.DefinitionIdentity, this.timeoutHelper.RemainingTime(),
3594 PrepareAsyncCompletion(UnloadOrPersistAsyncResult.initializedCallback), this);
3595 return SyncContinue(result);
3599 return EnsureProviderReadyness();
3603 static bool OnProviderInitialized(IAsyncResult result)
3605 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3606 thisPtr.instance.persistenceManager.EndInitialize(result);
3607 return thisPtr.EnsureProviderReadyness();
3610 bool EnsureProviderReadyness()
3612 if (this.instance.HasPersistenceProvider && !this.instance.persistenceManager.IsLocked && this.dependentTransaction != null)
3614 IAsyncResult result = this.instance.persistenceManager.BeginEnsureReadyness(this.timeoutHelper.RemainingTime(),
3615 PrepareAsyncCompletion(UnloadOrPersistAsyncResult.readynessEnsuredCallback), this);
3616 return SyncContinue(result);
3624 static bool OnProviderReadynessEnsured(IAsyncResult result)
3626 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3627 thisPtr.instance.persistenceManager.EndEnsureReadyness(result);
3628 return thisPtr.Track();
3631 public static void End(IAsyncResult result)
3633 AsyncResult.End<UnloadOrPersistAsyncResult>(result);
3636 void NotifyOperationComplete()
3638 RequiresPersistenceOperation localInstanceOperation = this.instanceOperation;
3639 this.instanceOperation = null;
3640 this.instance.NotifyOperationComplete(localInstanceOperation);
3645 // Do the tracking before preparing in case the tracking data is being pushed into
3646 // an extension and persisted transactionally with the instance state.
3648 if (this.instance.HasPersistenceProvider)
3650 // We only track the persistence operation if we actually
3651 // are persisting (and not just hitting PersistenceParticipants)
3652 this.instance.TrackPersistence(this.operation);
3655 if (this.instance.Controller.HasPendingTrackingRecords)
3657 TimeSpan flushTrackingRecordsTimeout;
3659 if (this.isInternalPersist)
3661 // If we're an internal persist we're using TimeSpan.MaxValue
3662 // for our persistence and we want to use a smaller timeout
3664 flushTrackingRecordsTimeout = ActivityDefaults.TrackingTimeout;
3668 flushTrackingRecordsTimeout = this.timeoutHelper.RemainingTime();
3671 IAsyncResult result = this.instance.Controller.BeginFlushTrackingRecords(flushTrackingRecordsTimeout, PrepareAsyncCompletion(trackingCompleteCallback), this);
3672 return SyncContinue(result);
3675 return CollectAndMap();
3678 static bool OnTrackingComplete(IAsyncResult result)
3680 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3681 thisPtr.instance.Controller.EndFlushTrackingRecords(result);
3682 return thisPtr.CollectAndMap();
3685 bool CollectAndMap()
3687 bool success = false;
3690 if (this.instance.HasPersistenceModule)
3692 IEnumerable<IPersistencePipelineModule> modules = this.instance.GetExtensions<IPersistencePipelineModule>();
3693 this.pipeline = new PersistencePipeline(modules, PersistenceManager.GenerateInitialData(this.instance));
3694 this.pipeline.Collect();
3695 this.pipeline.Map();
3696 this.data = this.pipeline.Values;
3702 if (!success && this.context != null)
3704 this.context.Abort();
3708 if (this.instance.HasPersistenceProvider)
3720 IAsyncResult result = null;
3723 if (this.data == null)
3725 this.data = PersistenceManager.GenerateInitialData(this.instance);
3728 if (this.context == null)
3730 this.context = new WorkflowPersistenceContext(this.pipeline != null && this.pipeline.IsSaveTransactionRequired,
3731 this.dependentTransaction, this.timeoutHelper.OriginalTimeout);
3734 using (PrepareTransactionalCall(this.context.PublicTransaction))
3736 result = this.instance.persistenceManager.BeginSave(this.data, this.operation, this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(persistedCallback), this);
3741 if (result == null && this.context != null)
3743 this.context.Abort();
3746 return SyncContinue(result);
3749 static bool OnPersisted(IAsyncResult result)
3751 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3752 bool success = false;
3755 thisPtr.instance.persistenceManager.EndSave(result);
3762 thisPtr.context.Abort();
3765 return thisPtr.Save();
3770 if (this.pipeline != null)
3772 IAsyncResult result = null;
3775 if (this.context == null)
3777 this.context = new WorkflowPersistenceContext(this.pipeline.IsSaveTransactionRequired,
3778 this.dependentTransaction, this.timeoutHelper.RemainingTime());
3781 this.instance.persistencePipelineInUse = this.pipeline;
3782 Thread.MemoryBarrier();
3783 if (this.instance.state == WorkflowApplicationState.Aborted)
3785 throw FxTrace.Exception.AsError(new OperationCanceledException(SR.DefaultAbortReason));
3788 using (PrepareTransactionalCall(this.context.PublicTransaction))
3790 result = this.pipeline.BeginSave(this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(savedCallback), this);
3797 this.instance.persistencePipelineInUse = null;
3798 if (this.context != null)
3800 this.context.Abort();
3804 return SyncContinue(result);
3808 return CompleteContext();
3812 static bool OnSaved(IAsyncResult result)
3814 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3816 bool success = false;
3819 thisPtr.pipeline.EndSave(result);
3824 thisPtr.instance.persistencePipelineInUse = null;
3827 thisPtr.context.Abort();
3831 return thisPtr.CompleteContext();
3834 bool CompleteContext()
3836 bool wentAsync = false;
3837 IAsyncResult completeResult = null;
3839 if (this.context != null)
3841 wentAsync = this.context.TryBeginComplete(this.PrepareAsyncCompletion(completeContextCallback), this, out completeResult);
3846 Fx.Assert(completeResult != null, "We shouldn't have null here because we would have rethrown or gotten false for went async.");
3847 return SyncContinue(completeResult);
3851 // We completed synchronously if we didn't get an async result out of
3853 return DeleteOwner();
3857 static bool OnCompleteContext(IAsyncResult result)
3859 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3860 thisPtr.context.EndComplete(result);
3862 return thisPtr.DeleteOwner();
3867 if (this.instance.HasPersistenceProvider && this.instance.persistenceManager.OwnerWasCreated &&
3868 (this.operation == PersistenceOperation.Unload || this.operation == PersistenceOperation.Complete))
3870 // This call uses the ambient transaction directly if there was one, to mimic the [....] case.
3872 IAsyncResult deleteOwnerResult = null;
3873 using (PrepareTransactionalCall(this.dependentTransaction))
3875 deleteOwnerResult = this.instance.persistenceManager.BeginDeleteOwner(this.timeoutHelper.RemainingTime(),
3876 this.PrepareAsyncCompletion(UnloadOrPersistAsyncResult.deleteOwnerCompleteCallback), this);
3878 return this.SyncContinue(deleteOwnerResult);
3882 return CloseInstance();
3886 static bool OnOwnerDeleted(IAsyncResult result)
3888 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3889 thisPtr.instance.persistenceManager.EndDeleteOwner(result);
3890 return thisPtr.CloseInstance();
3893 bool CloseInstance()
3895 // NOTE: We need to make sure that any changes which occur
3896 // here are appropriately ported to WorkflowApplication's
3897 // CompletionHandler.OnStage1Complete method in the case
3898 // where we don't call BeginPersist.
3899 if (this.operation != PersistenceOperation.Save)
3901 // Stop execution if we've given up the instance lock
3902 this.instance.state = WorkflowApplicationState.Paused;
3905 if (this.isUnloaded)
3907 this.instance.MarkUnloaded();
3913 static void OnComplete(AsyncResult result, Exception exception)
3915 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result;
3918 thisPtr.NotifyOperationComplete();
3922 if (thisPtr.dependentTransaction != null)
3924 thisPtr.dependentTransaction.Complete();
3930 abstract class SimpleOperationAsyncResult : AsyncResult
3932 static Action<object, TimeoutException> waitCompleteCallback = new Action<object, TimeoutException>(OnWaitComplete);
3933 static AsyncCallback trackingCompleteCallback = Fx.ThunkCallback(new AsyncCallback(OnTrackingComplete));
3935 WorkflowApplication instance;
3936 TimeoutHelper timeoutHelper;
3938 protected SimpleOperationAsyncResult(WorkflowApplication instance, AsyncCallback callback, object state)
3939 : base(callback, state)
3941 this.instance = instance;
3944 protected WorkflowApplication Instance
3948 return this.instance;
3952 protected void Run(TimeSpan timeout)
3954 this.timeoutHelper = new TimeoutHelper(timeout);
3956 InstanceOperation operation = new InstanceOperation();
3958 bool completeSelf = true;
3962 completeSelf = this.instance.WaitForTurnAsync(operation, this.timeoutHelper.RemainingTime(), waitCompleteCallback, this);
3966 this.ValidateState();
3968 completeSelf = PerformOperationAndTrack();
3975 this.instance.NotifyOperationComplete(operation);
3985 bool PerformOperationAndTrack()
3989 bool completedSync = true;
3991 if (this.instance.Controller.HasPendingTrackingRecords)
3993 IAsyncResult trackingResult = this.instance.Controller.BeginFlushTrackingRecords(this.timeoutHelper.RemainingTime(), trackingCompleteCallback, this);
3995 if (trackingResult.CompletedSynchronously)
3997 this.instance.Controller.EndFlushTrackingRecords(trackingResult);
4001 completedSync = false;
4005 return completedSync;
4008 static void OnWaitComplete(object state, TimeoutException asyncException)
4010 SimpleOperationAsyncResult thisPtr = (SimpleOperationAsyncResult)state;
4012 if (asyncException != null)
4014 thisPtr.Complete(false, asyncException);
4018 Exception completionException = null;
4019 bool completeSelf = true;
4023 thisPtr.ValidateState();
4025 completeSelf = thisPtr.PerformOperationAndTrack();
4034 completionException = e;
4040 thisPtr.instance.ForceNotifyOperationComplete();
4046 thisPtr.Complete(false, completionException);
4051 static void OnTrackingComplete(IAsyncResult result)
4053 if (result.CompletedSynchronously)
4058 SimpleOperationAsyncResult thisPtr = (SimpleOperationAsyncResult)result.AsyncState;
4060 Exception completionException = null;
4064 thisPtr.instance.Controller.EndFlushTrackingRecords(result);
4073 completionException = e;
4077 thisPtr.instance.ForceNotifyOperationComplete();
4080 thisPtr.Complete(false, completionException);
4083 protected abstract void ValidateState();
4084 protected abstract void PerformOperation();
4087 class TerminateAsyncResult : SimpleOperationAsyncResult
4091 TerminateAsyncResult(WorkflowApplication instance, Exception reason, AsyncCallback callback, object state)
4092 : base(instance, callback, state)
4094 this.reason = reason;
4097 public static TerminateAsyncResult Create(WorkflowApplication instance, Exception reason, TimeSpan timeout, AsyncCallback callback, object state)
4099 TerminateAsyncResult result = new TerminateAsyncResult(instance, reason, callback, state);
4100 result.Run(timeout);
4104 public static void End(IAsyncResult result)
4106 AsyncResult.End<TerminateAsyncResult>(result);
4109 protected override void ValidateState()
4111 this.Instance.ValidateStateForTerminate();
4114 protected override void PerformOperation()
4116 this.Instance.TerminateCore(this.reason);
4120 class CancelAsyncResult : SimpleOperationAsyncResult
4122 CancelAsyncResult(WorkflowApplication instance, AsyncCallback callback, object state)
4123 : base(instance, callback, state)
4127 public static CancelAsyncResult Create(WorkflowApplication instance, TimeSpan timeout, AsyncCallback callback, object state)
4129 CancelAsyncResult result = new CancelAsyncResult(instance, callback, state);
4130 result.Run(timeout);
4134 public static void End(IAsyncResult result)
4136 AsyncResult.End<CancelAsyncResult>(result);
4139 protected override void ValidateState()
4141 this.Instance.ValidateStateForCancel();
4144 protected override void PerformOperation()
4146 this.Instance.CancelCore();
4150 class RunAsyncResult : SimpleOperationAsyncResult
4154 RunAsyncResult(WorkflowApplication instance, bool isUserRun, AsyncCallback callback, object state)
4155 : base(instance, callback, state)
4157 this.isUserRun = isUserRun;
4160 public static RunAsyncResult Create(WorkflowApplication instance, bool isUserRun, TimeSpan timeout, AsyncCallback callback, object state)
4162 RunAsyncResult result = new RunAsyncResult(instance, isUserRun, callback, state);
4163 result.Run(timeout);
4167 public static void End(IAsyncResult result)
4169 AsyncResult.End<RunAsyncResult>(result);
4172 protected override void ValidateState()
4174 this.Instance.ValidateStateForRun();
4177 protected override void PerformOperation()
4181 // We set this to true here so that idle will be raised
4182 // regardless of whether any work is performed.
4183 this.Instance.hasExecutionOccurredSinceLastIdle = true;
4186 this.Instance.RunCore();
4190 class UnlockInstanceAsyncResult : TransactedAsyncResult
4192 static AsyncCompletion instanceUnlockedCallback = new AsyncCompletion(OnInstanceUnlocked);
4193 static AsyncCompletion ownerDeletedCallback = new AsyncCompletion(OnOwnerDeleted);
4194 static Action<AsyncResult, Exception> completeCallback = new Action<AsyncResult, Exception>(OnComplete);
4196 readonly PersistenceManager persistenceManager;
4197 readonly TimeoutHelper timeoutHelper;
4199 DependentTransaction dependentTransaction;
4201 public UnlockInstanceAsyncResult(PersistenceManager persistenceManager, TimeoutHelper timeoutHelper, AsyncCallback callback, object state)
4202 : base(callback, state)
4204 this.persistenceManager = persistenceManager;
4205 this.timeoutHelper = timeoutHelper;
4207 Transaction currentTransaction = Transaction.Current;
4208 if (currentTransaction != null)
4210 this.dependentTransaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
4213 OnCompleting = UnlockInstanceAsyncResult.completeCallback;
4215 bool success = false;
4218 IAsyncResult result;
4219 using (this.PrepareTransactionalCall(this.dependentTransaction))
4221 if (this.persistenceManager.OwnerWasCreated)
4223 // if the owner was created by this WorkflowApplication, delete it.
4224 // This implicitly unlocks the instance.
4225 result = this.persistenceManager.BeginDeleteOwner(this.timeoutHelper.RemainingTime(), this.PrepareAsyncCompletion(ownerDeletedCallback), this);
4229 result = this.persistenceManager.BeginUnlock(this.timeoutHelper.RemainingTime(), this.PrepareAsyncCompletion(instanceUnlockedCallback), this);
4233 if (SyncContinue(result))
4244 this.persistenceManager.Abort();
4249 public static void End(IAsyncResult result)
4251 AsyncResult.End<UnlockInstanceAsyncResult>(result);
4254 static bool OnInstanceUnlocked(IAsyncResult result)
4256 UnlockInstanceAsyncResult thisPtr = (UnlockInstanceAsyncResult)result.AsyncState;
4257 thisPtr.persistenceManager.EndUnlock(result);
4261 static bool OnOwnerDeleted(IAsyncResult result)
4263 UnlockInstanceAsyncResult thisPtr = (UnlockInstanceAsyncResult)result.AsyncState;
4264 thisPtr.persistenceManager.EndDeleteOwner(result);
4268 static void OnComplete(AsyncResult result, Exception exception)
4270 UnlockInstanceAsyncResult thisPtr = (UnlockInstanceAsyncResult)result;
4271 if (thisPtr.dependentTransaction != null)
4273 thisPtr.dependentTransaction.Complete();
4275 thisPtr.persistenceManager.Abort();
4279 class LoadAsyncResult : TransactedAsyncResult
4281 static Action<object, TimeoutException> waitCompleteCallback = new Action<object, TimeoutException>(OnWaitComplete);
4282 static AsyncCompletion providerRegisteredCallback = new AsyncCompletion(OnProviderRegistered);
4283 static AsyncCompletion loadCompleteCallback = new AsyncCompletion(OnLoadComplete);
4284 static AsyncCompletion loadPipelineCallback = new AsyncCompletion(OnLoadPipeline);
4285 static AsyncCompletion completeContextCallback = new AsyncCompletion(OnCompleteContext);
4286 static Action<AsyncResult, Exception> completeCallback = new Action<AsyncResult, Exception>(OnComplete);
4288 readonly WorkflowApplication application;
4289 readonly PersistenceManager persistenceManager;
4290 readonly TimeoutHelper timeoutHelper;
4291 readonly bool loadAny;
4293 object deserializedRuntimeState;
4294 PersistencePipeline pipeline;
4295 WorkflowPersistenceContext context;
4296 DependentTransaction dependentTransaction;
4297 IDictionary<XName, InstanceValue> values;
4298 DynamicUpdateMap updateMap;
4299 InstanceOperation instanceOperation;
4301 public LoadAsyncResult(WorkflowApplication application, PersistenceManager persistenceManager,
4302 IDictionary<XName, InstanceValue> values, DynamicUpdateMap updateMap, TimeSpan timeout,
4303 AsyncCallback callback, object state)
4304 : base(callback, state)
4306 this.application = application;
4307 this.persistenceManager = persistenceManager;
4308 this.values = values;
4309 this.timeoutHelper = new TimeoutHelper(timeout);
4310 this.updateMap = updateMap;
4315 public LoadAsyncResult(WorkflowApplication application, PersistenceManager persistenceManager,
4316 bool loadAny, TimeSpan timeout, AsyncCallback callback, object state)
4317 : base(callback, state)
4319 this.application = application;
4320 this.persistenceManager = persistenceManager;
4321 this.loadAny = loadAny;
4322 this.timeoutHelper = new TimeoutHelper(timeout);
4329 OnCompleting = LoadAsyncResult.completeCallback;
4331 // Save off the current transaction in case we have an async operation before we end up creating
4332 // the WorkflowPersistenceContext and create it on another thread. Do a simple clone here to prevent
4333 // the object referenced by Transaction.Current from disposing before we get around to referencing it
4334 // when we create the WorkflowPersistenceContext.
4336 // This will throw TransactionAbortedException by design, if the transaction is already rolled back.
4337 Transaction currentTransaction = Transaction.Current;
4338 if (currentTransaction != null)
4340 this.dependentTransaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
4344 bool success = false;
4345 Exception updateException = null;
4348 if (this.application == null)
4350 completeSelf = RegisterProvider();
4354 completeSelf = WaitForTurn();
4358 catch (InstanceUpdateException e)
4360 updateException = e;
4363 catch (VersionMismatchException e)
4365 updateException = e;
4372 if (this.dependentTransaction != null)
4374 this.dependentTransaction.Complete();
4376 Abort(this, updateException);
4386 public static void End(IAsyncResult result)
4388 AsyncResult.End<LoadAsyncResult>(result);
4391 public static WorkflowApplicationInstance EndAndCreateInstance(IAsyncResult result)
4393 LoadAsyncResult thisPtr = AsyncResult.End<LoadAsyncResult>(result);
4394 Fx.AssertAndThrow(thisPtr.application == null, "Should not create a WorkflowApplicationInstance if we already have a WorkflowApplication");
4396 ActivityExecutor deserializedRuntimeState = WorkflowApplication.ExtractRuntimeState(thisPtr.values, thisPtr.persistenceManager.InstanceId);
4397 return new WorkflowApplicationInstance(thisPtr.persistenceManager, thisPtr.values, deserializedRuntimeState.WorkflowIdentity);
4403 bool success = false;
4404 this.instanceOperation = new InstanceOperation { RequiresInitialized = false };
4407 if (this.application.WaitForTurnAsync(this.instanceOperation, this.timeoutHelper.RemainingTime(), waitCompleteCallback, this))
4409 completeSelf = ValidateState();
4413 completeSelf = false;
4421 NotifyOperationComplete();
4425 return completeSelf;
4428 static void OnWaitComplete(object state, TimeoutException asyncException)
4430 LoadAsyncResult thisPtr = (LoadAsyncResult)state;
4431 if (asyncException != null)
4433 thisPtr.Complete(false, asyncException);
4438 Exception completionException = null;
4442 completeSelf = thisPtr.ValidateState();
4451 completionException = e;
4452 completeSelf = true;
4457 thisPtr.Complete(false, completionException);
4461 bool ValidateState()
4463 this.application.ValidateStateForLoad();
4465 this.application.SetPersistenceManager(this.persistenceManager);
4468 this.application.instanceId = this.persistenceManager.InstanceId;
4469 this.application.instanceIdSet = true;
4471 if (this.application.InstanceStore == null)
4473 this.application.InstanceStore = this.persistenceManager.InstanceStore;
4476 return RegisterProvider();
4479 bool RegisterProvider()
4481 if (!this.persistenceManager.IsInitialized)
4483 WorkflowIdentity definitionIdentity = this.application != null ? this.application.DefinitionIdentity : WorkflowApplication.unknownIdentity;
4484 IAsyncResult result = this.persistenceManager.BeginInitialize(definitionIdentity, this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(providerRegisteredCallback), this);
4485 return SyncContinue(result);
4493 static bool OnProviderRegistered(IAsyncResult result)
4495 LoadAsyncResult thisPtr = (LoadAsyncResult)result.AsyncState;
4496 thisPtr.persistenceManager.EndInitialize(result);
4497 return thisPtr.Load();
4502 bool success = false;
4503 IAsyncResult result = null;
4506 bool transactionRequired = this.application != null ? this.application.IsLoadTransactionRequired() : false;
4507 this.context = new WorkflowPersistenceContext(transactionRequired,
4508 this.dependentTransaction, this.timeoutHelper.OriginalTimeout);
4510 // Values is null if this is an initial load from the database.
4511 // It is non-null if we already loaded values into a WorkflowApplicationInstance,
4512 // and are now loading from that WAI.
4513 if (this.values == null)
4515 using (PrepareTransactionalCall(this.context.PublicTransaction))
4519 result = this.persistenceManager.BeginTryLoad(this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(loadCompleteCallback), this);
4523 result = this.persistenceManager.BeginLoad(this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(loadCompleteCallback), this);
4531 if (!success && this.context != null)
4533 this.context.Abort();
4539 return LoadValues(null);
4543 return SyncContinue(result);
4547 static bool OnLoadComplete(IAsyncResult result)
4549 LoadAsyncResult thisPtr = (LoadAsyncResult)result.AsyncState;
4550 return thisPtr.LoadValues(result);
4553 bool LoadValues(IAsyncResult result)
4555 IAsyncResult loadResult = null;
4556 bool success = false;
4559 Fx.Assert((result == null) != (this.values == null), "We should either have values already retrieved, or an IAsyncResult to retrieve them");
4565 if (!this.persistenceManager.EndTryLoad(result, out this.values))
4567 throw FxTrace.Exception.AsError(new InstanceNotReadyException(SR.NoRunnableInstances));
4569 if (this.application != null)
4571 if (this.application.instanceIdSet)
4573 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowApplicationAlreadyHasId));
4576 this.application.instanceId = this.persistenceManager.InstanceId;
4577 this.application.instanceIdSet = true;
4582 this.values = this.persistenceManager.EndLoad(result);
4586 if (this.application != null)
4588 this.pipeline = this.application.ProcessInstanceValues(this.values, out this.deserializedRuntimeState);
4590 if (this.pipeline != null)
4592 this.pipeline.SetLoadedValues(this.values);
4594 this.application.persistencePipelineInUse = this.pipeline;
4595 Thread.MemoryBarrier();
4596 if (this.application.state == WorkflowApplicationState.Aborted)
4598 throw FxTrace.Exception.AsError(new OperationCanceledException(SR.DefaultAbortReason));
4601 using (this.PrepareTransactionalCall(this.context.PublicTransaction))
4603 loadResult = this.pipeline.BeginLoad(this.timeoutHelper.RemainingTime(), this.PrepareAsyncCompletion(loadPipelineCallback), this);
4614 this.context.Abort();
4618 if (this.pipeline != null)
4620 return this.SyncContinue(loadResult);
4624 return this.CompleteContext();
4628 static bool OnLoadPipeline(IAsyncResult result)
4630 LoadAsyncResult thisPtr = (LoadAsyncResult)result.AsyncState;
4632 bool success = false;
4635 thisPtr.pipeline.EndLoad(result);
4642 thisPtr.context.Abort();
4645 return thisPtr.CompleteContext();
4648 bool CompleteContext()
4650 if (this.application != null)
4652 this.application.Initialize(this.deserializedRuntimeState, this.updateMap);
4653 if (this.updateMap != null)
4655 this.application.UpdateInstanceMetadata();
4659 IAsyncResult completeResult;
4660 if (this.context.TryBeginComplete(PrepareAsyncCompletion(completeContextCallback), this, out completeResult))
4662 Fx.Assert(completeResult != null, "We shouldn't have null here.");
4663 return SyncContinue(completeResult);
4671 static bool OnCompleteContext(IAsyncResult result)
4673 LoadAsyncResult thisPtr = (LoadAsyncResult)result.AsyncState;
4674 thisPtr.context.EndComplete(result);
4675 return thisPtr.Finish();
4680 if (this.pipeline != null)
4682 this.pipeline.Publish();
4687 void NotifyOperationComplete()
4689 if (this.application != null)
4691 InstanceOperation localInstanceOperation = this.instanceOperation;
4692 this.instanceOperation = null;
4693 this.application.NotifyOperationComplete(localInstanceOperation);
4697 static void OnComplete(AsyncResult result, Exception exception)
4699 LoadAsyncResult thisPtr = (LoadAsyncResult)result;
4702 if (thisPtr.dependentTransaction != null)
4704 thisPtr.dependentTransaction.Complete();
4707 if (exception != null)
4709 Abort(thisPtr, exception);
4714 thisPtr.NotifyOperationComplete();
4718 static void Abort(LoadAsyncResult thisPtr, Exception exception)
4720 if (thisPtr.application == null)
4722 thisPtr.persistenceManager.Abort();
4726 thisPtr.application.AbortDueToException(exception);
4731 // this class is not a general purpose SyncContext and is only meant to work for workflow scenarios, where the scheduler ensures
4732 // at most one work item pending. The scheduler ensures that Invoke must run before Post is called on a different thread.
4733 class PumpBasedSynchronizationContext : SynchronizationContext
4735 // The waitObject is cached per thread so that we can avoid the cost of creating
4736 // events for multiple synchronous invokes.
4738 static AutoResetEvent waitObject;
4739 AutoResetEvent queueWaiter;
4740 WorkItem currentWorkItem;
4742 TimeoutHelper timeoutHelper;
4744 public PumpBasedSynchronizationContext(TimeSpan timeout)
4746 this.timeoutHelper = new TimeoutHelper(timeout);
4747 this.thisLock = new object();
4750 bool IsInvokeCompleted
4756 public void DoPump()
4758 Fx.Assert(this.currentWorkItem != null, "the work item cannot be null");
4761 lock (this.thisLock)
4763 if (PumpBasedSynchronizationContext.waitObject == null)
4765 PumpBasedSynchronizationContext.waitObject = new AutoResetEvent(false);
4767 this.queueWaiter = PumpBasedSynchronizationContext.waitObject;
4769 workItem = this.currentWorkItem;
4770 this.currentWorkItem = null;
4774 Fx.Assert(this.queueWaiter != null, "queue waiter cannot be null");
4776 while (this.WaitForNextItem())
4778 Fx.Assert(this.currentWorkItem != null, "the work item cannot be null");
4779 workItem = this.currentWorkItem;
4780 this.currentWorkItem = null;
4785 public override void Post(SendOrPostCallback d, object state)
4787 ScheduleWorkItem(new WorkItem(d, state));
4790 public override void Send(SendOrPostCallback d, object state)
4792 throw FxTrace.Exception.AsError(new NotSupportedException(SR.SendNotSupported));
4795 // Since tracking can go async this may or may not be called directly
4796 // under a call to workItem.Invoke. Also, the scheduler may call
4797 // OnNotifyPaused or OnNotifyUnhandledException from any random thread
4798 // if runtime goes async (post-work item tracking, AsyncCodeActivity).
4799 public void OnInvokeCompleted()
4801 Fx.AssertAndFailFast(this.currentWorkItem == null, "There can be no pending work items when complete");
4803 this.IsInvokeCompleted = true;
4805 lock (this.thisLock)
4807 if (this.queueWaiter != null)
4809 // Since we don't know which thread this is being called
4810 // from we just set the waiter directly rather than
4811 // doing our SetWaiter cleanup.
4812 this.queueWaiter.Set();
4817 void ScheduleWorkItem(WorkItem item)
4819 lock (this.thisLock)
4821 Fx.AssertAndFailFast(this.currentWorkItem == null, "There cannot be more than 1 work item at a given time");
4822 this.currentWorkItem = item;
4823 if (this.queueWaiter != null)
4825 // Since we don't know which thread this is being called
4826 // from we just set the waiter directly rather than
4827 // doing our SetWaiter cleanup.
4828 this.queueWaiter.Set();
4833 bool WaitOne(AutoResetEvent waiter, TimeSpan timeout)
4835 bool success = false;
4838 bool result = TimeoutHelper.WaitOne(waiter, timeout);
4839 // if the wait timed out, reset the thread static
4847 PumpBasedSynchronizationContext.waitObject = null;
4852 bool WaitForNextItem()
4854 if (!WaitOne(this.queueWaiter, timeoutHelper.RemainingTime()))
4856 throw FxTrace.Exception.AsError(new TimeoutException(SR.TimeoutOnOperation(timeoutHelper.OriginalTimeout)));
4859 // We need to check this after the wait as well in
4860 // case the notification came in asynchronously
4861 if (this.IsInvokeCompleted)
4871 SendOrPostCallback callback;
4874 public WorkItem(SendOrPostCallback callback, object state)
4876 this.callback = callback;
4880 public void Invoke()
4882 this.callback(this.state);
4887 class WorkflowEventData
4889 public WorkflowEventData(WorkflowApplication instance)
4891 this.Instance = instance;
4894 public WorkflowApplication Instance
4900 public Func<IAsyncResult, WorkflowApplication, bool, bool> NextCallback
4906 public Exception UnhandledException
4912 public Activity UnhandledExceptionSource
4918 public string UnhandledExceptionSourceInstance
4925 class IdleEventHandler
4927 Func<IAsyncResult, WorkflowApplication, bool, bool> stage1Callback;
4928 Func<IAsyncResult, WorkflowApplication, bool, bool> stage2Callback;
4930 public IdleEventHandler()
4934 Func<IAsyncResult, WorkflowApplication, bool, bool> Stage1Callback
4938 if (this.stage1Callback == null)
4940 this.stage1Callback = new Func<IAsyncResult, WorkflowApplication, bool, bool>(OnStage1Complete);
4943 return this.stage1Callback;
4947 Func<IAsyncResult, WorkflowApplication, bool, bool> Stage2Callback
4951 if (this.stage2Callback == null)
4953 this.stage2Callback = new Func<IAsyncResult, WorkflowApplication, bool, bool>(OnStage2Complete);
4956 return this.stage2Callback;
4960 public bool Run(WorkflowApplication instance)
4962 IAsyncResult result = null;
4964 if (instance.Controller.TrackingEnabled)
4966 instance.Controller.Track(new WorkflowInstanceRecord(instance.Id, instance.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Idle, instance.DefinitionIdentity));
4968 instance.EventData.NextCallback = this.Stage1Callback;
4969 result = instance.Controller.BeginFlushTrackingRecords(ActivityDefaults.TrackingTimeout, EventFrameCallback, instance.EventData);
4971 if (!result.CompletedSynchronously)
4977 return OnStage1Complete(result, instance, true);
4980 bool OnStage1Complete(IAsyncResult lastResult, WorkflowApplication application, bool isStillSync)
4982 if (lastResult != null)
4984 application.Controller.EndFlushTrackingRecords(lastResult);
4987 IAsyncResult result = null;
4989 if (application.RaiseIdleEvent())
4991 if (application.Controller.IsPersistable && application.persistenceManager != null)
4993 Func<WorkflowApplicationIdleEventArgs, PersistableIdleAction> persistableIdleHandler = application.PersistableIdle;
4995 if (persistableIdleHandler != null)
4997 PersistableIdleAction action = PersistableIdleAction.None;
4999 application.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
5003 application.isInHandler = true;
5004 action = persistableIdleHandler(new WorkflowApplicationIdleEventArgs(application));
5008 application.isInHandler = false;
5011 if (TD.WorkflowApplicationPersistableIdleIsEnabled())
5013 TD.WorkflowApplicationPersistableIdle(application.Id.ToString(), action.ToString());
5016 if (action != PersistableIdleAction.None)
5018 PersistenceOperation operation = PersistenceOperation.Unload;
5020 if (action == PersistableIdleAction.Persist)
5022 operation = PersistenceOperation.Save;
5024 else if (action != PersistableIdleAction.Unload)
5026 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InvalidIdleAction));
5029 application.EventData.NextCallback = this.Stage2Callback;
5030 result = application.BeginInternalPersist(operation, ActivityDefaults.InternalSaveTimeout, true, EventFrameCallback, application.EventData);
5032 if (!result.CompletedSynchronously)
5040 // Trace the default action
5041 if (TD.WorkflowApplicationPersistableIdleIsEnabled())
5043 TD.WorkflowApplicationPersistableIdle(application.Id.ToString(), PersistableIdleAction.None.ToString());
5049 return OnStage2Complete(result, application, isStillSync);
5052 bool OnStage2Complete(IAsyncResult lastResult, WorkflowApplication instance, bool isStillSync)
5054 if (lastResult != null)
5056 instance.EndInternalPersist(lastResult);
5063 class CompletedEventHandler
5065 Func<IAsyncResult, WorkflowApplication, bool, bool> stage1Callback;
5066 Func<IAsyncResult, WorkflowApplication, bool, bool> stage2Callback;
5068 public CompletedEventHandler()
5072 Func<IAsyncResult, WorkflowApplication, bool, bool> Stage1Callback
5076 if (this.stage1Callback == null)
5078 this.stage1Callback = new Func<IAsyncResult, WorkflowApplication, bool, bool>(OnStage1Complete);
5081 return this.stage1Callback;
5085 Func<IAsyncResult, WorkflowApplication, bool, bool> Stage2Callback
5089 if (this.stage2Callback == null)
5091 this.stage2Callback = new Func<IAsyncResult, WorkflowApplication, bool, bool>(OnStage2Complete);
5094 return this.stage2Callback;
5098 public bool Run(WorkflowApplication instance)
5100 IAsyncResult result = null;
5101 if (instance.Controller.HasPendingTrackingRecords)
5103 instance.EventData.NextCallback = this.Stage1Callback;
5104 result = instance.Controller.BeginFlushTrackingRecords(ActivityDefaults.TrackingTimeout, EventFrameCallback, instance.EventData);
5106 if (!result.CompletedSynchronously)
5112 return OnStage1Complete(result, instance, true);
5115 bool OnStage1Complete(IAsyncResult lastResult, WorkflowApplication instance, bool isStillSync)
5117 if (lastResult != null)
5119 instance.Controller.EndFlushTrackingRecords(lastResult);
5122 IDictionary<string, object> outputs;
5123 Exception completionException;
5124 ActivityInstanceState completionState = instance.Controller.GetCompletionState(out outputs, out completionException);
5126 if (instance.invokeCompletedCallback == null)
5128 Action<WorkflowApplicationCompletedEventArgs> handler = instance.Completed;
5130 if (handler != null)
5132 instance.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
5136 instance.isInHandler = true;
5137 handler(new WorkflowApplicationCompletedEventArgs(instance, completionException, completionState, outputs));
5141 instance.isInHandler = false;
5146 switch (completionState)
5148 case ActivityInstanceState.Closed:
5149 if (TD.WorkflowApplicationCompletedIsEnabled())
5151 TD.WorkflowApplicationCompleted(instance.Id.ToString());
5154 case ActivityInstanceState.Canceled:
5155 if (TD.WorkflowInstanceCanceledIsEnabled())
5157 TD.WorkflowInstanceCanceled(instance.Id.ToString());
5160 case ActivityInstanceState.Faulted:
5161 if (TD.WorkflowApplicationTerminatedIsEnabled())
5163 TD.WorkflowApplicationTerminated(instance.Id.ToString(), completionException);
5168 IAsyncResult result = null;
5169 Fx.Assert(instance.Controller.IsPersistable, "Should not be in a No Persist Zone once the instance is complete.");
5170 if (instance.persistenceManager != null || instance.HasPersistenceModule)
5172 instance.EventData.NextCallback = this.Stage2Callback;
5173 result = instance.BeginInternalPersist(PersistenceOperation.Unload, ActivityDefaults.InternalSaveTimeout, true, EventFrameCallback, instance.EventData);
5175 if (!result.CompletedSynchronously)
5182 instance.MarkUnloaded();
5185 return OnStage2Complete(result, instance, isStillSync);
5188 bool OnStage2Complete(IAsyncResult lastResult, WorkflowApplication instance, bool isStillSync)
5190 if (lastResult != null)
5192 instance.EndInternalPersist(lastResult);
5195 if (instance.invokeCompletedCallback != null)
5197 instance.invokeCompletedCallback();
5204 class UnhandledExceptionEventHandler
5206 Func<IAsyncResult, WorkflowApplication, bool, bool> stage1Callback;
5208 public UnhandledExceptionEventHandler()
5212 Func<IAsyncResult, WorkflowApplication, bool, bool> Stage1Callback
5216 if (this.stage1Callback == null)
5218 this.stage1Callback = new Func<IAsyncResult, WorkflowApplication, bool, bool>(OnStage1Complete);
5221 return this.stage1Callback;
5225 public bool Run(WorkflowApplication instance, Exception exception, Activity exceptionSource, string exceptionSourceInstanceId)
5227 IAsyncResult result = null;
5229 if (instance.Controller.HasPendingTrackingRecords)
5231 instance.EventData.NextCallback = this.Stage1Callback;
5232 instance.EventData.UnhandledException = exception;
5233 instance.EventData.UnhandledExceptionSource = exceptionSource;
5234 instance.EventData.UnhandledExceptionSourceInstance = exceptionSourceInstanceId;
5235 result = instance.Controller.BeginFlushTrackingRecords(ActivityDefaults.TrackingTimeout, EventFrameCallback, instance.EventData);
5237 if (!result.CompletedSynchronously)
5243 return OnStage1Complete(result, instance, exception, exceptionSource, exceptionSourceInstanceId);
5246 bool OnStage1Complete(IAsyncResult lastResult, WorkflowApplication instance, bool isStillSync)
5248 return OnStage1Complete(lastResult, instance, instance.EventData.UnhandledException, instance.EventData.UnhandledExceptionSource, instance.EventData.UnhandledExceptionSourceInstance);
5251 bool OnStage1Complete(IAsyncResult lastResult, WorkflowApplication instance, Exception exception, Activity source, string sourceInstanceId)
5253 if (lastResult != null)
5255 instance.Controller.EndFlushTrackingRecords(lastResult);
5258 Func<WorkflowApplicationUnhandledExceptionEventArgs, UnhandledExceptionAction> handler = instance.OnUnhandledException;
5260 UnhandledExceptionAction action = UnhandledExceptionAction.Terminate;
5262 if (handler != null)
5266 instance.isInHandler = true;
5267 instance.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
5269 action = handler(new WorkflowApplicationUnhandledExceptionEventArgs(instance, exception, source, sourceInstanceId));
5273 instance.isInHandler = false;
5277 if (instance.invokeCompletedCallback != null)
5279 action = UnhandledExceptionAction.Terminate;
5282 if (TD.WorkflowApplicationUnhandledExceptionIsEnabled())
5284 TD.WorkflowApplicationUnhandledException(instance.Id.ToString(), source.GetType().ToString(), source.DisplayName, action.ToString(), exception);
5289 case UnhandledExceptionAction.Abort:
5290 instance.AbortInstance(exception, true);
5292 case UnhandledExceptionAction.Cancel:
5293 instance.Controller.ScheduleCancel();
5295 case UnhandledExceptionAction.Terminate:
5296 instance.TerminateCore(exception);
5299 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InvalidUnhandledExceptionAction));
5306 class InstanceCommandWithTemporaryHandleAsyncResult : TransactedAsyncResult
5308 static AsyncCompletion commandCompletedCallback = new AsyncCompletion(OnCommandCompleted);
5309 static Action<AsyncResult, Exception> completeCallback = new Action<AsyncResult, Exception>(OnComplete);
5311 InstancePersistenceCommand command;
5312 DependentTransaction dependentTransaction;
5313 InstanceStore instanceStore;
5314 InstanceHandle temporaryHandle;
5315 InstanceView commandResult;
5317 public InstanceCommandWithTemporaryHandleAsyncResult(InstanceStore instanceStore, InstancePersistenceCommand command,
5318 TimeSpan timeout, AsyncCallback callback, object state)
5319 : base(callback, state)
5321 this.instanceStore = instanceStore;
5322 this.command = command;
5323 this.temporaryHandle = instanceStore.CreateInstanceHandle();
5325 Transaction currentTransaction = Transaction.Current;
5326 if (currentTransaction != null)
5328 this.dependentTransaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
5331 OnCompleting = completeCallback;
5333 IAsyncResult result;
5334 using (this.PrepareTransactionalCall(this.dependentTransaction))
5336 result = instanceStore.BeginExecute(this.temporaryHandle, command, timeout, PrepareAsyncCompletion(commandCompletedCallback), this);
5339 if (SyncContinue(result))
5345 public static void End(IAsyncResult result, out InstanceStore instanceStore, out InstanceView commandResult)
5347 InstanceCommandWithTemporaryHandleAsyncResult thisPtr = AsyncResult.End<InstanceCommandWithTemporaryHandleAsyncResult>(result);
5348 instanceStore = thisPtr.instanceStore;
5349 commandResult = thisPtr.commandResult;
5352 static bool OnCommandCompleted(IAsyncResult result)
5354 InstanceCommandWithTemporaryHandleAsyncResult thisPtr = (InstanceCommandWithTemporaryHandleAsyncResult)result.AsyncState;
5355 thisPtr.commandResult = thisPtr.instanceStore.EndExecute(result);
5359 static void OnComplete(AsyncResult result, Exception exception)
5361 InstanceCommandWithTemporaryHandleAsyncResult thisPtr = (InstanceCommandWithTemporaryHandleAsyncResult)result;
5362 if (thisPtr.dependentTransaction != null)
5364 thisPtr.dependentTransaction.Complete();
5366 thisPtr.temporaryHandle.Free();
5370 class InstanceOperation
5372 AsyncWaitHandle waitHandle;
5374 public InstanceOperation()
5376 this.InterruptsScheduler = true;
5377 this.RequiresInitialized = true;
5380 public bool Notified
5392 public bool InterruptsScheduler
5398 public bool RequiresInitialized
5404 public void OnEnqueued()
5406 this.waitHandle = new AsyncWaitHandle();
5409 public virtual bool CanRun(WorkflowApplication instance)
5414 public void NotifyTurn()
5416 Fx.Assert(this.waitHandle != null, "We must have a wait handle.");
5421 public bool WaitForTurn(TimeSpan timeout)
5423 if (this.waitHandle != null)
5425 return this.waitHandle.Wait(timeout);
5431 public bool WaitForTurnAsync(TimeSpan timeout, Action<object, TimeoutException> callback, object state)
5433 if (this.waitHandle != null)
5435 return this.waitHandle.WaitAsync(callback, state, timeout);
5442 class RequiresIdleOperation : InstanceOperation
5444 bool requiresRunnableInstance;
5446 public RequiresIdleOperation()
5451 public RequiresIdleOperation(bool requiresRunnableInstance)
5453 this.InterruptsScheduler = false;
5454 this.requiresRunnableInstance = requiresRunnableInstance;
5457 public override bool CanRun(WorkflowApplication instance)
5459 if (requiresRunnableInstance && instance.state != WorkflowApplicationState.Runnable)
5464 return instance.Controller.State == WorkflowInstanceState.Idle || instance.Controller.State == WorkflowInstanceState.Complete;
5468 class DeferredRequiresIdleOperation : InstanceOperation
5470 public DeferredRequiresIdleOperation()
5472 this.InterruptsScheduler = false;
5475 public override bool CanRun(WorkflowApplication instance)
5477 return (this.ActionId != instance.actionCount && instance.Controller.State == WorkflowInstanceState.Idle) || instance.Controller.State == WorkflowInstanceState.Complete;
5481 class RequiresPersistenceOperation : InstanceOperation
5483 public override bool CanRun(WorkflowApplication instance)
5485 if (!instance.Controller.IsPersistable && instance.Controller.State != WorkflowInstanceState.Complete)
5487 instance.Controller.PauseWhenPersistable();
5497 class WaitForTurnData
5499 public WaitForTurnData(Action<object, TimeoutException> callback, object state, InstanceOperation operation, WorkflowApplication instance)
5501 this.Callback = callback;
5503 this.Operation = operation;
5504 this.Instance = instance;
5507 public Action<object, TimeoutException> Callback
5519 public InstanceOperation Operation
5525 public WorkflowApplication Instance
5532 // This is a thin shell of PersistenceManager functionality so that WorkflowApplicationInstance
5533 // can hold onto a PM without exposing the entire persistence functionality
5534 internal abstract class PersistenceManagerBase
5536 public abstract InstanceStore InstanceStore { get; }
5537 public abstract Guid InstanceId { get; }
5540 class PersistenceManager : PersistenceManagerBase
5542 InstanceHandle handle;
5543 InstanceHandle temporaryHandle;
5544 InstanceOwner owner;
5545 bool ownerWasCreated;
5550 InstanceStore store;
5551 // Initializing metadata, used when instance is created
5552 IDictionary<XName, InstanceValue> instanceMetadata;
5553 // Updateable metadata, used when instance is saved
5554 IDictionary<XName, InstanceValue> mutableMetadata;
5556 public PersistenceManager(InstanceStore store, IDictionary<XName, InstanceValue> instanceMetadata, Guid instanceId)
5558 Fx.Assert(store != null, "We should never gets here without a store.");
5560 this.instanceId = instanceId;
5561 this.instanceMetadata = instanceMetadata;
5563 InitializeInstanceMetadata();
5565 this.owner = store.DefaultInstanceOwner;
5566 if (this.owner != null)
5568 this.handle = store.CreateInstanceHandle(this.owner, instanceId);
5574 public PersistenceManager(InstanceStore store, IDictionary<XName, InstanceValue> instanceMetadata)
5576 Fx.Assert(store != null, "We should never get here without a store.");
5578 this.isTryLoad = true;
5579 this.instanceMetadata = instanceMetadata;
5581 InitializeInstanceMetadata();
5583 this.owner = store.DefaultInstanceOwner;
5584 if (this.owner != null)
5586 this.handle = store.CreateInstanceHandle(this.owner);
5592 public sealed override Guid InstanceId
5596 return this.instanceId;
5600 public sealed override InstanceStore InstanceStore
5608 public bool IsInitialized
5612 return (this.handle != null);
5616 public bool IsLocked
5620 return this.isLocked;
5624 public bool OwnerWasCreated
5628 return this.ownerWasCreated;
5632 void InitializeInstanceMetadata()
5634 if (this.instanceMetadata == null)
5636 this.instanceMetadata = new Dictionary<XName, InstanceValue>(1);
5639 // We always set this key explicitly so that users can't override
5640 // this metadata value
5641 this.instanceMetadata[PersistenceMetadataNamespace.InstanceType] = new InstanceValue(WorkflowNamespace.WorkflowHostType, InstanceValueOptions.WriteOnly);
5644 public void SetInstanceMetadata(IDictionary<XName, InstanceValue> metadata)
5646 Fx.Assert(this.instanceMetadata.Count == 1, "We should only have the default metadata from InitializeInstanceMetadata");
5647 if (metadata != null)
5649 this.instanceMetadata = metadata;
5650 InitializeInstanceMetadata();
5654 public void SetMutablemetadata(IDictionary<XName, InstanceValue> metadata)
5656 this.mutableMetadata = metadata;
5659 public void Initialize(WorkflowIdentity definitionIdentity, TimeSpan timeout)
5661 Fx.Assert(this.handle == null, "We are already initialized by now");
5663 using (new TransactionScope(TransactionScopeOption.Suppress))
5667 CreateTemporaryHandle(null);
5668 this.owner = this.store.Execute(this.temporaryHandle, GetCreateOwnerCommand(definitionIdentity), timeout).InstanceOwner;
5669 this.ownerWasCreated = true;
5673 FreeTemporaryHandle();
5676 this.handle = this.isTryLoad ? this.store.CreateInstanceHandle(this.owner) : this.store.CreateInstanceHandle(this.owner, InstanceId);
5678 Thread.MemoryBarrier();
5686 void CreateTemporaryHandle(InstanceOwner owner)
5688 this.temporaryHandle = this.store.CreateInstanceHandle(owner);
5690 Thread.MemoryBarrier();
5694 FreeTemporaryHandle();
5698 void FreeTemporaryHandle()
5700 InstanceHandle handle = this.temporaryHandle;
5708 public IAsyncResult BeginInitialize(WorkflowIdentity definitionIdentity, TimeSpan timeout, AsyncCallback callback, object state)
5710 Fx.Assert(this.handle == null, "We are already initialized by now");
5712 using (new TransactionScope(TransactionScopeOption.Suppress))
5714 IAsyncResult result = null;
5718 CreateTemporaryHandle(null);
5719 result = this.store.BeginExecute(this.temporaryHandle, GetCreateOwnerCommand(definitionIdentity), timeout, callback, state);
5723 // We've encountered an exception
5726 FreeTemporaryHandle();
5733 public void EndInitialize(IAsyncResult result)
5737 this.owner = this.store.EndExecute(result).InstanceOwner;
5738 this.ownerWasCreated = true;
5742 FreeTemporaryHandle();
5745 this.handle = this.isTryLoad ? this.store.CreateInstanceHandle(this.owner) : this.store.CreateInstanceHandle(this.owner, InstanceId);
5746 Thread.MemoryBarrier();
5753 public void DeleteOwner(TimeSpan timeout)
5757 CreateTemporaryHandle(this.owner);
5758 this.store.Execute(this.temporaryHandle, new DeleteWorkflowOwnerCommand(), timeout);
5760 // Ignore some exceptions because DeleteWorkflowOwner is best effort.
5761 catch (InstancePersistenceCommandException) { }
5762 catch (InstanceOwnerException) { }
5763 catch (OperationCanceledException) { }
5766 FreeTemporaryHandle();
5770 public IAsyncResult BeginDeleteOwner(TimeSpan timeout, AsyncCallback callback, object state)
5772 IAsyncResult result = null;
5775 CreateTemporaryHandle(this.owner);
5776 result = this.store.BeginExecute(this.temporaryHandle, new DeleteWorkflowOwnerCommand(), timeout, callback, state);
5778 // Ignore some exceptions because DeleteWorkflowOwner is best effort.
5779 catch (InstancePersistenceCommandException) { }
5780 catch (InstanceOwnerException) { }
5781 catch (OperationCanceledException) { }
5786 FreeTemporaryHandle();
5792 public void EndDeleteOwner(IAsyncResult result)
5796 this.store.EndExecute(result);
5798 // Ignore some exceptions because DeleteWorkflowOwner is best effort.
5799 catch (InstancePersistenceCommandException) { }
5800 catch (InstanceOwnerException) { }
5801 catch (OperationCanceledException) { }
5804 FreeTemporaryHandle();
5808 public void EnsureReadyness(TimeSpan timeout)
5810 Fx.Assert(this.handle != null, "We should already be initialized by now");
5811 Fx.Assert(!IsLocked, "We are already ready for persistence; why are we being called?");
5812 Fx.Assert(!this.isTryLoad, "Should not be on an initial save path if we tried load.");
5814 using (new TransactionScope(TransactionScopeOption.Suppress))
5816 this.store.Execute(this.handle, CreateSaveCommand(null, this.instanceMetadata, PersistenceOperation.Save), timeout);
5817 this.isLocked = true;
5821 public IAsyncResult BeginEnsureReadyness(TimeSpan timeout, AsyncCallback callback, object state)
5823 Fx.Assert(this.handle != null, "We should already be initialized by now");
5824 Fx.Assert(!IsLocked, "We are already ready for persistence; why are we being called?");
5825 Fx.Assert(!this.isTryLoad, "Should not be on an initial save path if we tried load.");
5827 using (new TransactionScope(TransactionScopeOption.Suppress))
5829 return this.store.BeginExecute(this.handle, CreateSaveCommand(null, this.instanceMetadata, PersistenceOperation.Save), timeout, callback, state);
5833 public void EndEnsureReadyness(IAsyncResult result)
5835 this.store.EndExecute(result);
5836 this.isLocked = true;
5839 public static Dictionary<XName, InstanceValue> GenerateInitialData(WorkflowApplication instance)
5841 Dictionary<XName, InstanceValue> data = new Dictionary<XName, InstanceValue>(10);
5842 data[WorkflowNamespace.Bookmarks] = new InstanceValue(instance.Controller.GetBookmarks(), InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
5843 data[WorkflowNamespace.LastUpdate] = new InstanceValue(DateTime.UtcNow, InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
5845 foreach (KeyValuePair<string, LocationInfo> mappedVariable in instance.Controller.GetMappedVariables())
5847 data[WorkflowNamespace.VariablesPath.GetName(mappedVariable.Key)] = new InstanceValue(mappedVariable.Value, InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
5850 Fx.AssertAndThrow(instance.Controller.State != WorkflowInstanceState.Aborted, "Cannot generate data for an aborted instance.");
5851 if (instance.Controller.State != WorkflowInstanceState.Complete)
5853 data[WorkflowNamespace.Workflow] = new InstanceValue(instance.Controller.PrepareForSerialization());
5854 data[WorkflowNamespace.Status] = new InstanceValue(instance.Controller.State == WorkflowInstanceState.Idle ? "Idle" : "Executing", InstanceValueOptions.WriteOnly);
5858 data[WorkflowNamespace.Workflow] = new InstanceValue(instance.Controller.PrepareForSerialization(), InstanceValueOptions.Optional);
5860 Exception completionException;
5861 IDictionary<string, object> outputs;
5862 ActivityInstanceState completionState = instance.Controller.GetCompletionState(out outputs, out completionException);
5864 if (completionState == ActivityInstanceState.Faulted)
5866 data[WorkflowNamespace.Status] = new InstanceValue("Faulted", InstanceValueOptions.WriteOnly);
5867 data[WorkflowNamespace.Exception] = new InstanceValue(completionException, InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
5869 else if (completionState == ActivityInstanceState.Closed)
5871 data[WorkflowNamespace.Status] = new InstanceValue("Closed", InstanceValueOptions.WriteOnly);
5872 if (outputs != null)
5874 foreach (KeyValuePair<string, object> output in outputs)
5876 data[WorkflowNamespace.OutputPath.GetName(output.Key)] = new InstanceValue(output.Value, InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
5882 Fx.AssertAndThrow(completionState == ActivityInstanceState.Canceled, "Cannot be executing when WorkflowState was completed.");
5883 data[WorkflowNamespace.Status] = new InstanceValue("Canceled", InstanceValueOptions.WriteOnly);
5889 static InstancePersistenceCommand GetCreateOwnerCommand(WorkflowIdentity definitionIdentity)
5891 // Technically, we only need to pass the owner identity when doing LoadRunnable.
5892 // However, if we create an instance with identity on a store that doesn't recognize it,
5893 // the identity metadata might be stored in a way which makes it unqueryable if the store
5894 // is later upgraded to support identity (e.g. SWIS 4.0 -> 4.5 upgrade). So to be on the
5895 // safe side, if we're using identity, we require the store to explicitly support it.
5896 if (definitionIdentity != null)
5898 CreateWorkflowOwnerWithIdentityCommand result = new CreateWorkflowOwnerWithIdentityCommand();
5899 if (!object.ReferenceEquals(definitionIdentity, WorkflowApplication.unknownIdentity))
5901 result.InstanceOwnerMetadata.Add(Workflow45Namespace.DefinitionIdentities,
5902 new InstanceValue(new Collection<WorkflowIdentity> { definitionIdentity }));
5908 return new CreateWorkflowOwnerCommand();
5912 static SaveWorkflowCommand CreateSaveCommand(IDictionary<XName, InstanceValue> instance, IDictionary<XName, InstanceValue> instanceMetadata, PersistenceOperation operation)
5914 SaveWorkflowCommand saveCommand = new SaveWorkflowCommand()
5916 CompleteInstance = operation == PersistenceOperation.Complete,
5917 UnlockInstance = operation != PersistenceOperation.Save,
5920 if (instance != null)
5922 foreach (KeyValuePair<XName, InstanceValue> value in instance)
5924 saveCommand.InstanceData.Add(value);
5928 if (instanceMetadata != null)
5930 foreach (KeyValuePair<XName, InstanceValue> value in instanceMetadata)
5932 saveCommand.InstanceMetadataChanges.Add(value);
5939 bool TryLoadHelper(InstanceView view, out IDictionary<XName, InstanceValue> data)
5941 if (!view.IsBoundToLock)
5946 this.instanceId = view.InstanceId;
5947 this.isLocked = true;
5949 if (!this.handle.IsValid)
5951 throw FxTrace.Exception.AsError(new OperationCanceledException(SR.WorkflowInstanceAborted(InstanceId)));
5954 data = view.InstanceData;
5958 public void Save(IDictionary<XName, InstanceValue> instance, PersistenceOperation operation, TimeSpan timeout)
5960 this.store.Execute(this.handle, CreateSaveCommand(instance, (this.isLocked ? this.mutableMetadata : this.instanceMetadata), operation), timeout);
5961 this.isLocked = true;
5964 public IDictionary<XName, InstanceValue> Load(TimeSpan timeout)
5966 InstanceView view = this.store.Execute(this.handle, new LoadWorkflowCommand(), timeout);
5967 this.isLocked = true;
5969 if (!this.handle.IsValid)
5971 throw FxTrace.Exception.AsError(new OperationCanceledException(SR.WorkflowInstanceAborted(InstanceId)));
5974 return view.InstanceData;
5977 public bool TryLoad(TimeSpan timeout, out IDictionary<XName, InstanceValue> data)
5979 InstanceView view = this.store.Execute(this.handle, new TryLoadRunnableWorkflowCommand(), timeout);
5980 return TryLoadHelper(view, out data);
5983 public IAsyncResult BeginSave(IDictionary<XName, InstanceValue> instance, PersistenceOperation operation, TimeSpan timeout, AsyncCallback callback, object state)
5985 return this.store.BeginExecute(this.handle, CreateSaveCommand(instance, (this.isLocked ? this.mutableMetadata : this.instanceMetadata), operation), timeout, callback, state);
5988 public void EndSave(IAsyncResult result)
5990 this.store.EndExecute(result);
5991 this.isLocked = true;
5994 public IAsyncResult BeginLoad(TimeSpan timeout, AsyncCallback callback, object state)
5996 return this.store.BeginExecute(this.handle, new LoadWorkflowCommand(), timeout, callback, state);
5999 public IDictionary<XName, InstanceValue> EndLoad(IAsyncResult result)
6001 InstanceView view = this.store.EndExecute(result);
6002 this.isLocked = true;
6004 if (!this.handle.IsValid)
6006 throw FxTrace.Exception.AsError(new OperationCanceledException(SR.WorkflowInstanceAborted(InstanceId)));
6009 return view.InstanceData;
6012 public IAsyncResult BeginTryLoad(TimeSpan timeout, AsyncCallback callback, object state)
6014 return this.store.BeginExecute(this.handle, new TryLoadRunnableWorkflowCommand(), timeout, callback, state);
6017 public bool EndTryLoad(IAsyncResult result, out IDictionary<XName, InstanceValue> data)
6019 InstanceView view = this.store.EndExecute(result);
6020 return TryLoadHelper(view, out data);
6025 this.aborted = true;
6027 // Make sure the setter of handle sees aborted, or v.v., or both.
6028 Thread.MemoryBarrier();
6030 InstanceHandle handle = this.handle;
6036 FreeTemporaryHandle();
6039 public void Unlock(TimeSpan timeout)
6041 SaveWorkflowCommand saveCmd = new SaveWorkflowCommand()
6043 UnlockInstance = true,
6046 this.store.Execute(this.handle, saveCmd, timeout);
6049 public IAsyncResult BeginUnlock(TimeSpan timeout, AsyncCallback callback, object state)
6051 SaveWorkflowCommand saveCmd = new SaveWorkflowCommand()
6053 UnlockInstance = true,
6056 return this.store.BeginExecute(this.handle, saveCmd, timeout, callback, state);
6059 public void EndUnlock(IAsyncResult result)
6061 this.store.EndExecute(result);