1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
5 namespace System.ServiceModel.Activities.Dispatcher
8 using System.Activities;
9 using System.Activities.DynamicUpdate;
10 using System.Activities.Hosting;
11 using System.Activities.Tracking;
12 using System.Collections.Generic;
13 using System.Collections.ObjectModel;
14 using System.Diagnostics;
15 using System.Diagnostics.CodeAnalysis;
16 using System.Globalization;
19 using System.Runtime.Interop;
20 using System.Runtime.DurableInstancing;
21 using System.Security;
22 using System.Security.Permissions;
23 using System.ServiceModel.Activation;
24 using System.ServiceModel.Activities;
25 using System.ServiceModel.Activities.Description;
26 using System.ServiceModel.Activities.Diagnostics;
27 using System.Threading;
28 using System.Transactions;
29 using System.Xml.Linq;
31 // WorkflowServiceInstance is free-threaded. It is responsible for the correct locking and usage of the underlying WorkflowInstance.
32 // Given that there are two simultaneous users of WorkflowInstance (WorkflowServiceInstance and Activities),
33 // it is imperative that WorkflowServiceInstance only calls into WorkflowInstance when there are no activities executing
34 // (and thus no worries about colliding with AEC calls).
36 // LOCKING SCHEME DESCRIPTION
37 // AcquireLock* - These are the only locks that should call Enter on the WorkflowExecutionLock.
38 // ReleaseLock - This is the only method that should call Exit on the WorkflowExecutionLock.
39 // Lock Handoff - The lock is often handed off from one thread to another. This is handled by
40 // WorkflowExecutionLock itself. If there is a waiter (someone called Enter) then the Exit call
41 // will simply notify the first waiter. The waiter is now responsible for the lock.
42 // NOTE: There is a small period of time where no one things they own the lock. Exit has "handed
43 // off the lock by calling Set on the waiter, but the waiter has not yet executed the code
44 // which sets ownsLock to true.
45 // Sync Handoff - During sync handoff the ref bool ownsLock will be set accordingly by the
46 // Acquire* method. These methods should always be called in a try block with a finally
47 // which calls ReleaseLock.
48 // Async Handoff - During async handoff the callback can assume it has the lock if either
49 // there was no exception (FastAsyncCallback) or the call to End sets the ref bool ownsLock
50 // to true. Note that in cases of async handoff there should always be a guarding ReleaseLock
51 // which releases the lock if the async call does not state that it has gone async.
52 // Scheduler Interactions - The scheduler's state MUST ONLY be changed with the activeOperationsLock
53 // held. This is to guarantee that a Pause (Acquire) is not clobbered by a concurrently executing
54 // Resume (Release) resulting in an instance operation which times out when it shouldn't have.
55 // ActiveOperations RefCount - The activeOperations ref count MUST be incremented before calling
56 // any of the Enter variations and must be decremented after leaving the Enter. ActiveOperations
57 // is how ReleaseLock determines whether to hand the lock off to a waiting operation or to continue
58 // execution workflow when the workflow is in a runnable state.
59 // Future Innovation - If necessary we can consider iterating on the current code to provide
60 // better guarantees around async handoff. For example, at the risk of starvation we could
61 // actually exit the lock before notifying waiters rather than doing a direct handoff.
62 [Fx.Tag.XamlVisible(false)]
63 class WorkflowServiceInstance : WorkflowInstance
65 static AsyncCallback handleEndReleaseInstance;
66 static FastAsyncCallback lockAcquiredAsyncCallback = new FastAsyncCallback(OnLockAcquiredAsync);
67 static AsyncCallback trackCompleteDoneCallback;
68 static AsyncCallback trackIdleDoneCallback;
69 static AsyncCallback trackUnhandledExceptionDoneCallback;
70 static ReadOnlyCollection<BookmarkInfo> emptyBookmarkInfoCollection = new ReadOnlyCollection<BookmarkInfo>(new List<BookmarkInfo>());
72 WorkflowExecutionLock executorLock;
74 PersistenceContext persistenceContext;
75 PersistencePipeline persistencePipelineInUse;
76 bool abortingExtensions;
79 object activeOperationsLock;
82 List<AsyncWaitHandle> idleWaiters;
83 List<AsyncWaitHandle> nextIdleWaiters;
84 List<WaitForCanPersistAsyncResult> checkCanPersistWaiters;
86 // Used for synchronizing ResumeBookmark calls on the the load path from extensions (e.g DurableTimerExtension)
87 AsyncWaitHandle workflowServiceInstanceReadyWaitHandle;
88 bool isWorkflowServiceInstanceReady;
90 // Tracking for one-time actions per instance lifetime (these end up being persisted)
91 bool hasRaisedCompleted;
92 bool hasPersistedDeleted;
95 BufferedReceiveManager bufferedReceiveManager;
98 TransactionContext transactionContext;
100 bool isTransactedCancelled;
101 Dictionary<string, List<PendingOperationAsyncResult>> pendingOperations;
102 int pendingOperationCount;
105 // Used for synchronizing unload with persist
106 // This is to mark that the instance has made progress but has not been persisted by idle policy yet
107 bool hasDataToPersist;
109 // tracks the outstanding requests. This contributes to idle calculations, and the list is notified
110 // if workflow completes in any way (including unhandled exception)
111 List<WorkflowOperationContext> pendingRequests;
114 UnloadInstancePolicyHelper unloadInstancePolicy;
115 UnhandledExceptionPolicyHelper unhandledExceptionPolicy;
117 ThreadNeutralSemaphore acquireReferenceSemaphore;
119 WorkflowServiceHost serviceHost;
120 WorkflowCreationContext creationContext;
121 bool creationContextAborted;
122 IDictionary<string, object> workflowOutputs;
123 Exception terminationException;
124 ActivityInstanceState completionState;
125 TimeSpan persistTimeout;
126 TimeSpan trackTimeout;
127 TimeSpan acquireLockTimeout;
129 //Tracking for increment of ASP.NET busy count
130 bool hasIncrementedBusyCount;
132 // dummy ctor only used to calculate IsLoadTransactionRequired
133 WorkflowServiceInstance(WorkflowServiceHost serviceHost)
134 : base(serviceHost.Activity)
138 WorkflowServiceInstance(Activity workflowDefinition, WorkflowIdentity definitionIdentity, Guid instanceId, WorkflowServiceHost serviceHost, PersistenceContext persistenceContext)
139 : base(workflowDefinition, definitionIdentity)
141 this.serviceHost = serviceHost;
142 this.instanceId = instanceId;
143 this.persistTimeout = serviceHost.PersistTimeout;
144 this.trackTimeout = serviceHost.TrackTimeout;
145 this.bufferedReceiveManager = serviceHost.Extensions.Find<BufferedReceiveManager>();
147 if (persistenceContext != null)
149 this.persistenceContext = persistenceContext;
150 this.persistenceContext.Closed += this.OnPersistenceContextClosed;
153 this.thisLock = new object();
154 this.pendingRequests = new List<WorkflowOperationContext>();
155 this.executorLock = new WorkflowExecutionLock(this);
156 this.activeOperationsLock = new object();
157 this.acquireReferenceSemaphore = new ThreadNeutralSemaphore(1);
158 this.acquireLockTimeout = TimeSpan.MaxValue;
160 // Two initial references are held:
161 // The first referenceCount is owned by UnloadInstancePolicy (ReleaseInstance)
162 this.referenceCount = 1;
163 // The second referenceCount is owned by the loader / creator of the instance.
164 this.TryAddReference();
167 static AsyncCallback TrackIdleDoneCallback
171 if (trackIdleDoneCallback == null)
173 trackIdleDoneCallback = Fx.ThunkCallback(new AsyncCallback(OnTrackIdleDone));
176 return trackIdleDoneCallback;
180 static AsyncCallback TrackUnhandledExceptionDoneCallback
184 if (trackUnhandledExceptionDoneCallback == null)
186 trackUnhandledExceptionDoneCallback = Fx.ThunkCallback(new AsyncCallback(OnTrackUnhandledExceptionDone));
189 return trackUnhandledExceptionDoneCallback;
193 static AsyncCallback TrackCompleteDoneCallback
197 if (trackCompleteDoneCallback == null)
199 trackCompleteDoneCallback = Fx.ThunkCallback(new AsyncCallback(OnTrackCompleteDone));
202 return trackCompleteDoneCallback;
206 // cache the results for perf from the extension container
207 internal List<IPersistencePipelineModule> PipelineModules
213 public BufferedReceiveManager BufferedReceiveManager
217 return this.bufferedReceiveManager;
221 public override Guid Id
225 return this.instanceId;
233 return this.state == State.Active;
237 public bool HasBeenUpdated
243 protected override bool SupportsInstanceKeys
255 return this.Controller.State == WorkflowInstanceState.Idle;
259 bool ShouldRaiseComplete
263 return this.Controller.State == WorkflowInstanceState.Complete && !this.hasRaisedCompleted;
271 return this.IsIdle && !this.hasRaisedCompleted && this.state != State.Aborted;
279 return this.isInHandler && this.handlerThreadId == Thread.CurrentThread.ManagedThreadId;
283 UnloadInstancePolicyHelper UnloadInstancePolicy
287 if (this.unloadInstancePolicy == null)
289 this.unloadInstancePolicy = new UnloadInstancePolicyHelper(this, this.serviceHost.IdleTimeToPersist, this.serviceHost.IdleTimeToUnload);
291 return this.unloadInstancePolicy;
295 UnhandledExceptionPolicyHelper UnhandledExceptionPolicy
299 if (this.unhandledExceptionPolicy == null)
301 this.unhandledExceptionPolicy = new UnhandledExceptionPolicyHelper(this, this.serviceHost.UnhandledExceptionAction);
303 return this.unhandledExceptionPolicy;
307 // create a dummy instance to configure extensions and determine if a load-time transaction is required
308 public static bool IsLoadTransactionRequired(WorkflowServiceHost host)
310 WorkflowServiceInstance instance = new WorkflowServiceInstance(host);
311 instance.RegisterExtensionManager(host.WorkflowExtensions);
312 return instance.GetExtensions<IPersistencePipelineModule>().Any(module => module.IsLoadTransactionRequired);
315 public static WorkflowServiceInstance InitializeInstance(PersistenceContext persistenceContext, Guid instanceId, Activity workflowDefinition, WorkflowIdentity definitionIdentity, IDictionary<XName, InstanceValue> loadedObject, WorkflowCreationContext creationContext,
316 SynchronizationContext synchronizationContext, WorkflowServiceHost serviceHost, DynamicUpdateMap updateMap = null)
318 Fx.Assert(workflowDefinition != null, "workflowDefinition cannot be null.");
319 Fx.Assert(serviceHost != null, "serviceHost cannot be null!");
320 Fx.Assert(instanceId != Guid.Empty, "instanceId cannot be empty.");
322 WorkflowServiceInstance workflowInstance = new WorkflowServiceInstance(workflowDefinition, definitionIdentity, instanceId, serviceHost, persistenceContext)
324 SynchronizationContext = synchronizationContext
327 // let us initalize the instance level extensions here
328 workflowInstance.SetupExtensions(serviceHost.WorkflowExtensions);
330 if (loadedObject != null)
332 InstanceValue stateValue;
333 object deserializedRuntimeState;
335 if (!loadedObject.TryGetValue(WorkflowNamespace.Workflow, out stateValue) || stateValue.Value == null)
337 throw FxTrace.Exception.AsError(
338 new InstancePersistenceException(SR.WorkflowInstanceNotFoundInStore(instanceId)));
340 deserializedRuntimeState = stateValue.Value;
342 if (loadedObject.TryGetValue(WorkflowServiceNamespace.CreationContext, out stateValue))
344 workflowInstance.creationContext = (WorkflowCreationContext)stateValue.Value;
347 if (persistenceContext.IsSuspended)
349 workflowInstance.state = State.Suspended;
353 workflowInstance.Initialize(deserializedRuntimeState, updateMap);
355 catch (InstanceUpdateException)
357 // Need to flush the tracking record for the update failure
358 workflowInstance.ScheduleAbortTracking(true);
362 if (updateMap != null)
364 workflowInstance.HasBeenUpdated = true;
369 IList<Handle> rootExecutionProperties = null;
370 IDictionary<string, object> workflowArguments = null;
371 // Provide default CorrelationScope if root activity is not CorrelationScope
372 if (!(workflowDefinition is CorrelationScope))
374 rootExecutionProperties = new List<Handle>(1)
376 new CorrelationHandle()
380 if (creationContext != null)
382 workflowArguments = creationContext.RawWorkflowArguments;
383 workflowInstance.creationContext = creationContext;
385 workflowInstance.Initialize(workflowArguments, rootExecutionProperties);
388 return workflowInstance;
391 void SetupExtensions(WorkflowInstanceExtensionManager extensionManager)
393 base.RegisterExtensionManager(extensionManager);
395 // cache IPersistencePipelineModules
396 IEnumerable<IPersistencePipelineModule> modules = base.GetExtensions<IPersistencePipelineModule>();
397 int modulesCount = modules.Count<IPersistencePipelineModule>();
398 if (modulesCount > 0)
400 this.PipelineModules = new List<IPersistencePipelineModule>(modulesCount);
401 this.PipelineModules.AddRange(modules);
405 void OnPersistenceContextClosed(object sender, EventArgs e)
407 if (this.persistenceContext.Aborted && !this.abortingExtensions)
409 AbortInstance(new FaultException(OperationExecutionFault.CreateAbortedFault(SR.DefaultAbortReason)), false);
413 // Call when GetInstance to perform operation
414 bool TryAddReference()
416 bool success = false;
419 if (this.referenceCount > 0)
421 ++this.referenceCount;
427 this.UnloadInstancePolicy.Cancel();
432 // Called by unload via unload policy
433 bool TryReleaseLastReference()
437 if (this.referenceCount == 1)
439 this.referenceCount = 0;
446 // Called when terminating ongoing unload
447 void RecoverLastReference()
451 Fx.Assert(this.referenceCount == 0, "referenceCount must be 0 during unload");
452 this.referenceCount = 1;
456 // Release after operation done
457 public int ReleaseReference()
462 Fx.AssertAndThrow(this.referenceCount > 1, "referenceCount must be greater than 1");
463 refCount = --this.referenceCount;
465 StartUnloadInstancePolicyIfNecessary();
469 void StartUnloadInstancePolicyIfNecessary()
471 // The conditions to start unload policy.
472 // - referenceCount is 1. Like COM, This is the last reference count hold by WorkflowServiceInstance itself.
473 // It is incremented per command (control/resumebookmark) and decremented when command is done.
474 // - No lock pending. In general, when referenceCount is 1, the executor lock is freed and WF is idled.
475 // There is, however, one narrow case for Persist activity. When it goes async (executing Sql command),
476 // the referenceCount is decremented to 1 but WF sheduler still busy. In this case, we will let
477 // the lock release to initiate the policy.
478 // - Not in transaction (TxCommit will take care of this).
479 // - Must not be in completed or unloaded or aborted states.
480 // Note: it is okay to dirty read referenceCount and isLocked. If the UnloadInstancePolicy starts before
481 // increment, the increment will correct and cancel it. If the increment happens before, ReleaseReference
482 // will have a chance to start the policy. Same applies to isLocked.
483 if (this.referenceCount == 1 && !this.executorLock.IsLocked && !this.isInTransaction &&
484 this.state != State.Completed && this.state != State.Unloaded && this.state != State.Aborted)
486 this.UnloadInstancePolicy.Begin();
490 void AcquireLock(TimeSpan timeout, ref bool ownsLock)
492 Fx.Assert(!ownsLock, "We should never call acquire if we already think we own the lock.");
494 if (this.IsHandlerThread)
496 // We're in a handler, on the handler thread, and doing work synchronously so we already have the lock
500 if (!this.executorLock.TryEnter(ref ownsLock))
502 Fx.Assert(!ownsLock, "This should always match the return of TryEnter and is only useful in light of exceptions");
504 bool incrementedActiveOperations = false;
505 object lockToken = null;
509 lock (this.activeOperationsLock)
516 this.activeOperations++;
517 incrementedActiveOperations = true;
520 // An exception occuring before we call PauseScheduler causes no issues/----s since
521 // we'll just cleanup activeOperations and be in the same state as when AcquireLock
524 this.Controller.RequestPause();
526 this.executorLock.SetupWaiter(ref lockToken);
529 // There is a ---- here which is solved by code in ReleaseLock. In short, if we fail
530 // to acquire the lock here but before we decrement activeOperations the workflow pauses
531 // then nothing will ever restart the workflow. To that end, ReleaseLock does some
532 // special handling when it exits the lock and no one is waiting.
534 this.executorLock.Enter(timeout, ref lockToken, ref ownsLock);
538 if (incrementedActiveOperations)
540 lock (this.activeOperationsLock)
542 this.activeOperations--;
546 this.executorLock.CleanupWaiter(lockToken, ref ownsLock);
551 bool AcquireLockAsync(TimeSpan timeout, ref bool ownsLock, FastAsyncCallback callback, object state)
553 return AcquireLockAsync(timeout, false, false, ref ownsLock, callback, state);
556 bool AcquireLockAsync(TimeSpan timeout, bool isAbortPriority, bool skipPause, ref bool ownsLock, FastAsyncCallback callback, object state)
558 Fx.Assert(!ownsLock, "We should never call acquire if we already think we own the lock.");
560 // We cannot just hand off the lock if we are in a handler thread
561 // because this might eventually go async (during the operation)
562 // and we could have multiple operations occurring concurrently.
564 if (!this.executorLock.TryEnter(ref ownsLock))
566 Fx.Assert(!ownsLock, "This should always match the return of TryEnter and is only useful in light of exceptions");
568 bool incrementedActiveOperations = false;
569 bool decrementActiveOperations = true;
570 object lockToken = null;
574 lock (this.activeOperationsLock)
581 this.activeOperations++;
582 incrementedActiveOperations = true;
585 // An exception occuring before we call PauseScheduler causes no issues/----s since
586 // we'll just cleanup activeOperations and be in the same state as when AcquireLock
591 this.Controller.RequestPause();
594 this.executorLock.SetupWaiter(isAbortPriority, ref lockToken);
597 // If we get the lock here then we should decrement, otherwise
598 // it is up to the lock acquired callback
599 decrementActiveOperations = this.executorLock.EnterAsync(timeout, ref lockToken, ref ownsLock, lockAcquiredAsyncCallback, new AcquireLockAsyncData(this, callback, state));
600 return decrementActiveOperations;
604 if (incrementedActiveOperations && decrementActiveOperations)
606 lock (this.activeOperationsLock)
608 this.activeOperations--;
612 this.executorLock.CleanupWaiter(lockToken, ref ownsLock);
621 static void OnLockAcquiredAsync(object state, Exception asyncException)
623 AcquireLockAsyncData data = (AcquireLockAsyncData)state;
625 lock (data.Instance.activeOperationsLock)
627 data.Instance.activeOperations--;
630 data.Callback(data.State, asyncException);
633 AsyncWaitHandle SetupIdleWaiter(ref bool ownsLock)
635 AsyncWaitHandle idleEvent = new AsyncWaitHandle(EventResetMode.ManualReset);
637 lock (this.activeOperationsLock)
639 if (this.idleWaiters == null)
641 this.idleWaiters = new List<AsyncWaitHandle>();
644 this.idleWaiters.Add(idleEvent);
647 ReleaseLock(ref ownsLock);
652 bool CleanupIdleWaiter(AsyncWaitHandle idleEvent, Exception waitException, ref bool ownsLock)
654 lock (this.activeOperationsLock)
656 if (!this.idleWaiters.Remove(idleEvent))
658 // If it wasn't in the list that means we raced between throwing from Wait
659 // and setting the event. This thread now is responsible for the lock.
660 if (waitException is TimeoutException)
662 // In the case of Timeout we let setting the event win and signal to
663 // swallow the exception
674 // Called with the executor lock
675 // Returns true if someone was notified (this thread no longer owns the lock) or false if
676 // no one was notified.
677 bool NotifyNextIdleWaiter(ref bool ownsLock)
679 // If we are no longer active, flush all idle waiters (next + current) because we will
680 // not enter Idle state again. For Suspended, even we could ---- to unsuspend and become idle,
681 // the desirable behavior while suspending is to reject pending as well as new requests.
682 if (this.state != State.Active)
684 PrepareNextIdleWaiter();
687 if (this.idleWaiters != null && this.idleWaiters.Count > 0)
689 // We need to be careful about setting this event because if there is an async
690 // waiter then this thread will be used for some execution. Therefore we shouldn't
691 // call set with the activeOperationsLock held.
692 AsyncWaitHandle idleEvent = null;
694 // We need to lock this because a waiter might have timed out (or thrown another exception) and
695 // could be trying to remove itself from the list without the executor lock.
696 lock (this.activeOperationsLock)
698 if (this.idleWaiters.Count > 0)
700 idleEvent = this.idleWaiters[0];
701 this.idleWaiters.RemoveAt(0);
705 if (idleEvent != null)
716 void PrepareNextIdleWaiter()
718 if (this.nextIdleWaiters != null && this.nextIdleWaiters.Count > 0)
720 lock (this.activeOperationsLock)
722 if (this.idleWaiters == null)
724 this.idleWaiters = new List<AsyncWaitHandle>();
727 for (int i = 0; i < this.nextIdleWaiters.Count; i++)
729 this.idleWaiters.Add(this.nextIdleWaiters[i]);
733 this.nextIdleWaiters.Clear();
737 IAsyncResult BeginAcquireLockOnIdle(TimeSpan timeout, ref bool ownsLock, AsyncCallback callback, object state)
739 return new AcquireLockOnIdleAsyncResult(this, timeout, ref ownsLock, callback, state);
742 void EndAcquireLockOnIdle(IAsyncResult result)
744 Fx.Assert(result.CompletedSynchronously, "This overload should only be called when completed synchronously.");
745 AcquireLockOnIdleAsyncResult.End(result);
748 void EndAcquireLockOnIdle(IAsyncResult result, ref bool ownsLock)
750 Fx.Assert(!result.CompletedSynchronously, "This overload should only be called when completed asynchronously.");
751 AcquireLockOnIdleAsyncResult.End(result, ref ownsLock);
754 void ReleaseLock(ref bool ownsLock)
756 ReleaseLock(ref ownsLock, false);
759 void ReleaseLock(ref bool ownsLock, bool hasBeenPersistedByIdlePolicy)
761 // The hasBeenPersistedByIdlePolicy flag is only true when this is part of the idle policy initiated persist.
768 Fx.Assert(!this.IsHandlerThread, "We never set ownsLock if we are on the handler thread and therefore should have shortcut out earlier.");
770 bool resumeScheduler = false;
772 bool needToSignalWorkflowServiceInstanceReadyWaitHandle = false;
775 this.isWorkflowServiceInstanceReady = true;
776 if (this.workflowServiceInstanceReadyWaitHandle != null)
778 needToSignalWorkflowServiceInstanceReadyWaitHandle = true;
781 // Signal that workflow has made progress and this progress has not been persisted by idle policy,
782 // we need to supress the abort initiated by unload when TimeToPersist < TimeToUnload.
783 // If ReleaseLock is done by anyone other than idle policy persist, we mark the instance dirty.
784 // Conversely, if idle policy completed a persist, we mark the instance clean.
785 this.hasDataToPersist = !hasBeenPersistedByIdlePolicy;
788 if (needToSignalWorkflowServiceInstanceReadyWaitHandle)
790 this.workflowServiceInstanceReadyWaitHandle.Set();
793 lock (this.activeOperationsLock)
795 // We don't check for completion here because we need to make sure we always
796 // drain the scheduler queue. Note that the OnIdle handler only raises events
797 // if the workflow is truly idle. Therefore, if we are completed but not idle
798 // then we won't raise the events.
799 // Terminate capitalizes on this by assuring that there is at least one more
800 // work item in the queue. This provides a simple mechanism for getting a
801 // scheduler thread to raise the completed event.
802 bool isRunnable = this.state == State.Active && this.isRunnable && !this.IsIdle;
803 if (isRunnable && this.activeOperations == 0)
806 resumeScheduler = true;
808 else if ((this.IsIdle || this.state != State.Active) && NotifyNextIdleWaiter(ref ownsLock))
813 // If we are runnable then we want to hang onto the lock if Exit finds no one waiting.
814 if (!this.executorLock.Exit(isRunnable, ref ownsLock))
816 // No one was waiting, but we had activeOperations (otherwise we would not have gotten
817 // to this branch of the if). This means that we raced with a timeout and should resume
818 // the workflow's execution. If we don't resume execution we'll just hang ... no one
819 // has the lock, the workflow is ready to execute, but it is not.
820 Fx.Assert(this.activeOperations > 0, "We should always have active operations otherwise we should have taken a different code path.");
822 // We no longer "own" the lock because the scheduler has taken control
825 resumeScheduler = true;
832 IncrementBusyCount();
833 this.persistenceContext.Bookmarks = null;
834 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowExecuting(true);
835 if (this.Controller.State == WorkflowInstanceState.Complete)
841 this.Controller.Run();
847 public IAsyncResult BeginAbandon(Exception reason, TimeSpan timeout, AsyncCallback callback, object state)
849 Fx.Assert(reason != null, "reason must not be null!");
850 return BeginAbandon(reason, true, timeout, callback, state);
853 //used by UnloadPolicy when TimeToUnload > TimeToPersist to prevent an Abort tracking record.
854 IAsyncResult BeginAbandon(Exception reason, bool shouldTrackAbort, TimeSpan timeout, AsyncCallback callback, object state)
856 return AbandonAsyncResult.Create(this, reason, shouldTrackAbort, timeout, callback, state);
859 public void EndAbandon(IAsyncResult result)
861 AbandonAsyncResult.End(result);
864 IAsyncResult BeginAbandonAndSuspend(Exception reason, TimeSpan timeout, AsyncCallback callback, object state)
866 Fx.Assert(reason != null, "reason must not be null!");
867 return AbandonAndSuspendAsyncResult.Create(this, reason, timeout, callback, state);
870 void EndAbandonAndSuspend(IAsyncResult result)
872 AbandonAndSuspendAsyncResult.End(result);
875 void AbortInstance(Exception reason, bool isWorkflowThread)
877 AbortInstance(reason, isWorkflowThread, true);
880 void AbortInstance(Exception reason, bool isWorkflowThread, bool shouldTrackAbort)
882 bool completeSelf = false;
884 if (shouldTrackAbort)
886 FxTrace.Exception.AsWarning(reason);
889 FaultPendingRequests(reason);
895 if (this.creationContext != null && !this.creationContextAborted)
897 this.creationContextAborted = true;
898 this.creationContext.OnAbort();
901 if (isWorkflowThread)
904 if (ValidateStateForAbort())
906 this.state = State.Aborted;
907 if (shouldTrackAbort)
909 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowAborted();
910 this.Controller.Abort(reason);
914 // this ensures that reason is null when WorkflowInstance.Abort is called
915 // and prevents an Abort tracking record.
916 this.Controller.Abort();
918 DecrementBusyCount();
920 // We should get off this thread because we're unsure of its state
921 ScheduleAbortTracking(false);
926 bool ownsLock = false;
930 if (AcquireLockAsync(this.acquireLockTimeout, true, false, ref ownsLock, new FastAsyncCallback(OnAbortLockAcquired),
931 new AbortInstanceState(reason, shouldTrackAbort)))
934 if (ValidateStateForAbort())
936 this.state = State.Aborted;
937 if (shouldTrackAbort)
939 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowAborted();
940 this.Controller.Abort(reason);
944 // this ensures that reason is null when WorkflowInstance.Abort is called
945 // and prevents an Abort tracking record.
946 this.Controller.Abort();
948 DecrementBusyCount();
950 // We need to get off this thread so we don't block the caller
952 ScheduleAbortTracking(false);
960 ReleaseLock(ref ownsLock);
967 this.serviceHost.FaultServiceHostIfNecessary(reason);
971 void AbortExtensions()
973 this.abortingExtensions = true;
975 // Need to ensure that either components see the Aborted state, this method sees the components, or both.
976 Thread.MemoryBarrier();
978 if (this.persistenceContext != null)
980 this.persistenceContext.Abort();
983 PersistencePipeline currentPersistencePipeline = this.persistencePipelineInUse;
984 if (currentPersistencePipeline != null)
986 currentPersistencePipeline.Abort();
989 // We abandon buffered Receives only in the complete code path, not in abort code path.
990 if (this.hasRaisedCompleted && this.bufferedReceiveManager != null)
992 this.bufferedReceiveManager.AbandonBufferedReceives(this.persistenceContext.AssociatedKeys);
999 this.DisposeExtensions();
1001 // We abandon buffered Receives only in the complete code path, not in abort code path.
1002 if (this.hasRaisedCompleted && this.bufferedReceiveManager != null)
1004 this.bufferedReceiveManager.AbandonBufferedReceives(this.persistenceContext.AssociatedKeys);
1008 void OnAbortLockAcquired(object state, Exception exception)
1010 if (exception != null)
1012 // We ---- this exception because we were simply doing our
1013 // best to get the lock. Note that we won't proceed without
1014 // the lock because we may have already succeeded on another
1015 // thread. Technically this abort call has failed.
1017 FxTrace.Exception.AsWarning(exception);
1021 bool ownsLock = true;
1022 bool shouldRaise = false;
1023 AbortInstanceState abortInstanceState = (AbortInstanceState)state;
1027 if (ValidateStateForAbort())
1030 this.state = State.Aborted;
1031 if (abortInstanceState.ShouldTrackAbort)
1033 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowAborted();
1034 this.Controller.Abort(abortInstanceState.Reason);
1038 // this ensures that reason is null when WorkflowInstance.Abort is called
1039 // and prevents an Abort tracking record.
1040 this.Controller.Abort();
1042 DecrementBusyCount();
1047 ReleaseLock(ref ownsLock);
1052 // We call this from this thread because we've already
1053 // had a thread switch
1058 void ScheduleAbortTracking(bool isUpdateFailure)
1060 ActionItem.Schedule(new Action<object>(TrackAbort), isUpdateFailure);
1063 // This is only ever called from an appropriate thread (not the thread
1064 // that called abort unless it was an internal abort).
1065 // This method is called without the lock. We still provide single threaded
1066 // guarantees to the WorkflowInstance because:
1067 // * No other call can ever enter the executor again once the state has
1068 // switched to Aborted
1069 // * If this was an internal abort then the thread was fast pathing its
1070 // way out of the runtime and won't conflict
1071 // Or, in the case of a DynamicUpdate failure, the WorkflowInstance is
1072 // never returned from the factory method, and so will never be acessed by
1074 void TrackAbort(object state)
1076 bool isUpdateFailure = (bool)state;
1078 if (isUpdateFailure || this.Controller.HasPendingTrackingRecords)
1082 IAsyncResult result = this.BeginFlushTrackingRecords(this.trackTimeout, Fx.ThunkCallback(new AsyncCallback(OnAbortTrackingComplete)), isUpdateFailure);
1084 if (result.CompletedSynchronously)
1086 this.Controller.EndFlushTrackingRecords(result);
1100 // We ---- any exception here because we are on the abort path
1101 // and are doing a best effort to track this record.
1102 FxTrace.Exception.AsWarning(e);
1106 if (!isUpdateFailure)
1112 void OnAbortTrackingComplete(IAsyncResult result)
1114 if (result.CompletedSynchronously)
1119 bool isUpdateFailure = (bool)result.AsyncState;
1123 this.EndFlushTrackingRecords(result);
1132 // We ---- any exception here because we are on the abort path
1133 // and are doing a best effort to track this record.
1134 FxTrace.Exception.AsWarning(e);
1137 if (!isUpdateFailure)
1145 this.UnloadInstancePolicy.Cancel();
1146 CompletePendingOperations();
1149 public IAsyncResult BeginTerminate(string reason, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
1151 Fx.Assert(!String.IsNullOrEmpty(reason), "reason string must not be null or empty!");
1153 // the FaultException below is created using the FaultException(FaultReason, FaultCode) ctor instead of the FaultException(MessageFault) ctor
1154 // because the latter ctor saves the fault in its fault member. Saving the fault is problematic because faultException would serialize its
1155 // fault member and operationExecutionFault is not serializable. The faultException might need to be serialized if the workflowServiceInstance
1156 // is ever persisted since the faultException below ultimately becomes the terminationException saved with the workflowServiceInstance.
1157 OperationExecutionFault fault = OperationExecutionFault.CreateTerminatedFault(reason);
1158 return BeginTerminate(new FaultException(fault.Reason, fault.Code), transaction, timeout, callback, state);
1161 IAsyncResult BeginTerminate(Exception reason, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
1163 Fx.Assert(reason != null, "reason must not be null!");
1164 return TerminateAsyncResult.Create(this, reason, transaction, timeout, callback, state);
1167 public void EndTerminate(IAsyncResult result)
1169 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowTerminated();
1170 TerminateAsyncResult.End(result);
1173 public IAsyncResult BeginCancel(Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
1175 return CancelAsyncResult.Create(this, transaction, timeout, callback, state);
1178 public void EndCancel(IAsyncResult result)
1180 CancelAsyncResult.End(result);
1185 this.isRunnable = true;
1186 this.state = State.Active;
1189 public IAsyncResult BeginRun(Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
1191 return BeginRun(transaction, null, timeout, callback, state);
1194 public IAsyncResult BeginRun(Transaction transaction, string operationName, TimeSpan timeout, AsyncCallback callback, object state)
1196 return RunAsyncResult.Create(this, transaction, operationName, timeout, callback, state);
1199 public void EndRun(IAsyncResult result)
1201 RunAsyncResult.End(result);
1204 protected override void OnNotifyPaused()
1206 bool ownsLock = true;
1207 bool keepLock = false;
1211 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowExecuting(false);
1212 if (ShouldRaiseComplete)
1214 PrepareNextIdleWaiter();
1216 Exception abortException = null;
1220 // We're about to notify the world that this instance is completed
1221 // so let's make it official.
1222 this.hasRaisedCompleted = true;
1223 this.state = State.Completed;
1224 GetCompletionState();
1225 if (this.completionState == ActivityInstanceState.Closed)
1227 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowCompleted();
1230 if (this.Controller.HasPendingTrackingRecords)
1232 IAsyncResult result = this.Controller.BeginFlushTrackingRecords(this.trackTimeout, TrackCompleteDoneCallback, this);
1234 if (result.CompletedSynchronously)
1236 this.Controller.EndFlushTrackingRecords(result);
1245 this.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
1249 this.isInHandler = true;
1254 this.isInHandler = false;
1267 if (abortException != null)
1269 AbortInstance(abortException, true);
1272 else if (this.Controller.State == WorkflowInstanceState.Aborted)
1274 Exception abortReason = this.Controller.GetAbortReason();
1275 this.AbortInstance(abortReason, true);
1277 else if (ShouldRaiseIdle)
1279 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowIdle();
1281 PrepareNextIdleWaiter();
1283 if (this.Controller.TrackingEnabled)
1285 this.Controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Idle, this.DefinitionIdentity));
1286 IAsyncResult result = this.Controller.BeginFlushTrackingRecords(this.trackTimeout, TrackIdleDoneCallback, this);
1288 if (result.CompletedSynchronously)
1290 this.Controller.EndFlushTrackingRecords(result);
1299 this.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
1303 this.isInHandler = true;
1308 this.isInHandler = false;
1313 NotifyCheckCanPersistWaiters(ref ownsLock);
1320 ReleaseLock(ref ownsLock);
1325 // Note: this is runtime generated Abort such as Transaction failure
1326 protected override void OnRequestAbort(Exception reason)
1328 AbortInstance(reason, false);
1331 static void OnTrackCompleteDone(IAsyncResult result)
1333 if (result.CompletedSynchronously)
1338 WorkflowServiceInstance thisPtr = (WorkflowServiceInstance)result.AsyncState;
1339 bool ownsLock = true;
1343 thisPtr.Controller.EndFlushTrackingRecords(result);
1345 thisPtr.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
1349 thisPtr.isInHandler = true;
1350 thisPtr.OnCompleted();
1354 thisPtr.isInHandler = false;
1359 thisPtr.ReleaseLock(ref ownsLock);
1363 static void OnTrackIdleDone(IAsyncResult result)
1365 if (result.CompletedSynchronously)
1370 WorkflowServiceInstance thisPtr = (WorkflowServiceInstance)result.AsyncState;
1371 bool ownsLock = true;
1375 thisPtr.Controller.EndFlushTrackingRecords(result);
1377 thisPtr.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
1381 thisPtr.isInHandler = true;
1386 thisPtr.isInHandler = false;
1391 thisPtr.ReleaseLock(ref ownsLock);
1395 protected override void OnNotifyUnhandledException(Exception exception, Activity exceptionSource,
1396 string exceptionSourceInstanceId)
1398 bool ownsLock = true;
1399 bool keepLock = false;
1400 UnhandledExceptionAsyncData data = new UnhandledExceptionAsyncData(this, exception, exceptionSource);
1404 if (this.Controller.HasPendingTrackingRecords)
1406 IAsyncResult result = this.Controller.BeginFlushTrackingRecords(this.trackTimeout, TrackUnhandledExceptionDoneCallback, data);
1408 if (result.CompletedSynchronously)
1410 this.Controller.EndFlushTrackingRecords(result);
1419 this.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
1423 this.isInHandler = true;
1424 OnUnhandledException(data);
1428 this.isInHandler = false;
1435 ReleaseLock(ref ownsLock);
1440 static void OnTrackUnhandledExceptionDone(IAsyncResult result)
1442 if (result.CompletedSynchronously)
1447 UnhandledExceptionAsyncData data = (UnhandledExceptionAsyncData)result.AsyncState;
1448 WorkflowServiceInstance thisPtr = data.Instance;
1450 bool ownsLock = true;
1454 thisPtr.Controller.EndFlushTrackingRecords(result);
1456 thisPtr.handlerThreadId = Thread.CurrentThread.ManagedThreadId;
1460 thisPtr.isInHandler = true;
1461 thisPtr.OnUnhandledException(data);
1465 thisPtr.isInHandler = false;
1470 thisPtr.ReleaseLock(ref ownsLock);
1474 public IAsyncResult BeginSuspend(bool isUnlocked, string reason, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
1476 return SuspendAsyncResult.Create(this, isUnlocked, reason, transaction, timeout, callback, state);
1479 public void EndSuspend(IAsyncResult result)
1481 SuspendAsyncResult.End(result);
1484 public IAsyncResult BeginUnsuspend(Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
1486 return UnsuspendAsyncResult.Create(this, transaction, timeout, callback, state);
1489 public void EndUnsuspend(IAsyncResult result)
1491 UnsuspendAsyncResult.End(result);
1494 void GetCompletionState()
1496 this.completionState = this.Controller.GetCompletionState(out this.workflowOutputs, out this.terminationException);
1499 void TrackPersistence(PersistenceOperation operation)
1501 if (this.Controller.TrackingEnabled)
1503 if (operation == PersistenceOperation.Delete)
1505 this.Controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Deleted, this.DefinitionIdentity));
1507 else if (operation == PersistenceOperation.Unload)
1509 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowUnloaded();
1510 this.Controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Unloaded, this.DefinitionIdentity));
1514 this.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowPersisted();
1515 this.Controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Persisted, this.DefinitionIdentity));
1520 Dictionary<XName, InstanceValue> GeneratePersistenceData()
1522 Dictionary<XName, InstanceValue> data = new Dictionary<XName, InstanceValue>(10);
1523 data[WorkflowNamespace.Bookmarks] = new InstanceValue(Controller.GetBookmarks(), InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
1524 data[WorkflowNamespace.LastUpdate] = new InstanceValue(DateTime.UtcNow, InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
1526 foreach (KeyValuePair<string, LocationInfo> mappedVariable in Controller.GetMappedVariables())
1528 data[WorkflowNamespace.VariablesPath.GetName(mappedVariable.Key)] = new InstanceValue(mappedVariable.Value, InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
1531 Fx.AssertAndThrow(Controller.State != WorkflowInstanceState.Aborted, "Cannot generate data for an aborted service instance.");
1532 if (Controller.State != WorkflowInstanceState.Complete)
1534 data[WorkflowNamespace.Workflow] = new InstanceValue(Controller.PrepareForSerialization());
1536 if (this.creationContext != null)
1538 data[WorkflowServiceNamespace.CreationContext] = new InstanceValue(this.creationContext);
1541 data[WorkflowNamespace.Status] = new InstanceValue(Controller.State == WorkflowInstanceState.Idle ? "Idle" : "Executing", InstanceValueOptions.WriteOnly);
1545 data[WorkflowNamespace.Workflow] = new InstanceValue(Controller.PrepareForSerialization(), InstanceValueOptions.Optional);
1547 this.GetCompletionState();
1549 if (this.completionState == ActivityInstanceState.Faulted)
1551 data[WorkflowNamespace.Status] = new InstanceValue("Faulted", InstanceValueOptions.WriteOnly);
1552 data[WorkflowNamespace.Exception] = new InstanceValue(this.terminationException, InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
1554 else if (this.completionState == ActivityInstanceState.Closed)
1556 data[WorkflowNamespace.Status] = new InstanceValue("Closed", InstanceValueOptions.WriteOnly);
1557 if (this.workflowOutputs != null)
1559 foreach (KeyValuePair<string, object> output in this.workflowOutputs)
1561 data[WorkflowNamespace.OutputPath.GetName(output.Key)] = new InstanceValue(output.Value, InstanceValueOptions.WriteOnly | InstanceValueOptions.Optional);
1567 Fx.AssertAndThrow(this.completionState == ActivityInstanceState.Canceled, "Cannot be executing a service instance when WorkflowState was completed.");
1568 data[WorkflowNamespace.Status] = new InstanceValue("Canceled", InstanceValueOptions.WriteOnly);
1574 public IAsyncResult BeginPersist(TimeSpan timeout, AsyncCallback callback, object state)
1576 return BeginPersist(false, timeout, callback, state);
1579 IAsyncResult BeginPersist(bool isTry, TimeSpan timeout, AsyncCallback callback, object state)
1581 return new UnloadOrPersistAsyncResult(this, this.Controller.State == WorkflowInstanceState.Complete ? PersistenceOperation.Delete : PersistenceOperation.Save, false, isTry,
1582 timeout, callback, state);
1585 public bool EndPersist(IAsyncResult result)
1587 return UnloadOrPersistAsyncResult.End(result);
1590 protected override IAsyncResult OnBeginFlushTrackingRecords(AsyncCallback callback, object state)
1592 return this.Controller.BeginFlushTrackingRecords(this.trackTimeout, callback, state);
1595 protected override void OnEndFlushTrackingRecords(IAsyncResult result)
1597 this.Controller.EndFlushTrackingRecords(result);
1600 protected override IAsyncResult OnBeginPersist(AsyncCallback callback, object state)
1602 return new UnloadOrPersistAsyncResult(this, PersistenceOperation.Save, true, false, TimeSpan.MaxValue, callback, state);
1605 protected override void OnEndPersist(IAsyncResult result)
1607 UnloadOrPersistAsyncResult.End(result);
1610 protected override IAsyncResult OnBeginAssociateKeys(ICollection<InstanceKey> keys, AsyncCallback callback, object state)
1612 if (this.persistenceContext == null)
1614 return new CompletedAsyncResult(callback, state);
1618 return this.persistenceContext.BeginAssociateKeys(keys, this.persistTimeout, callback, state);
1622 protected override void OnEndAssociateKeys(IAsyncResult result)
1624 if (this.persistenceContext == null)
1626 CompletedAsyncResult.End(result);
1630 this.persistenceContext.EndAssociateKeys(result);
1634 protected override void OnDisassociateKeys(ICollection<InstanceKey> keys)
1636 if (persistenceContext != null)
1638 this.persistenceContext.DisassociateKeys(keys);
1642 BookmarkResumptionResult ResumeProtocolBookmarkCore(Bookmark bookmark, object value, BookmarkScope bookmarkScope, bool bufferedReceiveEnabled, ref AsyncWaitHandle waitHandle, ref bool ownsLock)
1644 Fx.Assert(this.state == State.Active, "WorkflowServiceInstance.State should be State.Active at this point.");
1646 BookmarkResumptionResult result;
1647 if (bookmarkScope == null)
1649 result = this.Controller.ScheduleBookmarkResumption(bookmark, value);
1653 result = this.Controller.ScheduleBookmarkResumption(bookmark, value, bookmarkScope);
1656 if (result == BookmarkResumptionResult.NotReady && !bufferedReceiveEnabled && (this.serviceHost.FilterResumeTimeout.TotalSeconds > 0))
1658 if (waitHandle == null)
1660 waitHandle = new AsyncWaitHandle();
1667 // Creation doesn't require the lock since it is guarded
1668 // by the executor lock.
1669 if (this.nextIdleWaiters == null)
1671 this.nextIdleWaiters = new List<AsyncWaitHandle>();
1674 lock (this.activeOperationsLock)
1676 this.nextIdleWaiters.Add(waitHandle);
1679 // We release the lock here so that the workflow will continue to process
1680 // until the NextIdle waiters get notified
1681 ReleaseLock(ref ownsLock);
1687 [Fx.Tag.Throws(typeof(TimeoutException), "Either the execution lock could not be acquired or the target sub-instance did not become stable in the allotted time.")]
1688 public IAsyncResult BeginResumeProtocolBookmark(Bookmark bookmark, BookmarkScope bookmarkScope, object value, TimeSpan timeout, AsyncCallback callback, object state)
1690 Fx.Assert(bookmark != null, "bookmark must not be null!");
1692 object bookmarkValue = value;
1693 WorkflowOperationContext context = value as WorkflowOperationContext;
1694 if (context != null)
1696 if (!context.HasResponse)
1698 lock (this.thisLock)
1700 this.pendingRequests.Add(context);
1703 bookmarkValue = context.BookmarkValue;
1706 return new ResumeProtocolBookmarkAsyncResult(this, bookmark, bookmarkValue, bookmarkScope, true, timeout, callback, state);
1709 [Fx.Tag.InheritThrows(From = "ResumeProtocolBookmark")]
1710 public BookmarkResumptionResult EndResumeProtocolBookmark(IAsyncResult result)
1712 return ResumeProtocolBookmarkAsyncResult.End(result);
1715 protected override IAsyncResult OnBeginResumeBookmark(Bookmark bookmark, object value, TimeSpan timeout, AsyncCallback callback, object state)
1717 return new ResumeProtocolBookmarkAsyncResult(this, bookmark, value, null, false, timeout, callback, state);
1720 protected override BookmarkResumptionResult OnEndResumeBookmark(IAsyncResult result)
1722 return ResumeProtocolBookmarkAsyncResult.End(result);
1727 this.state = State.Unloaded;
1729 // don't abort completed instances
1730 if (this.Controller.State != WorkflowInstanceState.Complete)
1732 this.Controller.Abort();
1735 DecrementBusyCount();
1738 // This always happens under executor lock
1739 void AddCheckCanPersistWaiter(WaitForCanPersistAsyncResult result)
1741 // Creation doesn't require the lock since it is guarded
1742 // by the executor lock.
1743 if (this.checkCanPersistWaiters == null)
1745 this.checkCanPersistWaiters = new List<WaitForCanPersistAsyncResult>();
1747 this.checkCanPersistWaiters.Add(result);
1750 // This always happens under executor lock
1751 void NotifyCheckCanPersistWaiters(ref bool ownsLock)
1753 // Always guarded by the executor lock.
1754 if (this.checkCanPersistWaiters != null && this.checkCanPersistWaiters.Count > 0 && this.Controller.IsPersistable)
1756 List<WaitForCanPersistAsyncResult> waiters = this.checkCanPersistWaiters;
1757 this.checkCanPersistWaiters = null;
1758 foreach (WaitForCanPersistAsyncResult waiter in waiters)
1760 waiter.SetEvent(ref ownsLock);
1765 IAsyncResult BeginWaitForCanPersist(ref bool ownsLock, TimeSpan timeout, AsyncCallback callback, object state)
1767 return new WaitForCanPersistAsyncResult(this, ref ownsLock, timeout, callback, state);
1770 void EndWaitForCanPersist(IAsyncResult result, ref bool ownsLock)
1772 WaitForCanPersistAsyncResult.End(result, ref ownsLock);
1775 void ThrowIfAborted()
1777 if (this.state == State.Aborted)
1779 throw FxTrace.Exception.AsError(new FaultException(OperationExecutionFault.CreateAbortedFault(SR.WorkflowInstanceAborted(this.Id))));
1783 void ThrowIfTerminatedOrCompleted()
1785 if (this.hasRaisedCompleted)
1787 if (this.terminationException != null)
1789 throw FxTrace.Exception.AsError(new FaultException(OperationExecutionFault.CreateTerminatedFault(SR.WorkflowInstanceTerminated(this.Id))));
1793 throw FxTrace.Exception.AsError(new FaultException(OperationExecutionFault.CreateCompletedFault(SR.WorkflowInstanceCompleted(this.Id))));
1798 void ThrowIfUnloaded()
1800 if (this.state == State.Unloaded)
1802 throw FxTrace.Exception.AsError(new FaultException(OperationExecutionFault.CreateInstanceUnloadedFault(SR.WorkflowInstanceUnloaded(this.Id))));
1806 void ThrowIfSuspended()
1808 if (this.state == State.Suspended)
1810 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InstanceMustNotBeSuspended));
1814 void ThrowIfNoPersistenceProvider()
1816 if (this.persistenceContext == null)
1818 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.PersistenceProviderRequiredToPersist));
1822 bool ValidateStateForSuspend(Transaction transaction)
1824 // Note: we allow suspend even when suspended to update Suspended reason.
1826 Validate(transaction == null ? XD2.WorkflowInstanceManagementService.Suspend : XD2.WorkflowInstanceManagementService.TransactedSuspend, transaction, true);
1828 // WorkflowInstanceException validations
1830 ThrowIfTerminatedOrCompleted();
1836 bool ValidateStateForUnsuspend(Transaction transaction)
1838 if (this.state == State.Active)
1843 Validate(transaction == null ? XD2.WorkflowInstanceManagementService.Unsuspend : XD2.WorkflowInstanceManagementService.TransactedUnsuspend, transaction, true);
1845 // WorkflowInstanceException validations
1847 ThrowIfTerminatedOrCompleted();
1853 bool ValidateStateForRun(Transaction transaction, string operationName)
1855 if (this.hasRaisedCompleted || (this.state == State.Active && this.isRunnable) || this.isInTransaction)
1860 Validate(operationName ?? (transaction == null ? XD2.WorkflowInstanceManagementService.Run : XD2.WorkflowInstanceManagementService.TransactedRun), transaction, true);
1862 // WorkflowInstanceException validations
1870 void ValidateStateForResumeProtocolBookmark()
1872 // WorkflowInstanceException validations
1874 ThrowIfTerminatedOrCompleted();
1879 void ValidateStateForAssociateKeys()
1881 // WorkflowInstanceException validations
1885 bool AreBookmarksInvalid(out BookmarkResumptionResult result)
1887 if (this.hasRaisedCompleted)
1889 result = BookmarkResumptionResult.NotFound;
1892 else if (this.state == State.Unloaded || this.state == State.Aborted || this.state == State.Suspended)
1894 result = BookmarkResumptionResult.NotReady;
1898 result = BookmarkResumptionResult.Success;
1902 bool ValidateStateForAbort()
1904 if (this.state == State.Aborted)
1912 bool ValidateStateForCancel(Transaction transaction)
1914 if (this.hasRaisedCompleted)
1919 Validate(transaction == null ? XD2.WorkflowInstanceManagementService.Cancel : XD2.WorkflowInstanceManagementService.TransactedCancel, transaction, true);
1921 // WorkflowInstanceException validations
1928 void ValidateStateForPersist()
1930 // WorkflowInstanceException validations
1934 // Other validations
1935 ThrowIfNoPersistenceProvider();
1938 bool ValidateStateForUnload()
1940 if (this.state == State.Unloaded)
1945 // WorkflowInstanceException validations
1948 // Other validations
1949 if (this.Controller.State != WorkflowInstanceState.Complete)
1951 ThrowIfNoPersistenceProvider();
1957 bool ValidateStateForTerminate(Transaction transaction)
1959 Validate(transaction == null ? XD2.WorkflowInstanceManagementService.Terminate : XD2.WorkflowInstanceManagementService.TransactedTerminate, transaction, true);
1961 // WorkflowInstanceException validations
1963 ThrowIfTerminatedOrCompleted();
1969 delegate void InvokeCompletedCallback();
1971 enum PersistenceOperation : byte
1978 struct AcquireLockAsyncData
1980 WorkflowServiceInstance instance;
1981 FastAsyncCallback callback;
1984 public AcquireLockAsyncData(WorkflowServiceInstance instance, FastAsyncCallback callback, object state)
1986 this.instance = instance;
1987 this.callback = callback;
1991 public WorkflowServiceInstance Instance
1999 public FastAsyncCallback Callback
2003 return this.callback;
2016 class AbortInstanceState
2018 public AbortInstanceState(Exception reason, bool shouldTrackAbort)
2020 this.Reason = reason;
2021 this.ShouldTrackAbort = shouldTrackAbort;
2024 public Exception Reason
2030 public bool ShouldTrackAbort
2037 public IAsyncResult BeginTryAcquireReference(TimeSpan timeout, AsyncCallback callback, object state)
2039 return new TryAcquireReferenceAsyncResult(this, timeout, callback, state);
2042 public bool EndTryAcquireReference(IAsyncResult result)
2044 return TryAcquireReferenceAsyncResult.End(result);
2047 public IAsyncResult BeginReleaseInstance(bool isTryUnload, TimeSpan timeout, AsyncCallback callback, object state)
2049 return new ReleaseInstanceAsyncResult(this, isTryUnload, timeout, callback, state);
2052 public void EndReleaseInstance(IAsyncResult result)
2054 ReleaseInstanceAsyncResult.End(result);
2057 public static void EndReleaseInstanceForClose(IAsyncResult result)
2059 ReleaseInstanceAsyncResult.End(result);
2062 public IAsyncResult BeginAssociateInfrastructureKeys(ICollection<InstanceKey> associatedKeys, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
2064 return new AssociateKeysAsyncResult(this, associatedKeys, transaction, timeout, callback, state);
2067 public void EndAssociateInfrastructureKeys(IAsyncResult result)
2069 AssociateKeysAsyncResult.End(result);
2072 public void ReleaseContext(WorkflowOperationContext context)
2074 lock (this.thisLock)
2076 this.pendingRequests.Remove(context);
2080 public IAsyncResult BeginWaitForPendingOperations(string sessionId, TimeSpan timeout, AsyncCallback callback, object state)
2082 PendingOperationAsyncResult result = null;
2083 lock (this.thisLock)
2085 if (this.pendingOperations == null)
2087 this.pendingOperations = new Dictionary<string, List<PendingOperationAsyncResult>>();
2089 List<PendingOperationAsyncResult> pendingList;
2090 if (!this.pendingOperations.TryGetValue(sessionId, out pendingList))
2092 pendingList = new List<PendingOperationAsyncResult>();
2093 this.pendingOperations.Add(sessionId, pendingList);
2095 bool isFirstRequest = (pendingList.Count == 0);
2096 result = new PendingOperationAsyncResult(isFirstRequest, timeout, callback, state);
2097 pendingList.Add(result);
2098 ++this.pendingOperationCount;
2104 public void EndWaitForPendingOperations(IAsyncResult result)
2106 PendingOperationAsyncResult.End(result);
2110 public void RemovePendingOperation(string sessionId, IAsyncResult result)
2112 // remove the async result from the queue. The result could represent the operation currently being processed for the session
2113 // or could be an operation that had timed out waiting to get to the head of the queue.
2114 // Also, note that if the instance has already completed/aborted etc all pending operations would call OnWorkflowOperationCompleted
2115 // simultaneously and this.pendingOperations would be null.
2116 lock (this.thisLock)
2118 List<PendingOperationAsyncResult> pendingList;
2119 if (this.pendingOperations != null && this.pendingOperations.TryGetValue(sessionId, out pendingList))
2121 if (pendingList.Count > 0)
2123 // In the happy path, RemovePendingOperation might get called more than more than once(HandleEndResume & ProcessReply)
2124 // wasInProcess would be false the second time. When wasInProcess is false, we do not unblock the next item in the list
2125 bool wasInProcess = pendingList[0] == result;
2127 if (pendingList.Remove((PendingOperationAsyncResult)result))
2129 --this.pendingOperationCount;
2131 if (pendingList.Count == 0)
2133 this.pendingOperations.Remove(sessionId);
2135 // signal the next request to resume bookmark
2136 else if (wasInProcess)
2138 pendingList[0].Unblock();
2145 void CompletePendingOperations()
2147 lock (this.thisLock)
2149 if (this.pendingOperations != null)
2151 foreach (List<PendingOperationAsyncResult> pendingList in this.pendingOperations.Values)
2153 foreach (PendingOperationAsyncResult result in pendingList)
2159 this.pendingOperations = null;
2160 this.pendingOperationCount = 0;
2166 if (this.BufferedReceiveManager != null)
2168 this.persistenceContext.Bookmarks = this.Controller.GetBookmarks();
2169 this.BufferedReceiveManager.Retry(this.persistenceContext.AssociatedKeys, this.persistenceContext.Bookmarks);
2175 if (this.terminationException != null)
2177 FaultPendingRequests(new FaultException(OperationExecutionFault.CreateTerminatedFault(SR.WorkflowInstanceTerminated(this.Id))));
2181 FaultPendingRequests(new FaultException(OperationExecutionFault.CreateCompletedFault(SR.WorkflowInstanceCompleted(this.Id))));
2184 if (handleEndReleaseInstance == null)
2186 handleEndReleaseInstance = Fx.ThunkCallback(new AsyncCallback(HandleEndReleaseInstance));
2188 IAsyncResult result = this.BeginReleaseInstance(false, TimeSpan.MaxValue, handleEndReleaseInstance, this);
2189 if (result.CompletedSynchronously)
2191 OnReleaseInstance(result);
2194 CompletePendingOperations();
2197 static void HandleEndReleaseInstance(IAsyncResult result)
2199 if (result.CompletedSynchronously)
2204 WorkflowServiceInstance thisPtr = (WorkflowServiceInstance)result.AsyncState;
2205 thisPtr.OnReleaseInstance(result);
2208 void OnReleaseInstance(IAsyncResult result)
2212 this.EndReleaseInstance(result);
2221 this.AbortInstance(e, false);
2225 void OnUnhandledException(UnhandledExceptionAsyncData data)
2227 Fx.Assert(data != null, "data must not be null!");
2228 Fx.Assert(data.Exception != null, "data.Exception must not be null!");
2230 FaultPendingRequests(data.Exception);
2231 this.UnhandledExceptionPolicy.OnUnhandledException(data);
2234 // notify pending requests so that clients don't hang
2235 void FaultPendingRequests(Exception e)
2237 WorkflowOperationContext[] requestsToFault = null;
2239 lock (this.thisLock)
2241 if (this.pendingRequests.Count == 0)
2246 requestsToFault = this.pendingRequests.ToArray();
2247 this.pendingRequests.Clear();
2250 for (int i = 0; i < requestsToFault.Length; i++)
2252 requestsToFault[i].SendFault(e);
2256 //Attached Transaction outcome Signals from IEnlistmentNotification.
2257 public void TransactionCommitted() //Signal from TransactionContext on attached transaction commit.
2259 if (this.TryAddReference())
2263 if ((this.state == State.Suspended && this.isTransactedCancelled) || this.state == State.Active)
2265 bool ownsLock = false;
2266 // this could ---- with other commands and may throw exception.
2267 // treat it as best effort pulse of workflow.
2271 AcquireLock(this.acquireLockTimeout, ref ownsLock);
2273 if ((this.state == State.Suspended && this.isTransactedCancelled) || ValidateStateForRun(null, null))
2275 this.isRunnable = true;
2276 this.state = State.Active;
2279 catch (Exception exception)
2281 if (Fx.IsFatal(exception))
2285 FxTrace.Exception.AsWarning(exception);
2289 ReleaseLock(ref ownsLock);
2292 // the workflow has completed thru transacted Terminate
2293 else if (this.state == State.Unloaded && this.completionState == ActivityInstanceState.Faulted)
2299 catch (Exception exception)
2301 if (Fx.IsFatal(exception))
2306 this.AbortInstance(exception, false);
2312 this.ReleaseReference();
2317 public void OnTransactionPrepared()
2319 // Transaction has been prepared.
2320 // As far as WorkflowServiceInstance is concerned, no longer in transaction.
2321 this.transactionContext = null;
2322 this.isInTransaction = false;
2325 public void OnTransactionAbortOrInDoubt(TransactionException exception)
2327 Fx.Assert(exception != null, "Need a valid TransactionException to call this");
2328 this.AbortInstance(exception, false);
2331 // Called under the lock.
2332 void Validate(string operationName, Transaction ambientTransaction, bool controlEndpoint)
2334 ValidateHelper(operationName, ambientTransaction, false, controlEndpoint);
2337 void ValidateHelper(string operationName, Transaction ambientTransaction, bool useThreadTransaction, bool controlEndpoint)
2339 TransactionContext attachedTransaction = this.transactionContext;
2341 //Ensure Instance is usable.
2342 if (attachedTransaction != null &&
2343 attachedTransaction.CurrentTransaction != (useThreadTransaction ? Transaction.Current : ambientTransaction))
2345 throw FxTrace.Exception.AsError(new FaultException(
2346 OperationExecutionFault.CreateTransactedLockException(this.persistenceContext.InstanceId, operationName)));
2349 if (controlEndpoint)
2351 Fx.AssertAndThrow(this.state != State.Unloaded, "Cannot be unloaded");
2354 if (this.state == State.Unloaded)
2356 throw FxTrace.Exception.AsError(new FaultException(
2357 OperationExecutionFault.CreateInstanceUnloadedFault(SR.ServiceInstanceUnloaded(this.persistenceContext.InstanceId))));
2360 //Do a fast check to fail fast.
2361 if (this.state == State.Completed || this.state == State.Aborted)
2363 throw FxTrace.Exception.AsError(new FaultException(
2364 OperationExecutionFault.CreateInstanceNotFoundFault(SR.ServiceInstanceTerminated(this.persistenceContext.InstanceId))));
2367 if (this.state == State.Suspended &&
2368 !(operationName == XD2.WorkflowInstanceManagementService.Suspend
2369 || operationName == XD2.WorkflowInstanceManagementService.TransactedSuspend
2370 || operationName == XD2.WorkflowInstanceManagementService.Unsuspend
2371 || operationName == XD2.WorkflowInstanceManagementService.TransactedUnsuspend
2372 || operationName == XD2.WorkflowInstanceManagementService.Terminate
2373 || operationName == XD2.WorkflowInstanceManagementService.TransactedTerminate
2374 || operationName == XD2.WorkflowInstanceManagementService.Cancel
2375 || operationName == XD2.WorkflowInstanceManagementService.TransactedCancel))
2377 throw FxTrace.Exception.AsError(new FaultException(
2378 OperationExecutionFault.CreateSuspendedFault(this.Id, operationName)));
2381 //already done under the scope of a lock.No additional locking needed here
2382 void DecrementBusyCount()
2384 if (this.hasIncrementedBusyCount)
2386 this.serviceHost.DecrementBusyCount();
2387 if (AspNetEnvironment.Current.TraceDecrementBusyCountIsEnabled())
2389 AspNetEnvironment.Current.TraceDecrementBusyCount(SR.BusyCountTraceFormatString(this.Id));
2391 this.hasIncrementedBusyCount = false;
2394 //already done under the scope of a lock.No additional locking needed here
2395 void IncrementBusyCount()
2397 if (!this.hasIncrementedBusyCount)
2399 this.serviceHost.IncrementBusyCount();
2400 if (AspNetEnvironment.Current.TraceIncrementBusyCountIsEnabled())
2402 AspNetEnvironment.Current.TraceIncrementBusyCount(SR.BusyCountTraceFormatString(this.Id));
2404 this.hasIncrementedBusyCount = true;
2418 class ReleaseInstanceAsyncResult : AsyncResult
2420 static AsyncCompletion handleEndUnload;
2421 static Action<AsyncResult, Exception> onCompleting = new Action<AsyncResult, Exception>(Finally);
2422 static FastAsyncCallback lockAcquiredCallback = new FastAsyncCallback(OnLockAcquired);
2423 static FastAsyncCallback acquireCompletedCallback = new FastAsyncCallback(AcquireCompletedCallback);
2424 static AsyncCompletion onReleasePersistenceContext;
2425 static AsyncCompletion onClosePersistenceContext;
2426 WorkflowServiceInstance workflowInstance;
2427 TimeoutHelper timeoutHelper;
2430 bool referenceAcquired;
2432 public ReleaseInstanceAsyncResult(WorkflowServiceInstance workflowServiceInstance,
2433 bool isTryUnload, TimeSpan timeout, AsyncCallback callback, object state)
2434 : base(callback, state)
2436 this.workflowInstance = workflowServiceInstance;
2437 this.isTryUnload = isTryUnload;
2438 this.timeoutHelper = new TimeoutHelper(timeout);
2439 this.OnCompleting = onCompleting;
2441 bool completeSelf = false;
2442 Exception completionException = null;
2445 completeSelf = TryAcquire();
2454 completionException = e;
2459 if (completionException != null)
2461 Finally(this, completionException);
2467 this.Complete(true);
2471 public static void End(IAsyncResult result)
2473 AsyncResult.End<ReleaseInstanceAsyncResult>(result);
2478 if (this.workflowInstance.acquireReferenceSemaphore.EnterAsync(timeoutHelper.RemainingTime(), acquireCompletedCallback, this))
2480 return this.HandleEndAcquireReference();
2488 bool HandleEndAcquireReference()
2490 this.referenceAcquired = true;
2491 if (this.workflowInstance.hasPersistedDeleted)
2493 return this.LockAndReleasePersistenceContext();
2497 return this.ReleaseInstance();
2501 static void AcquireCompletedCallback(object state, Exception completionException)
2503 ReleaseInstanceAsyncResult thisPtr = (ReleaseInstanceAsyncResult)state;
2505 bool completeSelf = true;
2506 if (completionException == null)
2510 completeSelf = thisPtr.HandleEndAcquireReference();
2518 completionException = e;
2524 thisPtr.Complete(false, completionException);
2528 bool ReleaseInstance()
2530 if (handleEndUnload == null)
2532 handleEndUnload = new AsyncCompletion(HandleEndUnload);
2535 IAsyncResult result = null;
2538 if (this.isTryUnload)
2540 result = this.BeginTryUnload(timeoutHelper.RemainingTime(),
2541 this.PrepareAsyncCompletion(handleEndUnload), this);
2545 result = this.BeginUnload(timeoutHelper.RemainingTime(),
2546 this.PrepareAsyncCompletion(handleEndUnload), this);
2549 catch (FaultException exception)
2551 if (OperationExecutionFault.IsAbortedFaultException(exception))
2553 FxTrace.Exception.AsWarning(exception);
2562 if (result.CompletedSynchronously)
2564 return HandleEndUnload(result);
2572 IAsyncResult BeginUnload(TimeSpan timeout, AsyncCallback callback, object state)
2574 return new UnloadOrPersistAsyncResult(this.workflowInstance, PersistenceOperation.Unload, false, false, timeout, callback, state);
2577 void EndUnload(IAsyncResult result)
2579 UnloadOrPersistAsyncResult.End(result);
2582 IAsyncResult BeginTryUnload(TimeSpan timeout, AsyncCallback callback, object state)
2584 return new UnloadOrPersistAsyncResult(this.workflowInstance, PersistenceOperation.Unload, false, true, timeout, callback, state);
2587 bool EndTryUnload(IAsyncResult result)
2589 return UnloadOrPersistAsyncResult.End(result);
2592 static bool HandleEndUnload(IAsyncResult result)
2594 ReleaseInstanceAsyncResult thisPtr = (ReleaseInstanceAsyncResult)result.AsyncState;
2595 bool successfulUnload = false;
2598 if (thisPtr.isTryUnload)
2600 // if EndTryUnload returns false, then we need to revert our changes
2601 successfulUnload = thisPtr.EndTryUnload(result);
2605 thisPtr.EndUnload(result);
2606 successfulUnload = true;
2609 catch (FaultException exception)
2611 if (OperationExecutionFault.IsAbortedFaultException(exception))
2613 FxTrace.Exception.AsWarning(exception);
2621 if (successfulUnload)
2623 return thisPtr.LockAndReleasePersistenceContext();
2631 bool LockAndReleasePersistenceContext()
2633 if (this.workflowInstance.AcquireLockAsync(this.timeoutHelper.RemainingTime(), ref this.ownsLock, lockAcquiredCallback, this))
2635 bool completeSelf = true;
2638 completeSelf = this.ReleasePersistenceContext();
2644 this.workflowInstance.ReleaseLock(ref this.ownsLock);
2647 return completeSelf;
2655 static void OnLockAcquired(object state, Exception asyncException)
2657 ReleaseInstanceAsyncResult thisPtr = (ReleaseInstanceAsyncResult)state;
2659 if (asyncException != null)
2661 thisPtr.Complete(false, asyncException);
2665 thisPtr.ownsLock = true;
2667 bool completeSelf = true;
2668 Exception completionException = null;
2672 completeSelf = thisPtr.ReleasePersistenceContext();
2674 catch (Exception exception)
2676 if (Fx.IsFatal(exception))
2681 completionException = exception;
2687 thisPtr.workflowInstance.ReleaseLock(ref thisPtr.ownsLock);
2693 thisPtr.Complete(false, completionException);
2697 bool ReleasePersistenceContext()
2699 if (this.workflowInstance.persistenceContext.State != CommunicationState.Opened)
2704 if (onReleasePersistenceContext == null)
2706 onReleasePersistenceContext = new AsyncCompletion(OnReleasePersistenceContext);
2709 IAsyncResult result = this.workflowInstance.persistenceContext.BeginRelease(this.workflowInstance.persistTimeout,
2710 PrepareAsyncCompletion(onReleasePersistenceContext), this);
2712 return SyncContinue(result);
2715 static bool OnReleasePersistenceContext(IAsyncResult result)
2717 ReleaseInstanceAsyncResult thisPtr = (ReleaseInstanceAsyncResult)result.AsyncState;
2718 thisPtr.workflowInstance.persistenceContext.EndRelease(result);
2719 if (onClosePersistenceContext == null)
2721 onClosePersistenceContext = new AsyncCompletion(OnClosePersistenceContext);
2724 IAsyncResult closeResult = thisPtr.workflowInstance.persistenceContext.BeginClose(thisPtr.timeoutHelper.RemainingTime(),
2725 thisPtr.PrepareAsyncCompletion(onClosePersistenceContext), thisPtr);
2726 return thisPtr.SyncContinue(closeResult);
2729 static bool OnClosePersistenceContext(IAsyncResult result)
2731 ReleaseInstanceAsyncResult thisPtr = (ReleaseInstanceAsyncResult)result.AsyncState;
2732 thisPtr.workflowInstance.persistenceContext.EndClose(result);
2733 thisPtr.workflowInstance.Dispose();
2737 static void Finally(AsyncResult result, Exception completionException)
2739 ReleaseInstanceAsyncResult thisPtr = (ReleaseInstanceAsyncResult)result;
2744 if (completionException != null && !Fx.IsFatal(completionException))
2746 thisPtr.workflowInstance.AbortInstance(completionException, thisPtr.ownsLock);
2751 if (thisPtr.ownsLock)
2753 thisPtr.workflowInstance.ReleaseLock(ref thisPtr.ownsLock);
2759 if (thisPtr.referenceAcquired)
2761 thisPtr.workflowInstance.acquireReferenceSemaphore.Exit();
2762 thisPtr.referenceAcquired = false;
2768 class TryAcquireReferenceAsyncResult : AsyncResult
2770 static FastAsyncCallback acquireCompletedCallback = new FastAsyncCallback(AcquireCompletedCallback);
2771 WorkflowServiceInstance instance;
2772 TimeoutHelper timeoutHelper;
2775 public TryAcquireReferenceAsyncResult(WorkflowServiceInstance instance, TimeSpan timeout, AsyncCallback callback, object state)
2776 : base(callback, state)
2778 this.instance = instance;
2779 this.timeoutHelper = new TimeoutHelper(timeout);
2783 this.Complete(true);
2787 public static bool End(IAsyncResult result)
2789 return AsyncResult.End<TryAcquireReferenceAsyncResult>(result).result;
2794 if (this.instance.acquireReferenceSemaphore.EnterAsync(timeoutHelper.RemainingTime(), acquireCompletedCallback, this))
2796 this.HandleEndAcquireReference();
2805 void HandleEndAcquireReference()
2809 this.result = this.instance.TryAddReference();
2813 this.instance.acquireReferenceSemaphore.Exit();
2817 static void AcquireCompletedCallback(object state, Exception completionException)
2819 TryAcquireReferenceAsyncResult thisPtr = (TryAcquireReferenceAsyncResult)state;
2821 if (completionException == null)
2825 thisPtr.HandleEndAcquireReference();
2833 completionException = e;
2837 thisPtr.Complete(false, completionException);
2841 class PendingOperationAsyncResult : AsyncResult
2843 static Action<object, TimeoutException> handleEndWait = new Action<object, TimeoutException>(HandleEndWait);
2844 AsyncWaitHandle waitHandle;
2845 bool isFirstRequest;
2848 public PendingOperationAsyncResult(bool isFirstRequest, TimeSpan timeout, AsyncCallback callback, object state)
2849 : base(callback, state)
2851 this.isFirstRequest = isFirstRequest;
2852 this.timeout = timeout;
2854 if (!this.isFirstRequest)
2856 this.waitHandle = new AsyncWaitHandle(EventResetMode.ManualReset);
2862 if (this.isFirstRequest)
2868 Fx.Assert(this.waitHandle != null, "waitHandle should not be null if the request is not the first");
2869 if (this.waitHandle.WaitAsync(handleEndWait, this, this.timeout))
2875 static void HandleEndWait(object state, TimeoutException e)
2877 PendingOperationAsyncResult thisPtr = (PendingOperationAsyncResult)state;
2878 thisPtr.Complete(false, e);
2881 public void Unblock()
2883 if (this.waitHandle != null)
2885 this.waitHandle.Set();
2889 public static void End(IAsyncResult result)
2891 AsyncResult.End<PendingOperationAsyncResult>(result);
2896 class AssociateKeysAsyncResult : TransactedAsyncResult
2898 static AsyncCompletion handleLockAcquired = new AsyncCompletion(HandleLockAcquired);
2899 static AsyncCompletion handleAssociateInfrastructureKeys = new AsyncCompletion(HandleAssociateInfrastructureKeys);
2900 static Action<AsyncResult, Exception> onCompleting = new Action<AsyncResult, Exception>(Finally);
2902 readonly WorkflowServiceInstance workflow;
2903 readonly ICollection<InstanceKey> associatedKeys;
2904 readonly TimeoutHelper timeoutHelper;
2905 readonly Transaction transaction;
2908 public AssociateKeysAsyncResult(WorkflowServiceInstance workflow, ICollection<InstanceKey> associatedKeys, Transaction transaction,
2909 TimeSpan timeout, AsyncCallback callback, object state)
2910 : base(callback, state)
2912 Fx.Assert(associatedKeys != null && associatedKeys.Count > 0, "Must have associatedKeys!");
2913 this.workflow = workflow;
2914 this.associatedKeys = associatedKeys;
2915 this.transaction = transaction;
2916 this.timeoutHelper = new TimeoutHelper(timeout);
2917 this.OnCompleting = onCompleting;
2919 IAsyncResult result = this.workflow.BeginAcquireLockOnIdle(this.timeoutHelper.RemainingTime(), ref this.ownsLock,
2920 PrepareAsyncCompletion(handleLockAcquired), this);
2921 if (SyncContinue(result))
2927 public static void End(IAsyncResult result)
2929 AsyncResult.End<AssociateKeysAsyncResult>(result);
2932 static bool HandleLockAcquired(IAsyncResult result)
2934 AssociateKeysAsyncResult thisPtr = (AssociateKeysAsyncResult)result.AsyncState;
2936 if (result.CompletedSynchronously)
2938 thisPtr.workflow.EndAcquireLockOnIdle(result);
2942 thisPtr.workflow.EndAcquireLockOnIdle(result, ref thisPtr.ownsLock);
2945 thisPtr.workflow.ValidateStateForAssociateKeys();
2946 return thisPtr.AssociateKeys();
2949 bool AssociateKeys()
2951 IAsyncResult result;
2952 using (PrepareTransactionalCall(this.transaction))
2954 result = this.workflow.persistenceContext.BeginAssociateInfrastructureKeys(this.associatedKeys, this.workflow.persistTimeout,
2955 PrepareAsyncCompletion(handleAssociateInfrastructureKeys), this);
2957 return SyncContinue(result);
2960 static bool HandleAssociateInfrastructureKeys(IAsyncResult result)
2962 AssociateKeysAsyncResult thisPtr = (AssociateKeysAsyncResult)result.AsyncState;
2963 thisPtr.workflow.persistenceContext.EndAssociateInfrastructureKeys(result);
2964 // Proper relase lock
2965 thisPtr.workflow.ReleaseLock(ref thisPtr.ownsLock);
2969 static void Finally(AsyncResult result, Exception completionException)
2971 AssociateKeysAsyncResult thisPtr = (AssociateKeysAsyncResult)result;
2972 // Fallback for exception
2973 if (thisPtr.ownsLock)
2975 thisPtr.workflow.ReleaseLock(ref thisPtr.ownsLock);
2980 class ResumeProtocolBookmarkAsyncResult : AsyncResult
2982 static Action<object, TimeoutException> nextIdleCallback;
2983 static Action<object, TimeoutException> workflowServiceInstanceReadyCallback;
2985 static Action<AsyncResult, Exception> onCompleting = new Action<AsyncResult, Exception>(Finally);
2986 static AsyncCompletion handleEndTrack = new AsyncCompletion(HandleEndTrack);
2987 static AsyncCompletion handleEndLockAcquired = new AsyncCompletion(HandleEndLockAcquired);
2988 static AsyncCompletion handleEndReferenceAcquired = new AsyncCompletion(HandleEndReferenceAcquired);
2990 WorkflowServiceInstance instance;
2993 BookmarkScope bookmarkScope;
2994 TimeoutHelper timeoutHelper;
2995 TimeoutHelper nextIdleTimeoutHelper;
2996 AsyncWaitHandle waitHandle;
2998 BookmarkResumptionResult resumptionResult;
2999 bool isResumeProtocolBookmark;
3000 bool referenceAcquired;
3002 public ResumeProtocolBookmarkAsyncResult(WorkflowServiceInstance instance, Bookmark bookmark, object value, BookmarkScope bookmarkScope, bool isResumeProtocolBookmark, TimeSpan timeout, AsyncCallback callback, object state)
3003 : base(callback, state)
3005 this.instance = instance;
3006 this.bookmark = bookmark;
3008 this.bookmarkScope = bookmarkScope;
3009 this.timeoutHelper = new TimeoutHelper(timeout);
3010 // The value for WorkflowServiceHost.FilterResumeTimeout comes from the AppSetting
3011 // "microsoft:WorkflowServices:FilterResumeTimeoutInSeconds"
3012 this.nextIdleTimeoutHelper = new TimeoutHelper(instance.serviceHost.FilterResumeTimeout);
3013 this.isResumeProtocolBookmark = isResumeProtocolBookmark;
3014 this.OnCompleting = onCompleting;
3016 Exception completionException = null;
3017 bool completeSelf = true;
3021 if (this.isResumeProtocolBookmark)
3023 completeSelf = DoResumeBookmark();
3027 completeSelf = WaitForInstanceToBeReady();
3038 completionException = e;
3043 this.Complete(true, completionException);
3047 bool DoResumeBookmark()
3049 IAsyncResult result = this.instance.BeginAcquireLockOnIdle(timeoutHelper.RemainingTime(), ref this.ownsLock, PrepareAsyncCompletion(handleEndLockAcquired), this);
3050 return SyncContinue(result);
3053 bool WaitForInstanceToBeReady()
3055 IAsyncResult result = this.instance.BeginTryAcquireReference(timeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleEndReferenceAcquired), this);
3056 return SyncContinue(result);
3059 static bool HandleEndReferenceAcquired(IAsyncResult result)
3061 ResumeProtocolBookmarkAsyncResult thisPtr = (ResumeProtocolBookmarkAsyncResult)result.AsyncState;
3062 thisPtr.referenceAcquired = thisPtr.instance.EndTryAcquireReference(result);
3063 if (thisPtr.referenceAcquired)
3065 return thisPtr.WaitToBeSignaled();
3069 thisPtr.resumptionResult = BookmarkResumptionResult.NotReady;
3074 bool WaitToBeSignaled()
3076 bool needToWait = false;
3078 lock (this.instance.thisLock)
3080 if (!this.instance.isWorkflowServiceInstanceReady)
3083 if (this.instance.workflowServiceInstanceReadyWaitHandle == null)
3085 this.instance.workflowServiceInstanceReadyWaitHandle = new AsyncWaitHandle(EventResetMode.ManualReset);
3092 if (workflowServiceInstanceReadyCallback == null)
3094 workflowServiceInstanceReadyCallback = new Action<object, TimeoutException>(OnSignaled);
3097 if (this.instance.workflowServiceInstanceReadyWaitHandle.WaitAsync(workflowServiceInstanceReadyCallback, this, this.timeoutHelper.RemainingTime()))
3099 return DoResumeBookmark();
3108 return DoResumeBookmark();
3112 static void OnSignaled(object state, TimeoutException exception)
3114 ResumeProtocolBookmarkAsyncResult thisPtr = (ResumeProtocolBookmarkAsyncResult)state;
3115 if (exception != null)
3117 thisPtr.Complete(false, exception);
3121 bool completeSelf = false;
3122 Exception completionException = null;
3126 completeSelf = thisPtr.DoResumeBookmark();
3134 completionException = e;
3138 if (completionException != null)
3140 thisPtr.Complete(false, completionException);
3146 thisPtr.Complete(false);
3150 public static BookmarkResumptionResult End(IAsyncResult result)
3152 ResumeProtocolBookmarkAsyncResult thisPtr = AsyncResult.End<ResumeProtocolBookmarkAsyncResult>(result);
3153 return thisPtr.resumptionResult;
3156 static bool HandleEndLockAcquired(IAsyncResult result)
3158 ResumeProtocolBookmarkAsyncResult thisPtr = (ResumeProtocolBookmarkAsyncResult)result.AsyncState;
3159 if (result.CompletedSynchronously)
3161 thisPtr.instance.EndAcquireLockOnIdle(result);
3165 thisPtr.instance.EndAcquireLockOnIdle(result, ref thisPtr.ownsLock);
3167 return thisPtr.PerformResumption();
3170 bool PerformResumption()
3172 // We always have the lock when entering this method
3174 bool waitFinishedSynchronously;
3175 bool completeSelf = false;
3177 // For ProtocolBookmark without Out-Of-Order messaging support, we will throw and
3178 // propagate Fault to client in case of invalid state (similar to management commands).
3179 // Otherwise, the result consistent with WorkflowApplication will be return and
3180 // the caller (eg. delay extension or OOM) needs to handle them accordingly.
3181 if (this.isResumeProtocolBookmark && this.instance.BufferedReceiveManager == null)
3183 this.instance.ValidateStateForResumeProtocolBookmark();
3187 if (this.instance.AreBookmarksInvalid(out this.resumptionResult))
3189 return TrackPerformResumption(true);
3195 waitFinishedSynchronously = false;
3197 bool bufferedReceiveEnabled = this.isResumeProtocolBookmark && this.instance.BufferedReceiveManager != null;
3198 this.resumptionResult = this.instance.ResumeProtocolBookmarkCore(this.bookmark, this.value, this.bookmarkScope, bufferedReceiveEnabled, ref this.waitHandle, ref this.ownsLock);
3199 if (this.resumptionResult == BookmarkResumptionResult.NotReady && !bufferedReceiveEnabled && (this.instance.serviceHost.FilterResumeTimeout.TotalSeconds > 0))
3201 if (nextIdleCallback == null)
3203 nextIdleCallback = new Action<object, TimeoutException>(OnNextIdle);
3206 if (this.waitHandle.WaitAsync(nextIdleCallback, this, !this.isResumeProtocolBookmark ? this.timeoutHelper.RemainingTime() : this.nextIdleTimeoutHelper.RemainingTime()))
3208 // We now have the lock
3209 this.ownsLock = true;
3211 // We should retry the resumption synchronously
3212 waitFinishedSynchronously = true;
3221 completeSelf = true;
3226 while (waitFinishedSynchronously);
3228 return TrackPerformResumption(completeSelf);
3231 bool TrackPerformResumption(bool completeSelf)
3233 if (this.instance.Controller.HasPendingTrackingRecords)
3235 Fx.Assert(completeSelf, "CompleteSelf should be true at this point.");
3237 IAsyncResult result = this.instance.Controller.BeginFlushTrackingRecords(this.instance.trackTimeout, PrepareAsyncCompletion(handleEndTrack), this);
3238 completeSelf = SyncContinue(result);
3241 return completeSelf;
3244 static bool HandleEndTrack(IAsyncResult result)
3246 ResumeProtocolBookmarkAsyncResult thisPtr = (ResumeProtocolBookmarkAsyncResult)result.AsyncState;
3247 thisPtr.instance.Controller.EndFlushTrackingRecords(result);
3249 if (thisPtr.ownsLock)
3251 thisPtr.instance.ReleaseLock(ref thisPtr.ownsLock);
3253 if (thisPtr.referenceAcquired)
3255 thisPtr.instance.ReleaseReference();
3256 thisPtr.referenceAcquired = false;
3261 static void OnNextIdle(object state, TimeoutException asyncException)
3263 ResumeProtocolBookmarkAsyncResult thisPtr = (ResumeProtocolBookmarkAsyncResult)state;
3265 if (asyncException != null)
3267 lock (thisPtr.instance.activeOperationsLock)
3269 // If the waitHandle is not in either of these lists then it must have
3270 // been removed by the Set() path - that means we've got the lock, so let's
3271 // just run with it (IE - swallow the exception).
3272 if (thisPtr.instance.nextIdleWaiters.Remove(thisPtr.waitHandle) || thisPtr.instance.idleWaiters.Remove(thisPtr.waitHandle))
3274 thisPtr.Complete(false, asyncException);
3280 thisPtr.ownsLock = true;
3282 bool completeSelf = true;
3283 Exception completionException = null;
3287 completeSelf = thisPtr.PerformResumption();
3296 completionException = e;
3302 thisPtr.instance.ReleaseLock(ref thisPtr.ownsLock);
3308 thisPtr.Complete(false, completionException);
3312 static void Finally(AsyncResult result, Exception completionException)
3314 ResumeProtocolBookmarkAsyncResult thisPtr = (ResumeProtocolBookmarkAsyncResult)result;
3317 if (thisPtr.ownsLock)
3319 thisPtr.instance.ReleaseLock(ref thisPtr.ownsLock);
3324 if (thisPtr.referenceAcquired)
3326 thisPtr.instance.ReleaseReference();
3327 thisPtr.referenceAcquired = false;
3333 class UnloadOrPersistAsyncResult : TransactedAsyncResult
3335 static FastAsyncCallback lockAcquiredCallback = new FastAsyncCallback(OnLockAcquired);
3336 static AsyncCompletion persistedCallback = new AsyncCompletion(OnPersisted);
3337 static AsyncCompletion savedCallback = new AsyncCompletion(OnSaved);
3338 static AsyncCompletion waitForCanPersistCallback = new AsyncCompletion(OnWaitForCanPersist);
3339 static AsyncCompletion providerOpenedCallback = new AsyncCompletion(OnProviderOpened);
3340 static AsyncCompletion outermostCallback = new AsyncCompletion(OutermostCallback);
3341 static AsyncCompletion trackingCompleteCallback = new AsyncCompletion(OnTrackingComplete);
3342 static AsyncCompletion completeContextCallback = new AsyncCompletion(OnCompleteContext);
3343 static AsyncCompletion notifyCompletionCallback = new AsyncCompletion(OnNotifyCompletion);
3344 static Action<AsyncResult, Exception> completeCallback = new Action<AsyncResult, Exception>(OnComplete);
3346 WorkflowServiceInstance instance;
3348 SaveStatus saveStatus;
3349 TimeoutHelper timeoutHelper;
3350 PersistenceOperation operation;
3351 WorkflowPersistenceContext context;
3352 AsyncCompletion nextInnerAsyncCompletion;
3353 IDictionary<XName, InstanceValue> data;
3354 PersistencePipeline pipeline;
3356 bool isWorkflowThread;
3360 bool isCompletionTransactionRequired;
3361 DependentTransaction dependentTransaction;
3362 bool isIdlePolicyPersist;
3365 public UnloadOrPersistAsyncResult(WorkflowServiceInstance instance, PersistenceOperation operation,
3366 bool isWorkflowThread, bool isTry, TimeSpan timeout, AsyncCallback callback, object state)
3367 : base(callback, state)
3369 // The isTry flag is only true when this is an idle policy initiated persist/unload.
3371 Fx.Assert((isWorkflowThread && !isTry) || !isWorkflowThread, "Either we're the workflow thread and NOT a try or we're not a workflow thread.");
3373 this.instance = instance;
3374 this.timeoutHelper = new TimeoutHelper(timeout);
3375 this.operation = operation;
3376 this.isWorkflowThread = isWorkflowThread;
3378 this.tryResult = true;
3379 this.isUnloaded = (operation == PersistenceOperation.Unload || operation == PersistenceOperation.Delete);
3380 this.saveStatus = SaveStatus.Locked;
3381 this.isCompletionTransactionRequired = this.isUnloaded && instance.Controller.State == WorkflowInstanceState.Complete &&
3382 instance.creationContext != null && instance.creationContext.IsCompletionTransactionRequired;
3383 this.isIdlePolicyPersist = isTry && operation == PersistenceOperation.Save;
3385 if (operation == PersistenceOperation.Unload)
3387 this.saveStatus = SaveStatus.Unlocked;
3389 else if (operation == PersistenceOperation.Delete)
3391 this.saveStatus = SaveStatus.Completed;
3393 else if (operation == PersistenceOperation.Save)
3398 // Save off the current transaction in case we have an async operation before we end up creating
3399 // the WorkflowPersistenceContext and create it on another thread. Do a simple clone here to prevent
3400 // the object referenced by Transaction.Current from disposing before we get around to referencing it
3401 // when we create the WorkflowPersistenceContext.
3403 // This will throw TransactionAbortedException by design, if the transaction is already rolled back.
3404 Transaction currentTransaction = Transaction.Current;
3405 if (currentTransaction != null)
3407 OnCompleting = UnloadOrPersistAsyncResult.completeCallback;
3408 this.dependentTransaction = currentTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
3411 bool completeSelf = true;
3412 bool success = false;
3415 if (this.isWorkflowThread)
3417 Fx.Assert(this.instance.Controller.IsPersistable, "The runtime won't schedule this work item unless we've passed the guard");
3419 // We're an internal persistence on the workflow thread which means
3420 // that we are passed the guard already, we have the lock, and we know
3421 // we aren't detached.
3423 completeSelf = OpenProvider();
3429 completeSelf = LockAndPassGuard();
3435 Fx.Assert(!this.isWorkflowThread, "We should never be calling ReleaseLock if this is the workflow thread.");
3437 this.instance.ReleaseLock(ref this.ownsLock, this.isIdlePolicyPersist && this.tryResult);
3447 if (this.dependentTransaction != null)
3449 this.dependentTransaction.Complete();
3460 [Fx.Tag.SecurityNote(Critical = "Critical because it accesses UnsafeNativeMethods.QueryPerformanceCounter.",
3461 Safe = "Safe because we only make the call if PartialTrustHelper.AppDomainFullyTrusted is true.")]
3462 [SecuritySafeCritical]
3465 if (PartialTrustHelpers.AppDomainFullyTrusted && UnsafeNativeMethods.QueryPerformanceCounter(out this.startTime) == 0)
3467 this.startTime = -1;
3471 bool LockAndPassGuard()
3473 if (this.instance.AcquireLockAsync(this.timeoutHelper.RemainingTime(), ref this.ownsLock, lockAcquiredCallback, this))
3483 if (this.operation == PersistenceOperation.Unload)
3485 if (!this.instance.ValidateStateForUnload())
3492 this.instance.ValidateStateForPersist();
3495 if (this.instance.Controller.IsPersistable)
3497 return OpenProvider();
3503 this.tryResult = false;
3507 IAsyncResult result = this.instance.BeginWaitForCanPersist(ref this.ownsLock, this.timeoutHelper.RemainingTime(),
3508 PrepareInnerAsyncCompletion(waitForCanPersistCallback), this);
3509 if (result.CompletedSynchronously)
3511 return OnWaitForCanPersist(result);
3520 [Fx.Tag.SecurityNote(Critical = "Critical because it accesses UnsafeNativeMethods.QueryPerformanceCounter.",
3521 Safe = "Safe because we only make the call if PartialTrustHelper.AppDomainFullyTrusted is true.")]
3522 [SecuritySafeCritical]
3525 long currentTime = 0;
3528 if (PartialTrustHelpers.AppDomainFullyTrusted && (this.startTime >= 0) &&
3529 (UnsafeNativeMethods.QueryPerformanceCounter(out currentTime) != 0))
3531 duration = currentTime - this.startTime;
3536 static void OnLockAcquired(object state, Exception asyncException)
3538 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)state;
3540 if (asyncException != null)
3542 // AcquireLock does not return an exception unless it doesn't have the lock
3543 thisPtr.Complete(false, asyncException);
3548 thisPtr.ownsLock = true;
3550 bool completeSelf = true;
3551 Exception completionException = null;
3555 completeSelf = thisPtr.PassGuard();
3564 completionException = e;
3570 Fx.Assert(!thisPtr.isWorkflowThread, "We should never be calling ReleaseLock if this is the workflow thread.");
3572 thisPtr.instance.ReleaseLock(ref thisPtr.ownsLock, thisPtr.isIdlePolicyPersist && thisPtr.tryResult);
3578 thisPtr.Complete(false, completionException);
3584 if (this.operation == PersistenceOperation.Unload)
3586 if (this.instance.state != State.Suspended && !this.instance.IsIdle)
3590 this.tryResult = false;
3596 // Release the last referenceCount
3597 if (!this.instance.TryReleaseLastReference())
3601 this.tryResult = false;
3608 // We finally have the lock and are passed the guard. Let's update our operation if this is an Unload.
3609 if (this.operation == PersistenceOperation.Unload && this.instance.Controller.State == WorkflowInstanceState.Complete)
3611 this.operation = PersistenceOperation.Delete;
3614 bool completedSync = false;
3616 if (this.instance.persistenceContext != null && this.instance.persistenceContext.State == CommunicationState.Created)
3618 IAsyncResult result = this.instance.persistenceContext.BeginOpen(timeoutHelper.RemainingTime(),
3619 PrepareInnerAsyncCompletion(providerOpenedCallback), this);
3621 if (result.CompletedSynchronously)
3623 completedSync = OnProviderOpened(result);
3628 completedSync = Track();
3631 return completedSync;
3634 public static bool End(IAsyncResult result)
3636 UnloadOrPersistAsyncResult thisPtr = AsyncResult.End<UnloadOrPersistAsyncResult>(result);
3638 return thisPtr.tryResult;
3641 static bool OutermostCallback(IAsyncResult result)
3643 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3645 bool completeSelf = true;
3646 AsyncCompletion innerCallback = thisPtr.GetNextInnerAsyncCompletion();
3650 completeSelf = innerCallback(result);
3654 // We're exiting either on purpose or because of an exception
3657 if (thisPtr.updateState)
3659 if (thisPtr.saveStatus != SaveStatus.Locked)
3661 // Stop execution if we've given up the instance lock
3662 thisPtr.instance.isRunnable = false;
3665 if (thisPtr.isUnloaded)
3667 thisPtr.instance.MarkUnloaded();
3669 if (thisPtr.isIdlePolicyPersist && thisPtr.tryResult)
3671 thisPtr.instance.DecrementBusyCount();
3675 // We don't want to release the lock if we're the workflow thread
3676 if (!thisPtr.isWorkflowThread)
3678 thisPtr.instance.ReleaseLock(ref thisPtr.ownsLock, thisPtr.isIdlePolicyPersist && thisPtr.tryResult);
3683 return completeSelf;
3686 AsyncCompletion GetNextInnerAsyncCompletion()
3688 AsyncCompletion next = this.nextInnerAsyncCompletion;
3690 Fx.Assert(this.nextInnerAsyncCompletion != null, "Must have had one if we are calling GetNext");
3691 this.nextInnerAsyncCompletion = null;
3696 AsyncCallback PrepareInnerAsyncCompletion(AsyncCompletion innerCallback)
3698 this.nextInnerAsyncCompletion = innerCallback;
3700 return PrepareAsyncCompletion(outermostCallback);
3703 static bool OnWaitForCanPersist(IAsyncResult result)
3705 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3707 thisPtr.instance.EndWaitForCanPersist(result, ref thisPtr.ownsLock);
3709 return thisPtr.OpenProvider();
3712 static bool OnProviderOpened(IAsyncResult result)
3714 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3716 thisPtr.instance.persistenceContext.EndOpen(result);
3718 return thisPtr.Track();
3723 // Do the tracking before preparing in case the tracking data is being pushed into
3724 // an extension and persisted transactionally with the instance state.
3726 if (this.instance.persistenceContext != null)
3728 // We only track the persistence operation if we actually
3729 // are persisting (and not just hitting PersistenceParticipants)
3730 this.instance.TrackPersistence(this.operation);
3733 if (this.instance.Controller.HasPendingTrackingRecords)
3735 IAsyncResult result = this.instance.Controller.BeginFlushTrackingRecords(this.instance.trackTimeout, PrepareInnerAsyncCompletion(trackingCompleteCallback), this);
3736 return SyncContinue(result);
3740 return CollectAndMap();
3744 static bool OnTrackingComplete(IAsyncResult result)
3746 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3748 thisPtr.instance.Controller.EndFlushTrackingRecords(result);
3750 return thisPtr.CollectAndMap();
3753 bool CollectAndMap()
3755 // From this point forward we'll update the state unless we get a persistence exception
3756 this.updateState = true;
3758 Dictionary<XName, InstanceValue> initialPersistenceData = this.instance.GeneratePersistenceData();
3760 bool success = false;
3763 List<IPersistencePipelineModule> modules = this.instance.PipelineModules;
3764 if (modules != null)
3766 Fx.Assert(modules.Count > 0, "should only setup modules if we have some");
3767 this.pipeline = new PersistencePipeline(modules, initialPersistenceData);
3768 this.pipeline.Collect();
3769 this.pipeline.Map();
3770 this.data = this.pipeline.Values;
3774 this.data = initialPersistenceData;
3780 if (!success && this.context != null)
3782 this.context.Abort();
3786 if (this.instance.persistenceContext != null)
3798 IAsyncResult result = null;
3801 if (this.operation == PersistenceOperation.Delete)
3803 this.saveStatus = SaveStatus.Completed;
3806 if (this.context == null)
3808 this.context = new WorkflowPersistenceContext(this.instance, (this.pipeline != null && this.pipeline.IsSaveTransactionRequired) || this.isCompletionTransactionRequired,
3809 this.dependentTransaction, this.instance.persistTimeout);
3812 using (PrepareTransactionalCall(this.context.PublicTransaction))
3814 result = this.instance.persistenceContext.BeginSave(this.data, this.saveStatus, this.instance.persistTimeout, PrepareInnerAsyncCompletion(persistedCallback), this);
3817 catch (InstancePersistenceException)
3819 this.updateState = false;
3824 if (result == null && this.context != null)
3826 this.context.Abort();
3830 return SyncContinue(result);
3833 static bool OnPersisted(IAsyncResult result)
3835 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3836 bool success = false;
3839 thisPtr.instance.persistenceContext.EndSave(result);
3842 catch (InstancePersistenceException)
3844 thisPtr.updateState = false;
3851 thisPtr.context.Abort();
3855 return thisPtr.Save();
3860 if (this.pipeline != null)
3862 IAsyncResult result = null;
3865 if (this.context == null)
3867 this.context = new WorkflowPersistenceContext(this.instance, this.pipeline.IsSaveTransactionRequired || this.isCompletionTransactionRequired,
3868 this.dependentTransaction, this.instance.persistTimeout);
3871 this.instance.persistencePipelineInUse = this.pipeline;
3872 Thread.MemoryBarrier();
3873 if (this.instance.abortingExtensions)
3875 throw FxTrace.Exception.AsError(new OperationCanceledException(SR.DefaultAbortReason));
3878 using (PrepareTransactionalCall(this.context.PublicTransaction))
3880 result = this.pipeline.BeginSave(this.timeoutHelper.RemainingTime(), PrepareInnerAsyncCompletion(savedCallback), this);
3887 this.instance.persistencePipelineInUse = null;
3888 if (this.context != null)
3890 this.context.Abort();
3894 return SyncContinue(result);
3898 return NotifyCompletion();
3902 static bool OnSaved(IAsyncResult result)
3904 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3906 bool success = false;
3909 thisPtr.pipeline.EndSave(result);
3914 thisPtr.instance.persistencePipelineInUse = null;
3917 thisPtr.context.Abort();
3921 return thisPtr.NotifyCompletion();
3924 bool NotifyCompletion()
3926 if (this.isUnloaded && this.instance.Controller.State == WorkflowInstanceState.Complete && this.instance.creationContext != null)
3928 IAsyncResult result = null;
3931 if (this.context == null)
3933 this.context = new WorkflowPersistenceContext(this.instance, this.isCompletionTransactionRequired,
3934 this.dependentTransaction, this.instance.persistTimeout);
3937 using (PrepareTransactionalCall(this.context.PublicTransaction))
3939 result = this.instance.creationContext.OnBeginWorkflowCompleted(this.instance.completionState, this.instance.workflowOutputs, this.instance.terminationException,
3940 this.timeoutHelper.RemainingTime(), PrepareInnerAsyncCompletion(notifyCompletionCallback), this);
3943 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowCompletionAsyncResultCannotBeNull));
3949 if (result == null && this.context != null)
3951 this.context.Abort();
3954 return SyncContinue(result);
3958 return CompleteContext();
3962 static bool OnNotifyCompletion(IAsyncResult result)
3964 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
3966 bool success = false;
3969 thisPtr.instance.creationContext.OnEndWorkflowCompleted(result);
3976 thisPtr.context.Abort();
3980 return thisPtr.CompleteContext();
3983 bool CompleteContext()
3985 bool wentAsync = false;
3986 IAsyncResult completeResult = null;
3988 // Computing Persist Duration.
3989 if (this.operation == PersistenceOperation.Save)
3991 this.instance.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowPersistDuration(GetDuration());
3994 if (this.context != null)
3996 wentAsync = this.context.TryBeginComplete(this.PrepareInnerAsyncCompletion(completeContextCallback), this, out completeResult);
3999 // we have persisted deleted state. this is to address TransactedTerminate avoiding
4000 // multiple deleted persistence.
4001 this.instance.hasPersistedDeleted = this.operation == PersistenceOperation.Delete;
4005 Fx.Assert(completeResult != null, "We shouldn't have null here because we would have rethrown or gotten false for went async.");
4006 return SyncContinue(completeResult);
4010 // We completed synchronously if we didn't get an async result out of
4017 static bool OnCompleteContext(IAsyncResult result)
4019 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result.AsyncState;
4020 thisPtr.context.EndComplete(result);
4024 static void OnComplete(AsyncResult result, Exception exception)
4026 UnloadOrPersistAsyncResult thisPtr = (UnloadOrPersistAsyncResult)result;
4027 if (thisPtr.dependentTransaction != null)
4029 thisPtr.dependentTransaction.Complete();
4034 abstract class SimpleOperationAsyncResult : AsyncResult
4036 static FastAsyncCallback lockAcquiredCallback = new FastAsyncCallback(OnLockAcquired);
4037 static Action<AsyncResult, Exception> onCompleting = new Action<AsyncResult, Exception>(Finally);
4038 static AsyncCompletion handleEndPerformOperation;
4039 static AsyncCompletion handleEndTrack;
4041 protected WorkflowServiceInstance instance;
4042 protected TimeoutHelper timeoutHelper;
4043 protected bool ownsLock;
4045 protected SimpleOperationAsyncResult(WorkflowServiceInstance instance, Transaction transaction, AsyncCallback callback, object state)
4046 : base(callback, state)
4048 this.instance = instance;
4049 this.OperationTransaction = transaction;
4050 this.OnCompleting = onCompleting;
4053 protected WorkflowServiceInstance Instance
4057 return this.instance;
4061 protected Transaction OperationTransaction
4067 protected virtual bool IsSynchronousOperation
4075 protected void Run(TimeSpan timeout)
4077 this.timeoutHelper = new TimeoutHelper(timeout);
4079 Exception completionException = null;
4080 bool completeSelf = true;
4082 if (this.instance.AcquireLockAsync(this.timeoutHelper.RemainingTime(), ref this.ownsLock, lockAcquiredCallback, this))
4086 completeSelf = HandleLockAcquired();
4088 catch (Exception exception)
4090 if (Fx.IsFatal(exception))
4094 completionException = exception;
4099 completeSelf = false;
4104 Complete(true, completionException);
4108 static void OnLockAcquired(object state, Exception asyncException)
4110 SimpleOperationAsyncResult thisPtr = (SimpleOperationAsyncResult)state;
4112 if (asyncException != null)
4114 thisPtr.Complete(false, asyncException);
4118 thisPtr.ownsLock = true;
4120 Exception completionException = null;
4121 bool completeSelf = true;
4125 completeSelf = thisPtr.HandleLockAcquired();
4127 catch (Exception exception)
4129 if (Fx.IsFatal(exception))
4133 completionException = exception;
4138 thisPtr.Complete(false, completionException);
4143 bool HandleLockAcquired()
4145 if (ValidateState())
4147 return AttachTransaction();
4155 bool AttachTransaction()
4157 if (this.OperationTransaction != null && this.Instance.transactionContext == null)
4159 this.Instance.transactionContext = new TransactionContext(this.Instance, this.OperationTransaction);
4160 this.Instance.isInTransaction = true;
4161 this.Instance.isRunnable = false;
4164 if (this.IsSynchronousOperation)
4171 if (handleEndPerformOperation == null)
4173 handleEndPerformOperation = new AsyncCompletion(HandleEndPerformOperation);
4176 IAsyncResult result = BeginPerformOperation(PrepareAsyncCompletion(handleEndPerformOperation), this);
4177 if (result.CompletedSynchronously)
4179 return HandleEndPerformOperation(result);
4188 static bool HandleEndPerformOperation(IAsyncResult result)
4190 SimpleOperationAsyncResult thisPtr = (SimpleOperationAsyncResult)result.AsyncState;
4191 thisPtr.EndPerformOperation(result);
4192 return thisPtr.Track();
4197 // For aborted, the AbortInstance will handle tracking.
4198 if (this.instance.state != State.Aborted && this.instance.Controller.HasPendingTrackingRecords)
4200 if (handleEndTrack == null)
4202 handleEndTrack = new AsyncCompletion(HandleEndTrack);
4205 IAsyncResult result = this.instance.Controller.BeginFlushTrackingRecords(this.instance.trackTimeout, PrepareAsyncCompletion(handleEndTrack), this);
4206 if (result.CompletedSynchronously)
4208 return HandleEndTrack(result);
4217 return ReleaseLock();
4221 static bool HandleEndTrack(IAsyncResult result)
4223 SimpleOperationAsyncResult thisPtr = (SimpleOperationAsyncResult)result.AsyncState;
4224 thisPtr.instance.Controller.EndFlushTrackingRecords(result);
4225 return thisPtr.ReleaseLock();
4230 this.instance.ReleaseLock(ref this.ownsLock);
4235 static void Finally(AsyncResult result, Exception completionException)
4237 SimpleOperationAsyncResult thisPtr = (SimpleOperationAsyncResult)result;
4238 if (thisPtr.ownsLock)
4240 thisPtr.instance.ReleaseLock(ref thisPtr.ownsLock);
4244 protected abstract bool ValidateState();
4245 protected abstract void PerformOperation();
4246 protected virtual IAsyncResult BeginPerformOperation(AsyncCallback callback, object state)
4248 throw Fx.AssertAndThrow("Should not reach here!");
4250 protected virtual void EndPerformOperation(IAsyncResult result)
4252 throw Fx.AssertAndThrow("Should not reach here!");
4254 protected abstract void PostOperation();
4257 class TerminateAsyncResult : SimpleOperationAsyncResult
4261 TerminateAsyncResult(WorkflowServiceInstance instance, Exception reason, Transaction transaction, AsyncCallback callback, object state)
4262 : base(instance, transaction, callback, state)
4264 this.reason = reason;
4267 public static TerminateAsyncResult Create(WorkflowServiceInstance instance, Exception reason, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
4269 TerminateAsyncResult result = new TerminateAsyncResult(instance, reason, transaction, callback, state);
4270 result.Run(timeout);
4274 public static void End(IAsyncResult result)
4276 AsyncResult.End<TerminateAsyncResult>(result);
4279 protected override bool ValidateState()
4281 return this.Instance.ValidateStateForTerminate(this.OperationTransaction);
4284 protected override void PerformOperation()
4286 this.Instance.Controller.Terminate(reason);
4288 // Reset suspended reason for Cancel and Terminate
4289 if (this.Instance.persistenceContext.IsSuspended)
4291 this.Instance.persistenceContext.IsSuspended = false;
4292 this.Instance.persistenceContext.SuspendedReason = null;
4295 // For non-transacted, we used the normal pulse to complete/unload the workflow.
4296 if (!this.Instance.isInTransaction)
4298 this.Instance.isRunnable = true;
4299 this.Instance.state = State.Active;
4301 // For transacted, the unload will happen at Tx committed time.
4304 this.Instance.GetCompletionState();
4308 protected override void PostOperation()
4310 this.Instance.CompletePendingOperations();
4314 class AbandonAsyncResult : SimpleOperationAsyncResult
4318 // The shouldTrackAbort flag is only false when idle policy has TimeToPersist < TimeToUnload.
4319 bool shouldTrackAbort;
4321 AbandonAsyncResult(WorkflowServiceInstance instance, Exception reason, bool shouldTrackAbort, AsyncCallback callback, object state)
4322 : base(instance, null, callback, state)
4324 this.reason = reason;
4325 this.shouldTrackAbort = shouldTrackAbort;
4328 public static AbandonAsyncResult Create(WorkflowServiceInstance instance, Exception reason, bool shouldTrackAbort, TimeSpan timeout, AsyncCallback callback, object state)
4330 AbandonAsyncResult result = new AbandonAsyncResult(instance, reason, shouldTrackAbort, callback, state);
4331 result.Run(timeout);
4335 protected override bool IsSynchronousOperation
4339 // We go through the synchronous code path only when we want to terminate the unload.
4340 // We want to terminate the unload only when
4341 // TimeToPersist < TimeToUnload AND instance is dirty and waiting to be persisted by idle policy.
4343 // The hasDataToPersist flag should only be read under the executor lock.
4344 if (!this.shouldTrackAbort && this.Instance.hasDataToPersist)
4355 public static void End(IAsyncResult result)
4357 AsyncResult.End<AbandonAsyncResult>(result);
4360 protected override bool ValidateState()
4362 return this.Instance.ValidateStateForAbort();
4365 protected override void PerformOperation()
4367 // This is the synchronous code path. This path terminates the unload and leaves the instance intact.
4368 Fx.Assert(!this.shouldTrackAbort && this.Instance.hasDataToPersist, "We should only get here when we need to terminate the unload.");
4370 // Since reference count has already been decremented to 0 by now, we should set it back to 1.
4371 this.Instance.RecoverLastReference();
4374 protected override IAsyncResult BeginPerformOperation(AsyncCallback callback, object state)
4378 return this.Instance.persistenceContext.BeginRelease(this.Instance.persistTimeout, callback, state);
4380 catch (Exception exception)
4382 if (Fx.IsFatal(exception))
4387 this.Instance.AbortInstance(this.reason, true);
4392 protected override void EndPerformOperation(IAsyncResult result)
4396 this.Instance.persistenceContext.EndRelease(result);
4397 if (!this.shouldTrackAbort && this.Instance.Controller.TrackingEnabled)
4399 this.Instance.Controller.Track(new WorkflowInstanceRecord(this.Instance.Id, this.Instance.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Unloaded, this.Instance.DefinitionIdentity));
4402 if (!this.shouldTrackAbort)
4404 this.instance.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowUnloaded();
4407 this.Instance.AbortInstance(this.reason, true, this.shouldTrackAbort);
4409 catch (Exception exception)
4411 if (Fx.IsFatal(exception))
4416 this.Instance.AbortInstance(this.reason, true);
4421 protected override void PostOperation()
4426 class AbandonAndSuspendAsyncResult : SimpleOperationAsyncResult
4430 AbandonAndSuspendAsyncResult(WorkflowServiceInstance instance, Exception reason, AsyncCallback callback, object state)
4431 : base(instance, null, callback, state)
4433 this.reason = reason;
4436 public static AbandonAndSuspendAsyncResult Create(WorkflowServiceInstance instance, Exception reason, TimeSpan timeout, AsyncCallback callback, object state)
4438 AbandonAndSuspendAsyncResult result = new AbandonAndSuspendAsyncResult(instance, reason, callback, state);
4439 result.Run(timeout);
4443 protected override bool IsSynchronousOperation
4451 public static void End(IAsyncResult result)
4453 AsyncResult.End<AbandonAndSuspendAsyncResult>(result);
4456 protected override bool ValidateState()
4458 return this.Instance.ValidateStateForAbort();
4461 protected override void PerformOperation()
4463 throw Fx.AssertAndThrow("Should not reach here!");
4466 protected override IAsyncResult BeginPerformOperation(AsyncCallback callback, object state)
4470 return this.Instance.persistenceContext.BeginUpdateSuspendMetadata(this.reason, this.Instance.persistTimeout, callback, state);
4472 catch (Exception exception)
4474 if (Fx.IsFatal(exception))
4479 this.Instance.AbortInstance(this.reason, true);
4484 protected override void EndPerformOperation(IAsyncResult result)
4488 this.Instance.persistenceContext.EndUpdateSuspendMetadata(result);
4489 AbandonAndSuspendAsyncResult data = (AbandonAndSuspendAsyncResult)result.AsyncState;
4490 if (this.Instance.Controller.TrackingEnabled)
4492 this.Instance.Controller.Track(new WorkflowInstanceSuspendedRecord(this.Instance.Id, this.Instance.WorkflowDefinition.DisplayName, data.reason.Message, this.Instance.DefinitionIdentity));
4495 this.Instance.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowSuspended();
4499 this.Instance.AbortInstance(this.reason, true);
4503 protected override void PostOperation()
4508 class CancelAsyncResult : SimpleOperationAsyncResult
4510 CancelAsyncResult(WorkflowServiceInstance instance, Transaction transaction, AsyncCallback callback, object state)
4511 : base(instance, transaction, callback, state)
4515 public static CancelAsyncResult Create(WorkflowServiceInstance instance, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
4517 CancelAsyncResult result = new CancelAsyncResult(instance, transaction, callback, state);
4518 result.Run(timeout);
4522 public static void End(IAsyncResult result)
4524 AsyncResult.End<CancelAsyncResult>(result);
4527 protected override bool ValidateState()
4529 return this.Instance.ValidateStateForCancel(this.OperationTransaction);
4532 protected override void PerformOperation()
4534 this.Instance.Controller.ScheduleCancel();
4536 // Reset suspended reason for Cancel and Terminate
4537 if (this.Instance.persistenceContext.IsSuspended)
4539 this.Instance.persistenceContext.IsSuspended = false;
4540 this.Instance.persistenceContext.SuspendedReason = null;
4543 // Cancel implies a state change to runnable.
4544 if (!this.Instance.isInTransaction)
4546 this.Instance.isRunnable = true;
4547 this.Instance.state = State.Active;
4549 // For transacted, the unload will happen at Tx committed time.
4552 this.Instance.isTransactedCancelled = true;
4556 protected override void PostOperation()
4558 this.Instance.CompletePendingOperations();
4562 class RunAsyncResult : SimpleOperationAsyncResult
4564 string operationName;
4566 RunAsyncResult(WorkflowServiceInstance instance, Transaction transaction, string operationName, AsyncCallback callback, object state)
4567 : base(instance, transaction, callback, state)
4569 this.operationName = operationName;
4572 public static RunAsyncResult Create(WorkflowServiceInstance instance, Transaction transaction, string operationName, TimeSpan timeout, AsyncCallback callback, object state)
4574 RunAsyncResult result = new RunAsyncResult(instance, transaction, operationName, callback, state);
4575 result.Run(timeout);
4579 public static void End(IAsyncResult result)
4581 AsyncResult.End<RunAsyncResult>(result);
4584 protected override bool ValidateState()
4586 return this.Instance.ValidateStateForRun(this.OperationTransaction, this.operationName);
4589 protected override void PerformOperation()
4591 if (!this.Instance.isInTransaction)
4593 this.Instance.RunCore();
4597 protected override void PostOperation()
4602 class SuspendAsyncResult : SimpleOperationAsyncResult
4607 SuspendAsyncResult(WorkflowServiceInstance instance, bool isUnlocked, string reason, Transaction transaction, AsyncCallback callback, object state)
4608 : base(instance, transaction, callback, state)
4610 this.isUnlocked = isUnlocked;
4611 this.reason = reason;
4614 public static SuspendAsyncResult Create(WorkflowServiceInstance instance, bool isUnlocked, string reason, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
4616 SuspendAsyncResult result = new SuspendAsyncResult(instance, isUnlocked, reason, transaction, callback, state);
4617 result.Run(timeout);
4621 public static void End(IAsyncResult result)
4623 AsyncResult.End<SuspendAsyncResult>(result);
4626 protected override bool IsSynchronousOperation
4634 protected override bool ValidateState()
4636 return this.Instance.ValidateStateForSuspend(this.OperationTransaction);
4639 protected override void PerformOperation()
4641 throw Fx.AssertAndThrow("Should not reach here!");
4644 protected override IAsyncResult BeginPerformOperation(AsyncCallback callback, object state)
4646 return new SuspendCoreAsyncResult(this, callback, state);
4649 protected override void EndPerformOperation(IAsyncResult result)
4651 SuspendCoreAsyncResult.End(result);
4654 protected override void PostOperation()
4656 this.Instance.CompletePendingOperations();
4659 class SuspendCoreAsyncResult : AsyncResult
4661 static AsyncCompletion handleEndWaitForCanPersist = new AsyncCompletion(HandleEndWaitForCanPersist);
4663 SuspendAsyncResult parent;
4665 public SuspendCoreAsyncResult(SuspendAsyncResult parent, AsyncCallback callback, object state)
4666 : base(callback, state)
4668 this.parent = parent;
4670 IAsyncResult result = this.parent.Instance.BeginWaitForCanPersist(ref this.parent.ownsLock, this.parent.timeoutHelper.RemainingTime(),
4671 PrepareAsyncCompletion(handleEndWaitForCanPersist), this);
4672 if (SyncContinue(result))
4674 this.Complete(true);
4678 public static void End(IAsyncResult result)
4680 AsyncResult.End<SuspendCoreAsyncResult>(result);
4683 static bool HandleEndWaitForCanPersist(IAsyncResult result)
4685 SuspendCoreAsyncResult thisPtr = (SuspendCoreAsyncResult)result.AsyncState;
4686 thisPtr.parent.Instance.EndWaitForCanPersist(result, ref thisPtr.parent.ownsLock);
4688 thisPtr.parent.Instance.persistenceContext.IsSuspended = true;
4689 thisPtr.parent.Instance.persistenceContext.SuspendedReason = thisPtr.parent.reason;
4690 thisPtr.parent.Instance.state = State.Suspended;
4692 if (thisPtr.parent.Instance.Controller.TrackingEnabled)
4694 thisPtr.parent.Instance.Controller.Track(new WorkflowInstanceSuspendedRecord(thisPtr.parent.Instance.Id, thisPtr.parent.Instance.WorkflowDefinition.DisplayName, thisPtr.parent.reason, thisPtr.parent.Instance.DefinitionIdentity));
4697 thisPtr.parent.instance.serviceHost.WorkflowServiceHostPerformanceCounters.WorkflowSuspended();
4699 // This is to handle a corner case where Pause is called
4700 // from an event handler:
4701 // Case 1: Called while executing - pauses the scheduler
4702 // in order to obtain the lock and ReleaseLock never
4704 // Case 2: Called while not executing - no need to pause
4705 // the scheduler because ReleaseLock makes sure never
4706 // to tell it to post.
4707 // Case 3: Called from UnhandledException handler - the
4708 // scheduler is unpaused and ReleaseLock doesn't
4709 // control the fate of this thread. Instead, this
4710 // thread will return to the scheduler unless we
4711 // tell it to Pause here.
4712 thisPtr.parent.Instance.Controller.RequestPause();
4719 class UnsuspendAsyncResult : SimpleOperationAsyncResult
4721 UnsuspendAsyncResult(WorkflowServiceInstance instance, Transaction transaction, AsyncCallback callback, object state)
4722 : base(instance, transaction, callback, state)
4726 public static UnsuspendAsyncResult Create(WorkflowServiceInstance instance, Transaction transaction, TimeSpan timeout, AsyncCallback callback, object state)
4728 UnsuspendAsyncResult result = new UnsuspendAsyncResult(instance, transaction, callback, state);
4729 result.Run(timeout);
4733 public static void End(IAsyncResult result)
4735 AsyncResult.End<UnsuspendAsyncResult>(result);
4738 protected override bool ValidateState()
4740 return this.Instance.ValidateStateForUnsuspend(this.OperationTransaction);
4743 protected override void PerformOperation()
4745 if (!this.Instance.isInTransaction)
4747 this.Instance.isRunnable = true;
4749 this.Instance.persistenceContext.IsSuspended = false;
4750 this.Instance.persistenceContext.SuspendedReason = null;
4751 this.Instance.state = State.Active;
4753 if (this.Instance.Controller.TrackingEnabled)
4755 this.Instance.Controller.Track(new WorkflowInstanceRecord(this.Instance.Id, this.Instance.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Unsuspended, this.Instance.DefinitionIdentity));
4759 protected override void PostOperation()
4764 class AcquireLockOnIdleAsyncResult : AsyncResult
4766 static FastAsyncCallback lockAcquiredCallback = new FastAsyncCallback(OnLockAcquired);
4767 static Action<object, TimeoutException> idleReceivedCallback = new Action<object, TimeoutException>(OnIdleReceived);
4769 AsyncWaitHandle idleEvent;
4770 WorkflowServiceInstance instance;
4771 TimeoutHelper timeoutHelper;
4772 bool acquiredLockAsynchronously;
4774 public AcquireLockOnIdleAsyncResult(WorkflowServiceInstance instance, TimeSpan timeout, ref bool ownsLock, AsyncCallback callback, object state)
4775 : base(callback, state)
4777 Fx.Assert(!ownsLock, "We should never call acquire if we already think we own the lock.");
4779 // We cannot just hand off the lock if we are in a handler thread
4780 // because this might eventually go async (during the operation)
4781 // and we could have multiple operations occurring concurrently.
4783 this.instance = instance;
4784 this.timeoutHelper = new TimeoutHelper(timeout);
4786 bool incrementedActiveOperations = false;
4787 bool decrementActiveOperations = true;
4788 bool completeSelf = true;
4789 object lockToken = null;
4793 lock (this.instance.activeOperationsLock)
4800 this.instance.activeOperations++;
4801 incrementedActiveOperations = true;
4804 this.instance.executorLock.SetupWaiter(ref lockToken);
4807 completeSelf = this.instance.executorLock.EnterAsync(this.timeoutHelper.RemainingTime(), ref lockToken, ref ownsLock, lockAcquiredCallback, this);
4809 // We don't want to decrement the count if we went async
4810 // because the async callback will do the decrement
4811 decrementActiveOperations = completeSelf;
4815 if (incrementedActiveOperations && decrementActiveOperations)
4817 lock (this.instance.activeOperationsLock)
4819 this.instance.activeOperations--;
4823 this.instance.executorLock.CleanupWaiter(lockToken, ref ownsLock);
4828 if (CheckState(ref ownsLock))
4835 public static void End(IAsyncResult result)
4837 AsyncResult.End<AcquireLockOnIdleAsyncResult>(result);
4840 public static void End(IAsyncResult result, ref bool ownsLock)
4842 // We don't care about validating type because worst
4843 // case scenario we skip this section and validation
4844 // occurs in the base AsyncResult call.
4845 AcquireLockOnIdleAsyncResult thisPtr = result as AcquireLockOnIdleAsyncResult;
4847 if (thisPtr != null)
4849 ownsLock = thisPtr.acquiredLockAsynchronously;
4852 AsyncResult.End<AcquireLockOnIdleAsyncResult>(result);
4855 static void OnLockAcquired(object state, Exception asyncException)
4857 AcquireLockOnIdleAsyncResult thisPtr = (AcquireLockOnIdleAsyncResult)state;
4859 lock (thisPtr.instance.activeOperationsLock)
4861 thisPtr.instance.activeOperations--;
4864 if (asyncException != null)
4866 thisPtr.Complete(false, asyncException);
4870 bool completeSelf = true;
4871 Exception completionException = null;
4875 thisPtr.acquiredLockAsynchronously = true;
4876 completeSelf = thisPtr.CheckState(ref thisPtr.acquiredLockAsynchronously);
4885 completionException = e;
4890 thisPtr.Complete(false, completionException);
4894 bool CheckState(ref bool ownsLock)
4896 if (this.instance.state == State.Active && !this.instance.isRunnable)
4898 this.instance.RunCore();
4901 // If instance state is non-Active, the AcquireOnIdle will succeed (WSI is doing nothing),
4902 // the caller is responsible for dealing with state vs. operation.
4903 // For instance, ResumeBookmark will call ValidateStateForResumeProtocolBookmark.
4904 if (this.instance.state == State.Active && this.instance.Controller.State == WorkflowInstanceState.Runnable)
4906 this.idleEvent = this.instance.SetupIdleWaiter(ref ownsLock);
4910 if (this.idleEvent.WaitAsync(idleReceivedCallback, this, this.timeoutHelper.RemainingTime()))
4926 if (this.instance.CleanupIdleWaiter(this.idleEvent, e, ref ownsLock))
4936 static void OnIdleReceived(object state, TimeoutException asyncException)
4938 AcquireLockOnIdleAsyncResult thisPtr = (AcquireLockOnIdleAsyncResult)state;
4940 if (asyncException != null)
4942 if (thisPtr.instance.CleanupIdleWaiter(thisPtr.idleEvent, asyncException, ref thisPtr.acquiredLockAsynchronously))
4944 Fx.Assert(!thisPtr.acquiredLockAsynchronously, "We shouldn't own the lock if we're rethrowing");
4945 thisPtr.Complete(false, asyncException);
4949 Fx.Assert(thisPtr.acquiredLockAsynchronously, "We should own the lock if we're ----ing");
4952 thisPtr.acquiredLockAsynchronously = true;
4954 thisPtr.Complete(false, null);
4958 class WaitForCanPersistAsyncResult : AsyncResult
4960 static Action<object, TimeoutException> onWaitEvent;
4961 static FastAsyncCallback onLockAcquired;
4963 WorkflowServiceInstance instance;
4964 TimeoutHelper timeoutHelper;
4967 AsyncWaitHandle checkCanPersistEvent;
4969 public WaitForCanPersistAsyncResult(WorkflowServiceInstance instance, ref bool ownsLock, TimeSpan timeout, AsyncCallback callback, object state)
4970 : base(callback, state)
4972 this.instance = instance;
4973 this.ownsLock = ownsLock;
4974 this.timeoutHelper = new TimeoutHelper(timeout);
4976 Fx.Assert(ownsLock, "Must be called under locked!");
4978 if (WaitForCanPersist())
4984 public static void End(IAsyncResult result, ref bool ownsLock)
4986 // We don't care about validating type because worst
4987 // case scenario we skip this section and validation
4988 // occurs in the base AsyncResult call.
4989 WaitForCanPersistAsyncResult thisPtr = result as WaitForCanPersistAsyncResult;
4991 if (thisPtr != null)
4993 ownsLock = thisPtr.ownsLock;
4996 AsyncResult.End<WaitForCanPersistAsyncResult>(result);
4999 bool WaitForCanPersist()
5001 if (this.instance.Controller.IsPersistable)
5006 this.instance.Controller.PauseWhenPersistable();
5008 this.mustWait = false;
5009 if (this.instance.IsIdle)
5011 if (this.checkCanPersistEvent == null)
5013 this.checkCanPersistEvent = new AsyncWaitHandle(EventResetMode.AutoReset);
5016 // Will be signaled when WF is paused.
5017 this.instance.AddCheckCanPersistWaiter(this);
5018 this.mustWait = true;
5021 this.instance.ReleaseLock(ref this.ownsLock);
5025 if (onWaitEvent == null)
5027 onWaitEvent = new Action<object, TimeoutException>(OnWaitEvent);
5030 if (this.checkCanPersistEvent.WaitAsync(onWaitEvent, this, this.timeoutHelper.RemainingTime()))
5032 return HandleWaitEvent();
5041 return HandleWaitEvent();
5045 static void OnWaitEvent(object state, TimeoutException asyncException)
5047 WaitForCanPersistAsyncResult thisPtr = (WaitForCanPersistAsyncResult)state;
5049 if (asyncException != null)
5051 thisPtr.Complete(false, asyncException);
5055 bool completeSelf = true;
5056 Exception completionException = null;
5060 completeSelf = thisPtr.HandleWaitEvent();
5062 catch (Exception exception)
5064 if (Fx.IsFatal(exception))
5069 completionException = exception;
5074 thisPtr.Complete(false, completionException);
5078 public void SetEvent(ref bool ownsLock)
5080 this.ownsLock = ownsLock;
5082 this.checkCanPersistEvent.Set();
5085 bool HandleWaitEvent()
5087 return AcquireLockWithoutPause();
5090 bool AcquireLockWithoutPause()
5092 if (!this.instance.IsHandlerThread && !this.ownsLock)
5094 if (onLockAcquired == null)
5096 onLockAcquired = new FastAsyncCallback(OnLockAcquired);
5099 if (this.instance.AcquireLockAsync(this.timeoutHelper.RemainingTime(), false, true, ref this.ownsLock, onLockAcquired, this))
5101 return HandleLockAcquired();
5110 return HandleLockAcquired();
5114 static void OnLockAcquired(object state, Exception asyncException)
5116 WaitForCanPersistAsyncResult thisPtr = (WaitForCanPersistAsyncResult)state;
5118 if (asyncException != null)
5120 thisPtr.Complete(false, asyncException);
5124 thisPtr.ownsLock = true;
5126 bool completeSelf = true;
5127 Exception completionException = null;
5131 completeSelf = thisPtr.HandleLockAcquired();
5133 catch (Exception exception)
5135 if (Fx.IsFatal(exception))
5140 completionException = exception;
5145 thisPtr.Complete(false, completionException);
5149 bool HandleLockAcquired()
5151 this.instance.ValidateStateForPersist();
5152 return WaitForCanPersist();
5156 [Fx.Tag.SynchronizationPrimitive(Fx.Tag.BlocksUsing.PrivatePrimitive, SupportsAsync = true, ReleaseMethod = "Exit")]
5157 class WorkflowExecutionLock
5159 static Action<object, TimeoutException> asyncWaiterSignaledCallback = new Action<object, TimeoutException>(OnAsyncWaiterSignaled);
5162 WorkflowServiceInstance instance;
5164 [Fx.Tag.SynchronizationObject(Blocking = false)]
5165 object ThisLock = new object();
5167 [Fx.Tag.SynchronizationObject]
5168 List<object> waiters;
5171 StackTrace exitStack;
5174 public WorkflowExecutionLock(WorkflowServiceInstance instance)
5176 this.instance = instance;
5179 public bool IsLocked
5181 get { return this.owned; }
5184 List<object> Waiters
5188 if (waiters == null)
5190 waiters = new List<object>();
5197 public void SetupWaiter(ref object token)
5199 SetupWaiter(false, ref token);
5202 // The token returned here must be fed to all Enter calls
5203 // and finally to CleanupWaiter by the thread that calls
5204 // SetupWaiter. If the enter goes async (such as EnterAsync
5205 // might) then the caller should NOT call cleanup in the async
5207 public void SetupWaiter(bool isAbortPriority, ref object token)
5216 token = new AsyncWaitHandle();
5218 if (isAbortPriority)
5220 this.Waiters.Insert(0, token);
5224 this.Waiters.Add(token);
5230 public void CleanupWaiter(object token, ref bool ownsLock)
5236 if (!this.waiters.Remove(token))
5238 // If it is not in the list that means we've been
5239 // signaled and now own the lock.
5247 public void Enter(TimeSpan timeout, ref object token, ref bool ownsLock)
5249 Fx.Assert(!ownsLock, "We should never attempt to get the lock if we think we own it.");
5251 if (!TryEnter(timeout, ref token, ref ownsLock))
5253 throw FxTrace.Exception.AsError(new TimeoutException(SR.TimeoutOnOperation(timeout)));
5257 public bool EnterAsync(TimeSpan timeout, ref object token, ref bool ownsLock, FastAsyncCallback callback, object state)
5259 Fx.Assert(!ownsLock, "We should never attempt to get the lock if we think we own it.");
5260 Fx.Assert(callback != null, "must have a non-null call back for async purposes");
5261 Fx.Assert(token is AsyncWaitHandle, "The token must be an AsyncWaitHandle.");
5263 AsyncWaitHandle waitHandle = null;
5281 waitHandle = (AsyncWaitHandle)token;
5284 bool result = false;
5286 if (waitHandle.WaitAsync(asyncWaiterSignaledCallback, new AsyncWaiterData(this, callback, state, waitHandle), timeout))
5288 Fx.Assert(!this.Waiters.Contains(waitHandle), "We should not have this wait handle in the list.");
5290 // Since the waiter is only signaled when they own the lock we won't have
5291 // to set owned to true if this returns true. owned was never set to false
5292 // by Exit in this case.
5302 static void OnAsyncWaiterSignaled(object state, TimeoutException asyncException)
5304 AsyncWaiterData asyncWaiter = (AsyncWaiterData)state;
5306 Exception completionException = asyncException;
5308 if (asyncException != null)
5310 lock (asyncWaiter.Owner.ThisLock)
5312 if (!asyncWaiter.Owner.waiters.Remove(asyncWaiter.Token))
5314 // We raced between timing out and getting signaled.
5315 // We'll take the signal which means we now own the lock
5317 completionException = null;
5322 // Callers of EnterAsync take a null value for the exception to mean
5323 // that they own the lock. Either we were signaled (asyncException was
5324 // null), we got the lock in a ----y way (we nulled the exception when
5325 // we found we weren't in the list), or we don't have the lock (asyncException
5326 // is non-null and we are passing it along).
5327 asyncWaiter.Callback(asyncWaiter.State, completionException);
5330 public bool TryEnter(ref bool ownsLock)
5332 Fx.Assert(!ownsLock, "We should never attempt to get the lock if we think we own it.");
5354 public bool TryEnter(TimeSpan timeout, ref object token, ref bool ownsLock)
5356 Fx.Assert(!ownsLock, "We should never attempt to get the lock if we think we own it.");
5358 AsyncWaitHandle waiter = EnterCore(ref token, ref ownsLock);
5362 Fx.Assert(!ownsLock, "We should not have gotten a waiter if EnterCore gave us the lock.");
5364 if (waiter.Wait(timeout))
5372 // The waiter will be cleaned up by the caller
5378 Fx.Assert(ownsLock, "We didn't have a waiter which means we got the lock.");
5383 AsyncWaitHandle EnterCore(ref object token, ref bool ownsLock)
5385 AsyncWaitHandle waiter = null;
5393 waiter = new AsyncWaitHandle();
5394 this.Waiters.Add(waiter);
5398 waiter = (AsyncWaitHandle)token;
5417 // Returns false if the lock was not released, returns true if released.
5418 public bool Exit(bool keepLockIfNoWaiters, ref bool ownsLock)
5420 Fx.Assert(ownsLock, "We shouldn't call Exit unless we think we own the lock.");
5422 AsyncWaitHandle waiter = null;
5428 string message = InternalSR.InvalidSemaphoreExit;
5431 if (!Fx.FastDebug && exitStack != null)
5433 string originalStack = exitStack.ToString().Replace("\r\n", "\r\n ");
5434 message = string.Format(CultureInfo.InvariantCulture,
5435 "Object synchronization method was called from an unsynchronized block of code. Previous Exit(): {0}", originalStack);
5439 throw FxTrace.Exception.AsError(new SynchronizationLockException(message));
5442 if (this.waiters == null || this.waiters.Count == 0)
5444 if (keepLockIfNoWaiters)
5457 this.instance.StartUnloadInstancePolicyIfNecessary();
5463 exitStack = new StackTrace();
5471 waiter = (AsyncWaitHandle)this.waiters[0];
5472 this.waiters.RemoveAt(0);
5475 // We're giving up the lock to another thread which now has to
5476 // take care of releasing it
5480 // This counts as a successful exit from the point of view
5481 // of callers of Exit.
5485 class AsyncWaiterData
5487 public AsyncWaiterData(WorkflowExecutionLock owner, FastAsyncCallback callback, object state, object token)
5490 this.Callback = callback;
5495 public WorkflowExecutionLock Owner
5501 public FastAsyncCallback Callback
5521 class UnhandledExceptionAsyncData
5523 public UnhandledExceptionAsyncData(WorkflowServiceInstance instance, Exception exception, Activity exceptionSource)
5525 this.Instance = instance;
5526 this.Exception = exception;
5527 this.ExceptionSource = exceptionSource;
5530 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode,
5531 Justification = "Tracking team is considering to provide the exception source as part of the WorkflowInstanceUnhandledException record")]
5532 public Activity ExceptionSource
5538 public WorkflowServiceInstance Instance
5544 public Exception Exception
5551 class WorkflowPersistenceContext
5553 WorkflowServiceInstance instance;
5554 CommittableTransaction contextOwnedTransaction;
5555 Transaction clonedTransaction;
5557 public WorkflowPersistenceContext(WorkflowServiceInstance instance, bool transactionRequired, Transaction transactionToUse, TimeSpan transactionTimeout)
5559 this.instance = instance;
5561 if (transactionToUse != null)
5563 this.clonedTransaction = transactionToUse;
5565 else if (transactionRequired)
5567 this.contextOwnedTransaction = new CommittableTransaction(transactionTimeout);
5568 // Clone it so that we don't pass a CommittableTransaction to the participants
5569 this.clonedTransaction = this.contextOwnedTransaction.Clone();
5573 public Transaction PublicTransaction
5577 return this.clonedTransaction;
5583 if (this.contextOwnedTransaction != null)
5587 this.contextOwnedTransaction.Rollback();
5596 // ---- these exceptions as we are already on the error path
5601 // Returns true if end needs to be called
5602 // Note: this is side effecting even if it returns false
5603 public bool TryBeginComplete(AsyncCallback callback, object state, out IAsyncResult result)
5605 // In the interest of allocating less objects we don't implement
5606 // the full async pattern here. Instead, we've flattened it to
5607 // do the sync part and then optionally delegate down to the inner
5609 if (this.contextOwnedTransaction != null)
5611 result = this.contextOwnedTransaction.BeginCommit(callback, state);
5621 public void EndComplete(IAsyncResult result)
5623 Fx.Assert(this.contextOwnedTransaction != null, "We must have a contextOwnedTransaction if we are calling End");
5625 this.contextOwnedTransaction.EndCommit(result);
5629 class UnloadInstancePolicyHelper
5631 static Action<object> onTimerCallback = new Action<object>(OnTimerCallback);
5632 static AsyncCallback onPersistCallback = Fx.ThunkCallback(new AsyncCallback(PersistCallback));
5633 static AsyncCallback onUnloadCallback = Fx.ThunkCallback(new AsyncCallback(UnloadCallback));
5634 static AsyncCallback onUnlockAndAbortCallback = Fx.ThunkCallback(new AsyncCallback(UnlockAndAbortCallback));
5636 WorkflowServiceInstance instance;
5637 TimeSpan timeToPersist;
5638 TimeSpan timeToUnload;
5639 IOThreadTimer persistTimer;
5640 IOThreadTimer unloadTimer;
5642 bool persistEnabled;
5645 public UnloadInstancePolicyHelper(WorkflowServiceInstance instance, TimeSpan timeToPersist, TimeSpan timeToUnload)
5647 Fx.Assert(instance != null, String.Empty);
5649 this.instance = instance;
5650 this.timeToPersist = timeToPersist;
5651 this.timeToUnload = timeToUnload;
5652 this.persistEnabled = this.instance.persistenceContext.CanPersist && this.timeToPersist < this.timeToUnload;
5653 this.unloadEnabled = this.instance.persistenceContext.CanPersist && this.timeToUnload < TimeSpan.MaxValue;
5655 if (this.persistEnabled)
5657 this.persistTimer = new IOThreadTimer(onTimerCallback, new Action(Persist), true);
5659 if (this.unloadEnabled)
5661 this.unloadTimer = new IOThreadTimer(onTimerCallback, new Action(Unload), true);
5665 [System.Diagnostics.CodeAnalysis.SuppressMessage("Exceptions", "DoNotCatchGeneralExceptionTypes", MessageId = "System.ServiceModel.Activities.WorkflowServiceInstance+UnloadInstancePolicyHelper.OnTimerCallback(System.Object):System.Void", Justification = "The non-fatal exceptions will be traced")]
5666 static void OnTimerCallback(object state)
5670 ((Action)state).Invoke();
5672 catch (Exception ex)
5678 FxTrace.Exception.AsWarning(ex);
5686 this.cancelled = false;
5687 if (this.persistEnabled)
5689 Fx.Assert(this.persistTimer != null, "persistTimer cannot be null if persist is enabled");
5690 SetTimer(this.persistTimer, this.timeToPersist);
5694 if (this.instance.persistenceContext.CanPersist)
5696 if (this.unloadEnabled)
5698 Fx.Assert(this.unloadTimer != null, "unloadTimer cannot be null if unload is enabled");
5699 SetTimer(this.unloadTimer, this.timeToUnload);
5706 public void Cancel()
5708 this.cancelled = true;
5709 if (this.persistTimer != null)
5711 this.persistTimer.Cancel();
5713 if (this.unloadTimer != null)
5715 this.unloadTimer.Cancel();
5723 IAsyncResult result = this.instance.BeginPersist(true, TimeSpan.MaxValue, onPersistCallback, this);
5724 if (result.CompletedSynchronously)
5726 HandleEndPersist(result);
5729 catch (Exception ex)
5735 this.instance.AbortInstance(ex, false);
5739 static void PersistCallback(IAsyncResult result)
5741 if (result.CompletedSynchronously)
5746 UnloadInstancePolicyHelper thisPtr = (UnloadInstancePolicyHelper)result.AsyncState;
5749 thisPtr.HandleEndPersist(result);
5751 catch (Exception ex)
5757 thisPtr.instance.AbortInstance(ex, false);
5761 void HandleEndPersist(IAsyncResult result)
5763 bool persistSucceeded = this.instance.EndPersist(result);
5765 if (!this.cancelled)
5767 if (this.instance.persistenceContext.CanPersist)
5769 if (this.unloadEnabled)
5771 Fx.Assert(this.unloadTimer != null, "unloadTimer cannot be null if unload is enabled");
5773 if (persistSucceeded)
5775 Fx.Assert(this.timeToUnload > this.timeToPersist, String.Empty);
5776 SetTimer(this.unloadTimer, this.timeToUnload - this.timeToPersist);
5783 void SetTimer(IOThreadTimer timer, TimeSpan ts)
5785 Fx.Assert(timer != null && ts >= TimeSpan.Zero, String.Empty);
5787 // It is ok to dirty read the state, the consistency will be ensured by persis/unload itself.
5788 if (this.instance.state == State.Suspended)
5790 // Unload/Persist immediately when suspended
5791 timer.Set(TimeSpan.Zero);
5803 if (this.persistEnabled)
5805 // This is an optimization to avoid expensive redundant persist (already persisted).
5806 // We will simply Unlock and Abort an instance.
5807 IAsyncResult result = BeginUnlockAndAbort(TimeSpan.MaxValue, onUnlockAndAbortCallback, this);
5808 if (result.CompletedSynchronously)
5810 EndUnlockAndAbort(result);
5815 IAsyncResult result = this.instance.BeginReleaseInstance(true, TimeSpan.MaxValue, onUnloadCallback, this);
5816 if (result.CompletedSynchronously)
5818 HandleEndUnload(result);
5822 catch (Exception ex)
5828 this.instance.AbortInstance(ex, false);
5832 static void UnloadCallback(IAsyncResult result)
5834 if (result.CompletedSynchronously)
5839 UnloadInstancePolicyHelper thisPtr = (UnloadInstancePolicyHelper)result.AsyncState;
5842 thisPtr.HandleEndUnload(result);
5844 catch (Exception ex)
5851 thisPtr.instance.AbortInstance(ex, false);
5855 void HandleEndUnload(IAsyncResult result)
5857 this.instance.EndReleaseInstance(result);
5860 IAsyncResult BeginUnlockAndAbort(TimeSpan timeout, AsyncCallback callback, object state)
5862 return new UnlockAndAbortAsyncResult(this.instance, timeout, callback, state);
5865 void EndUnlockAndAbort(IAsyncResult result)
5867 UnlockAndAbortAsyncResult.End(result);
5870 static void UnlockAndAbortCallback(IAsyncResult result)
5872 if (result.CompletedSynchronously)
5877 UnloadInstancePolicyHelper thisPtr = (UnloadInstancePolicyHelper)result.AsyncState;
5880 thisPtr.EndUnlockAndAbort(result);
5882 catch (Exception ex)
5888 thisPtr.instance.AbortInstance(ex, false);
5892 // This class provides a safe unlock and abort of the instance without persisting.
5893 // The synchronized mechanism is the same as ReleaseAsyncResult.
5894 class UnlockAndAbortAsyncResult : AsyncResult
5896 static Action<AsyncResult, Exception> onCompleting = new Action<AsyncResult, Exception>(Finally);
5897 static FastAsyncCallback acquireCompletedCallback = new FastAsyncCallback(AcquireCompletedCallback);
5898 static AsyncCompletion handleEndAbandon;
5900 WorkflowServiceInstance instance;
5901 TimeoutHelper timeoutHelper;
5902 bool referenceAcquired;
5904 public UnlockAndAbortAsyncResult(WorkflowServiceInstance instance, TimeSpan timeout, AsyncCallback callback, object state)
5905 : base(callback, state)
5907 this.instance = instance;
5908 this.timeoutHelper = new TimeoutHelper(timeout);
5909 this.OnCompleting = onCompleting;
5911 Exception completionException = null;
5912 bool completeSelf = true;
5914 if (this.instance.acquireReferenceSemaphore.EnterAsync(this.timeoutHelper.RemainingTime(), acquireCompletedCallback, this))
5918 completeSelf = this.HandleEndAcquireReference();
5920 catch (Exception exception)
5922 if (Fx.IsFatal(exception))
5926 completionException = exception;
5931 completeSelf = false;
5936 Complete(true, completionException);
5940 public static void End(IAsyncResult result)
5942 AsyncResult.End<UnlockAndAbortAsyncResult>(result);
5945 static void AcquireCompletedCallback(object state, Exception completionException)
5947 UnlockAndAbortAsyncResult thisPtr = (UnlockAndAbortAsyncResult)state;
5949 bool completeSelf = true;
5950 if (completionException == null)
5954 completeSelf = thisPtr.HandleEndAcquireReference();
5956 catch (Exception exception)
5958 if (Fx.IsFatal(exception))
5962 completionException = exception;
5968 thisPtr.Complete(false, completionException);
5972 bool HandleEndAcquireReference()
5974 this.referenceAcquired = true;
5976 if (this.instance.TryReleaseLastReference())
5978 if (handleEndAbandon == null)
5980 handleEndAbandon = new AsyncCompletion(HandleEndAbandon);
5983 IAsyncResult result = this.instance.BeginAbandon(new FaultException(OperationExecutionFault.CreateAbortedFault(SR.DefaultAbortReason)), false,
5984 this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleEndAbandon), this);
5985 return SyncContinue(result);
5993 static bool HandleEndAbandon(IAsyncResult result)
5995 UnlockAndAbortAsyncResult thisPtr = (UnlockAndAbortAsyncResult)result.AsyncState;
5996 thisPtr.instance.EndAbandon(result);
5998 return thisPtr.ReleaseAcquiredReference();
6001 bool ReleaseAcquiredReference()
6003 this.instance.acquireReferenceSemaphore.Exit();
6004 this.referenceAcquired = false;
6008 static void Finally(AsyncResult result, Exception completionException)
6010 UnlockAndAbortAsyncResult thisPtr = (UnlockAndAbortAsyncResult)result;
6011 if (thisPtr.referenceAcquired)
6013 thisPtr.ReleaseAcquiredReference();
6019 class UnhandledExceptionPolicyHelper
6021 static AsyncCallback operationCallback = Fx.ThunkCallback(new AsyncCallback(OperationCallback));
6023 WorkflowServiceInstance instance;
6024 WorkflowUnhandledExceptionAction action;
6026 public UnhandledExceptionPolicyHelper(WorkflowServiceInstance instance, WorkflowUnhandledExceptionAction action)
6028 Fx.Assert(instance != null, "instance must not be null!");
6029 Fx.Assert(WorkflowUnhandledExceptionActionHelper.IsDefined(action), action + " is invalid!");
6030 this.instance = instance;
6031 this.action = action;
6034 public void OnUnhandledException(UnhandledExceptionAsyncData data)
6036 Fx.Assert(data != null, "data must not be null!");
6037 Fx.Assert(data.Exception != null, "data.Exception must not be null!");
6039 FxTrace.Exception.AsWarning(data.Exception);
6043 IAsyncResult result;
6044 if (this.action == WorkflowUnhandledExceptionAction.Cancel)
6046 result = this.instance.BeginCancel(null, TimeSpan.MaxValue, operationCallback, data);
6048 else if (this.action == WorkflowUnhandledExceptionAction.Terminate)
6050 result = this.instance.BeginTerminate(data.Exception, null, TimeSpan.MaxValue, operationCallback, data);
6052 else if (this.action == WorkflowUnhandledExceptionAction.AbandonAndSuspend)
6054 this.instance.isRunnable = false;
6055 // For non-durable WF, simply abandon.
6056 if (this.instance.persistenceContext.CanPersist)
6058 result = this.instance.BeginAbandonAndSuspend(data.Exception, TimeSpan.MaxValue, operationCallback, data);
6062 result = this.instance.BeginAbandon(data.Exception, TimeSpan.MaxValue, operationCallback, data);
6067 this.instance.isRunnable = false;
6068 result = this.instance.BeginAbandon(data.Exception, TimeSpan.MaxValue, operationCallback, data);
6071 if (result.CompletedSynchronously)
6073 HandleEndOperation(result);
6076 catch (Exception ex)
6082 this.instance.AbortInstance(ex, true);
6086 static void OperationCallback(IAsyncResult result)
6088 if (result.CompletedSynchronously)
6093 UnhandledExceptionAsyncData data = (UnhandledExceptionAsyncData)result.AsyncState;
6094 UnhandledExceptionPolicyHelper thisPtr = data.Instance.UnhandledExceptionPolicy;
6097 thisPtr.HandleEndOperation(result);
6099 catch (Exception ex)
6105 thisPtr.instance.AbortInstance(ex, false);
6109 void HandleEndOperation(IAsyncResult result)
6111 if (this.action == WorkflowUnhandledExceptionAction.Cancel)
6113 this.instance.EndCancel(result);
6115 else if (this.action == WorkflowUnhandledExceptionAction.Terminate)
6117 this.instance.EndTerminate(result);
6119 else if (this.action == WorkflowUnhandledExceptionAction.AbandonAndSuspend)
6121 if (this.instance.persistenceContext.CanPersist)
6123 this.instance.EndAbandonAndSuspend(result);
6127 this.instance.EndAbandon(result);
6132 this.instance.EndAbandon(result);