// Created for ref. 20575 internal void PostPersist(bool isPersistSuccessful) { // If persist is unsuccessful, we'll undo the changes done // because of the call to .Complete() in PrePresist if (!isPersistSuccessful) { Debug.Assert(rootWorkflowExecutor.CurrentAtomicActivity != null); Debug.Assert(pendingQueueStateSnapshot != null); Debug.Assert(persistedQueueStatesSnapshot != null); TransactionalProperties transactionalProperties = rootWorkflowExecutor.CurrentAtomicActivity.GetValue(WorkflowExecutor.TransactionalPropertiesProperty) as TransactionalProperties; Debug.Assert(transactionalProperties != null); // Restore queuing states and set root activity's dependency properties to the new values. pendingQueueState = pendingQueueStateSnapshot; persistedQueueStates = persistedQueueStatesSnapshot; rootWorkflowExecutor.RootActivity.SetValue(WorkflowQueuingService.RootPersistedQueueStatesProperty, persistedQueueStatesSnapshot); rootWorkflowExecutor.RootActivity.SetValue(WorkflowQueuingService.PendingMessagesProperty, pendingQueueStateSnapshot.Messages); // Also call Subscribe...() because the .Complete() call called Unsubscribe transactionalProperties.LocalQueuingService.SubscribeForRootMessageDelivery(); } // The backups are no longer necessary. // The next call to PrePresistQueuingServiceState() will do a re-backup. persistedQueueStatesSnapshot = null; pendingQueueStateSnapshot = null; }
// Created for ref. 20575 internal void PrePersist() { if (rootWorkflowExecutor.CurrentAtomicActivity != null) { // Create transactionalProperties from currentAtomicActivity TransactionalProperties transactionalProperties = this.rootWorkflowExecutor.CurrentAtomicActivity.GetValue(WorkflowExecutor.TransactionalPropertiesProperty) as TransactionalProperties; // Create backup snapshot of root queuing service's persistedQueuesStates // qService.persistedQueueStates is changed when LocalQueuingService.Complete is called later. persistedQueueStatesSnapshot = new Dictionary <IComparable, EventQueueState>(); foreach (KeyValuePair <IComparable, EventQueueState> kv in persistedQueueStates) { EventQueueState individualPersistedQueueStateValue = new EventQueueState(); individualPersistedQueueStateValue.CopyFrom(kv.Value); persistedQueueStatesSnapshot.Add(kv.Key, individualPersistedQueueStateValue); } // Create backup snapshot of root queuing service's pendingQueueState // qService.pendingQueueState is changed when LocalQueuingService.Complete is called later. pendingQueueStateSnapshot = new EventQueueState(); pendingQueueStateSnapshot.CopyFrom(pendingQueueState); // Reconcile differences between root and local queuing services. transactionalProperties.LocalQueuingService.Complete(true); } }
internal void PostPersist(bool isPersistSuccessful) { if (!isPersistSuccessful) { TransactionalProperties properties = this.rootWorkflowExecutor.CurrentAtomicActivity.GetValue(WorkflowExecutor.TransactionalPropertiesProperty) as TransactionalProperties; this.pendingQueueState = this.pendingQueueStateSnapshot; this.persistedQueueStates = this.persistedQueueStatesSnapshot; this.rootWorkflowExecutor.RootActivity.SetValue(RootPersistedQueueStatesProperty, this.persistedQueueStatesSnapshot); this.rootWorkflowExecutor.RootActivity.SetValue(PendingMessagesProperty, this.pendingQueueStateSnapshot.Messages); properties.LocalQueuingService.SubscribeForRootMessageDelivery(); } this.persistedQueueStatesSnapshot = null; this.pendingQueueStateSnapshot = null; }
internal void PrePersist() { if (this.rootWorkflowExecutor.CurrentAtomicActivity != null) { TransactionalProperties properties = this.rootWorkflowExecutor.CurrentAtomicActivity.GetValue(WorkflowExecutor.TransactionalPropertiesProperty) as TransactionalProperties; this.persistedQueueStatesSnapshot = new Dictionary <IComparable, EventQueueState>(); foreach (KeyValuePair <IComparable, EventQueueState> pair in this.persistedQueueStates) { EventQueueState state = new EventQueueState(); state.CopyFrom(pair.Value); this.persistedQueueStatesSnapshot.Add(pair.Key, state); } this.pendingQueueStateSnapshot = new EventQueueState(); this.pendingQueueStateSnapshot.CopyFrom(this.pendingQueueState); properties.LocalQueuingService.Complete(true); } }
private void DisposeTransactionScope(TransactionalProperties transactionalProperties) { if (transactionalProperties.TransactionScope != null) { // Need to call Complete othwise the transaction will be aborted transactionalProperties.TransactionScope.Complete(); transactionalProperties.TransactionScope.Dispose(); transactionalProperties.TransactionScope = null; WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + "Left TransactionScope, Current atomic acitivity was " + ((this.currentAtomicActivity == null) ? null : this.currentAtomicActivity.Name)); } }
/* * Leaving this class in place as we will need it for the flow through tx story in V2 class TransactionNotificationEnlistment : IEnlistmentNotification, IActivityEventListener<EventArgs> { WorkflowExecutor workflowExecutor; Transaction transaction; Activity atomicActivity; internal TransactionNotificationEnlistment(WorkflowExecutor exec, Transaction tx, Activity atomicActivity) { this.workflowExecutor = exec; this.transaction = tx; this.atomicActivity = atomicActivity; } #region IEnlistmentNotification Members void IEnlistmentNotification.Commit(Enlistment enlistment) { enlistment.Done(); } void IEnlistmentNotification.InDoubt(Enlistment enlistment) { enlistment.Done(); } void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment) { preparingEnlistment.Prepared(); } void IEnlistmentNotification.Rollback(Enlistment enlistment) { // // Currently this method isn't used. // The problem is that we must acquire the sched lock in order to schedule // an item. While we wait trying to acquire the lock the transaction is held open. // If the instance is idle we acquire the lock right away and this works fine. // However is we have items to run we'll check the transaction, find that it is aborted // and start exception handling. During the entire exception handling process the transaction // and the associated connections will be held open. This is not good. // Post V1 we need scheduler changes to allow us to safely asynchronously schedule work // without taking the scheduler lock. enlistment.Done(); // // ensure transaction timeout/abort is processed in case of a // blocked activity inside a transactional scope ScheduleTransactionTimeout(); } private void ScheduleTransactionTimeout() { try { // // We're going to check executor state and possibly enqueue a workitem // Must take the scheduleExecutor lock using (this.workflowExecutor._schedulerLock.Enter()) { if (!this.workflowExecutor.IsInstanceValid) return; // If the exception has already been taken care of, ignore this abort notification Activity curAtomicActivity = this.workflowExecutor.currentAtomicActivity; if ((curAtomicActivity != null)&&(curAtomicActivity==atomicActivity)) { TransactionalProperties transactionalProperties = (TransactionalProperties)curAtomicActivity.GetValue(TransactionalPropertiesProperty); if ((transactionalProperties.Transaction == this.transaction) && (transactionalProperties.TransactionState != TransactionProcessState.AbortProcessed)) { transactionalProperties.TransactionState = TransactionProcessState.Aborted; using (this.workflowExecutor.MessageDeliveryLock.Enter()) { using (new ServiceEnvironment(this.workflowExecutor.RootActivity)) { using (this.workflowExecutor.SetCurrentActivity(curAtomicActivity)) { // // This will schedule (async) a work item to cancel the tx scope activity // However this item will never get run - we always check if the // tx has aborted prior to running any items so this is really // just a "wake up" notification to the scheduler. Activity contextActivity = ContextActivityUtils.ContextActivity(curAtomicActivity); ActivityExecutorDelegateInfo<EventArgs> dummyCallback = new ActivityExecutorDelegateInfo<EventArgs>(this, contextActivity, true); dummyCallback.InvokeDelegate(contextActivity, EventArgs.Empty, false); } } } } } } } catch (Exception e) { WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "AbortNotificationEnlistment: instanceId: {0} failed to process ScheduleTransactionTimeout with exception {1} ", this.workflowExecutor.this.InstanceIdString, e.Message); } } void IActivityEventListener<EventArgs>.OnEvent(object sender, EventArgs e) { // this will never be invoked since Scheduler will process the transaction aborted request } #endregion }*/ #endregion VolatileEnlistment for AbortNotification internal static bool CheckAndProcessTransactionAborted(TransactionalProperties transactionalProperties) { if (transactionalProperties.Transaction != null && transactionalProperties.Transaction.TransactionInformation.Status != TransactionStatus.Aborted) return false; // If transaction aborted but not processed, // process it (i.e. throw to invoke Exception handling) // otherwise return if transaction aborted switch (transactionalProperties.TransactionState) { case TransactionProcessState.Ok: case TransactionProcessState.Aborted: transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed; throw new TransactionAbortedException(); case TransactionProcessState.AbortProcessed: return true; default: return false; } }
private void CreateTransaction(Activity atomicActivity) { Debug.Assert(this.currentAtomicActivity == null, "There is already a transacted activity running"); TransactionalProperties transactionalProperties = new TransactionalProperties(); TransactionOptions tranOpts = new TransactionOptions(); WorkflowTransactionOptions atomicTxn = TransactedContextFilter.GetTransactionOptions(atomicActivity); Debug.Assert(atomicTxn != null, "null atomicTxn"); // tranOpts.IsolationLevel = atomicTxn.IsolationLevel; if (tranOpts.IsolationLevel == IsolationLevel.Unspecified) tranOpts.IsolationLevel = IsolationLevel.Serializable; tranOpts.Timeout = atomicTxn.TimeoutDuration; // Create a promotable transaction (can be promoted to DTC when necessary) // as COM+ user code may want to participate in the transaction // Enlist to the transaction for abort notification System.Transactions.CommittableTransaction transaction = new CommittableTransaction(tranOpts); // Can switch back to using TransactionCompletionHandler once VS562627 is fixed // transaction.TransactionCompleted += new TransactionCompletedEventHandler(TransactionCompletionHandler); //transaction.EnlistVolatile(new TransactionNotificationEnlistment(this, transaction, atomicActivity), EnlistmentOptions.None); transactionalProperties.Transaction = transaction; WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + " .Created enlistable transaction " + ((System.Transactions.Transaction)transaction).GetHashCode() + " with timeout " + tranOpts.Timeout + ", isolation " + tranOpts.IsolationLevel); // create a local queuing service per atomic context transactionalProperties.LocalQueuingService = new WorkflowQueuingService(this.qService); // Store the transaction properties onto the activity atomicActivity.SetValue(TransactionalPropertiesProperty, transactionalProperties); // Set current atomic activity this.currentAtomicActivity = atomicActivity; atomicActivityEvent = new ManualResetEvent(false); WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + " .Set CurrentAtomicActivity to " + atomicActivity.Name); }
public void Run() { do { this.RootWorkflowExecutor.ProcessQueuedEvents(); // Get item to run SchedulableItem item = GetItemToRun(); bool runningItem = false; // no ready work to run... go away if (item == null) { break; } Activity itemActivity = null; Exception exp = null; TransactionalProperties transactionalProperties = null; int contextId = item.ContextId; // This function gets the root or enclosing while-loop activity Activity contextActivity = this.RootWorkflowExecutor.GetContextActivityForId(contextId); if (contextActivity == null) { throw new InvalidOperationException(ExecutionStringManager.InvalidExecutionContext); } // This is the activity corresponding to the item's ActivityId itemActivity = contextActivity.GetActivityByName(item.ActivityId); using (new ServiceEnvironment(itemActivity)) { exp = null; bool ignoreFinallyBlock = false; try { // item preamble // set up the item transactional context if necessary // Debug.Assert(itemActivity != null, "null itemActivity"); if (itemActivity == null) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InvalidActivityName, item.ActivityId)); } Activity atomicActivity = null; if (this.RootWorkflowExecutor.IsActivityInAtomicContext(itemActivity, out atomicActivity)) { transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(WorkflowExecutor.TransactionalPropertiesProperty); // If we've aborted for any reason stop now! // If we attempt to enter a new TransactionScope the com+ context will get corrupted // See windows se bug 137267 if (!WorkflowExecutor.CheckAndProcessTransactionAborted(transactionalProperties)) { if (transactionalProperties.TransactionScope == null) { // Use TimeSpan.Zero so scope will not create timeout independent of the transaction // Use EnterpriseServicesInteropOption.Full to flow transaction to COM+ transactionalProperties.TransactionScope = new TransactionScope(transactionalProperties.Transaction, TimeSpan.Zero, EnterpriseServicesInteropOption.Full); WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: Scheduler: instanceId: " + this.RootWorkflowExecutor.InstanceIdString + "Entered into TransactionScope, Current atomic acitivity " + atomicActivity.Name); } } } // Run the item // runningItem = true; WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Running scheduled entry: {1}", this.RootWorkflowExecutor.InstanceIdString, item.ToString()); // running any entry implicitly changes some state of the workflow instance this.RootWorkflowExecutor.stateChangedSincePersistence = true; item.Run(this.RootWorkflowExecutor); } catch (Exception e) { if (WorkflowExecutor.IsIrrecoverableException(e)) { ignoreFinallyBlock = true; throw; } else { if (transactionalProperties != null) { transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed; } exp = e; } } finally { if (!ignoreFinallyBlock) { if (runningItem) { WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Done with running scheduled entry: {1}", this.RootWorkflowExecutor.InstanceIdString, item.ToString()); } // Process exception // if (exp != null) { // this.RootWorkflowExecutor.ExceptionOccured(exp, itemActivity == null ? contextActivity : itemActivity, null); exp = null; } } } } } while (true); }
private void CreateTransaction(Activity atomicActivity) { TransactionalProperties properties = new TransactionalProperties(); TransactionOptions options = new TransactionOptions(); WorkflowTransactionOptions transactionOptions = TransactedContextFilter.GetTransactionOptions(atomicActivity); options.IsolationLevel = transactionOptions.IsolationLevel; if (options.IsolationLevel == IsolationLevel.Unspecified) { options.IsolationLevel = IsolationLevel.Serializable; } options.Timeout = transactionOptions.TimeoutDuration; CommittableTransaction transaction = new CommittableTransaction(options); properties.Transaction = transaction; WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, string.Concat(new object[] { "Workflow Runtime: WorkflowExecutor: instanceId: ", this.InstanceIdString, " .Created enlistable transaction ", transaction.GetHashCode(), " with timeout ", options.Timeout, ", isolation ", options.IsolationLevel })); properties.LocalQueuingService = new WorkflowQueuingService(this.qService); atomicActivity.SetValue(TransactionalPropertiesProperty, properties); this.currentAtomicActivity = atomicActivity; this.atomicActivityEvent = new ManualResetEvent(false); WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + " .Set CurrentAtomicActivity to " + atomicActivity.Name); }
internal static bool CheckAndProcessTransactionAborted(TransactionalProperties transactionalProperties) { if ((transactionalProperties.Transaction == null) || (transactionalProperties.Transaction.TransactionInformation.Status == TransactionStatus.Aborted)) { switch (transactionalProperties.TransactionState) { case TransactionProcessState.Ok: case TransactionProcessState.Aborted: transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed; throw new TransactionAbortedException(); case TransactionProcessState.AbortProcessed: return true; } } return false; }
public void CheckpointInstanceState(Activity atomicActivity) { // Note that the WF4 runtime does not create checkpoints. If the transaction aborts, the workflow aborts. // We are following the WF4 behavior and not creating a checkpoint. TransactionOptions tranOpts = new TransactionOptions(); WorkflowTransactionOptions atomicTxn = TransactedContextFilter.GetTransactionOptions(atomicActivity); tranOpts.IsolationLevel = atomicTxn.IsolationLevel; if (tranOpts.IsolationLevel == IsolationLevel.Unspecified) { tranOpts.IsolationLevel = IsolationLevel.Serializable; } tranOpts.Timeout = atomicTxn.TimeoutDuration; TransactionalProperties transactionProperties = new TransactionalProperties(); atomicActivity.SetValue(WorkflowExecutor.TransactionalPropertiesProperty, transactionProperties); this.ServiceProvider.CreateTransaction(tranOpts); this.currentAtomicActivity = atomicActivity; this.scheduler.Pause(); }
public bool CheckAndProcessTransactionAborted(TransactionalProperties transactionalProperties) { if (transactionalProperties.Transaction != null && transactionalProperties.Transaction.TransactionInformation.Status != TransactionStatus.Aborted) { return false; } if (transactionalProperties.TransactionState != TransactionProcessState.AbortProcessed) { // The transaction has aborted. The WF3 runtime throws a TransactionAborted exception here, which then propagates as fault. // But WF4 aborts the workflow, so pause the scheduler and return. this.scheduler.Pause(); transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed; } return true; }
public void Run() { Label_0000: this.RootWorkflowExecutor.ProcessQueuedEvents(); SchedulableItem itemToRun = this.GetItemToRun(); bool flag = false; if (itemToRun != null) { Activity currentActivity = null; Exception exp = null; TransactionalProperties transactionalProperties = null; int contextId = itemToRun.ContextId; Activity contextActivityForId = this.RootWorkflowExecutor.GetContextActivityForId(contextId); if (contextActivityForId == null) { throw new InvalidOperationException(ExecutionStringManager.InvalidExecutionContext); } currentActivity = contextActivityForId.GetActivityByName(itemToRun.ActivityId); using (new ServiceEnvironment(currentActivity)) { exp = null; bool flag2 = false; try { if (currentActivity == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InvalidActivityName, new object[] { itemToRun.ActivityId })); } Activity atomicActivity = null; if (this.RootWorkflowExecutor.IsActivityInAtomicContext(currentActivity, out atomicActivity)) { transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(WorkflowExecutor.TransactionalPropertiesProperty); if (!WorkflowExecutor.CheckAndProcessTransactionAborted(transactionalProperties) && (transactionalProperties.TransactionScope == null)) { transactionalProperties.TransactionScope = new TransactionScope(transactionalProperties.Transaction, TimeSpan.Zero, EnterpriseServicesInteropOption.Full); WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: Scheduler: instanceId: " + this.RootWorkflowExecutor.InstanceIdString + "Entered into TransactionScope, Current atomic acitivity " + atomicActivity.Name); } } flag = true; WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Running scheduled entry: {1}", new object[] { this.RootWorkflowExecutor.InstanceIdString, itemToRun.ToString() }); this.RootWorkflowExecutor.stateChangedSincePersistence = true; itemToRun.Run(this.RootWorkflowExecutor); } catch (Exception exception2) { if (WorkflowExecutor.IsIrrecoverableException(exception2)) { flag2 = true; throw; } if (transactionalProperties != null) { transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed; } exp = exception2; } finally { if (!flag2) { if (flag) { WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 1, "Workflow Runtime: Scheduler: InstanceId: {0} : Done with running scheduled entry: {1}", new object[] { this.RootWorkflowExecutor.InstanceIdString, itemToRun.ToString() }); } if (exp != null) { this.RootWorkflowExecutor.ExceptionOccured(exp, (currentActivity == null) ? contextActivityForId : currentActivity, null); exp = null; } } } goto Label_0000; } } }