e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
375 lines
11 KiB
C#
375 lines
11 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.Activities.Runtime
|
|
{
|
|
using System;
|
|
using System.Runtime;
|
|
using System.Reflection;
|
|
using System.Runtime.DurableInstancing;
|
|
using System.Runtime.Serialization;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Activities.Debugger;
|
|
using System.Collections.Generic;
|
|
using System.Activities.Tracking;
|
|
|
|
[DataContract]
|
|
internal abstract class WorkItem
|
|
{
|
|
static AsyncCallback associateCallback;
|
|
static AsyncCallback trackingCallback;
|
|
|
|
// We use a protected field here because it works well with
|
|
// ref style Cleanup exception handling.
|
|
protected Exception workflowAbortException;
|
|
|
|
ActivityInstance activityInstance;
|
|
|
|
bool isEmpty;
|
|
|
|
Exception exceptionToPropagate;
|
|
|
|
// Used by subclasses in the pooled case.
|
|
protected WorkItem()
|
|
{
|
|
}
|
|
|
|
protected WorkItem(ActivityInstance activityInstance)
|
|
{
|
|
this.activityInstance = activityInstance;
|
|
this.activityInstance.IncrementBusyCount();
|
|
}
|
|
|
|
public ActivityInstance ActivityInstance
|
|
{
|
|
get
|
|
{
|
|
return this.activityInstance;
|
|
}
|
|
}
|
|
|
|
public Exception WorkflowAbortException
|
|
{
|
|
get
|
|
{
|
|
return this.workflowAbortException;
|
|
}
|
|
}
|
|
|
|
public Exception ExceptionToPropagate
|
|
{
|
|
get
|
|
{
|
|
return this.exceptionToPropagate;
|
|
}
|
|
set
|
|
{
|
|
Fx.Assert(value != null, "We should never set this back to null explicitly. Use the ExceptionPropagated method below.");
|
|
|
|
this.exceptionToPropagate = value;
|
|
}
|
|
}
|
|
|
|
public abstract ActivityInstance PropertyManagerOwner
|
|
{
|
|
get;
|
|
}
|
|
|
|
public virtual ActivityInstance OriginalExceptionSource
|
|
{
|
|
get
|
|
{
|
|
return this.ActivityInstance;
|
|
}
|
|
}
|
|
|
|
public bool IsEmpty
|
|
{
|
|
get
|
|
{
|
|
return this.isEmpty;
|
|
}
|
|
protected set
|
|
{
|
|
this.isEmpty = value;
|
|
}
|
|
}
|
|
|
|
public bool ExitNoPersistRequired
|
|
{
|
|
get;
|
|
protected set;
|
|
}
|
|
|
|
protected bool IsPooled
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public abstract bool IsValid
|
|
{
|
|
get;
|
|
}
|
|
|
|
[DataMember(Name = "activityInstance")]
|
|
internal ActivityInstance SerializedActivityInstance
|
|
{
|
|
get { return this.activityInstance; }
|
|
set { this.activityInstance = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "IsEmpty")]
|
|
internal bool SerializedIsEmpty
|
|
{
|
|
get { return this.IsEmpty; }
|
|
set { this.IsEmpty = value; }
|
|
}
|
|
|
|
public void Dispose(ActivityExecutor executor)
|
|
{
|
|
if (FxTrace.ShouldTraceVerboseToTraceSource)
|
|
{
|
|
TraceCompleted();
|
|
}
|
|
|
|
if (this.IsPooled)
|
|
{
|
|
this.ReleaseToPool(executor);
|
|
}
|
|
}
|
|
|
|
protected virtual void ClearForReuse()
|
|
{
|
|
this.exceptionToPropagate = null;
|
|
this.workflowAbortException = null;
|
|
this.activityInstance = null;
|
|
}
|
|
|
|
protected virtual void Reinitialize(ActivityInstance activityInstance)
|
|
{
|
|
this.activityInstance = activityInstance;
|
|
this.activityInstance.IncrementBusyCount();
|
|
}
|
|
|
|
// this isn't just public for performance reasons. We avoid the virtual call
|
|
// by going through Dispose()
|
|
protected virtual void ReleaseToPool(ActivityExecutor executor)
|
|
{
|
|
Fx.Assert("This should never be called ... only overridden versions should get called.");
|
|
}
|
|
|
|
static void OnAssociateComplete(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CallbackData data = (CallbackData)result.AsyncState;
|
|
|
|
try
|
|
{
|
|
data.Executor.EndAssociateKeys(result);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
data.WorkItem.workflowAbortException = e;
|
|
}
|
|
|
|
data.Executor.FinishWorkItem(data.WorkItem);
|
|
}
|
|
|
|
static void OnTrackingComplete(IAsyncResult result)
|
|
{
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CallbackData data = (CallbackData)result.AsyncState;
|
|
|
|
try
|
|
{
|
|
data.Executor.EndTrackPendingRecords(result);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
data.WorkItem.workflowAbortException = e;
|
|
}
|
|
|
|
data.Executor.FinishWorkItemAfterTracking(data.WorkItem);
|
|
}
|
|
|
|
public void ExceptionPropagated()
|
|
{
|
|
// We just null this out, but using this API helps with
|
|
// readability over the property setter
|
|
this.exceptionToPropagate = null;
|
|
}
|
|
|
|
public void Release(ActivityExecutor executor)
|
|
{
|
|
this.activityInstance.DecrementBusyCount();
|
|
|
|
if (this.ExitNoPersistRequired)
|
|
{
|
|
executor.ExitNoPersist();
|
|
}
|
|
}
|
|
|
|
public abstract void TraceScheduled();
|
|
|
|
protected void TraceRuntimeWorkItemScheduled()
|
|
{
|
|
if (TD.ScheduleRuntimeWorkItemIsEnabled())
|
|
{
|
|
TD.ScheduleRuntimeWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
|
|
}
|
|
}
|
|
|
|
public abstract void TraceStarting();
|
|
|
|
protected void TraceRuntimeWorkItemStarting()
|
|
{
|
|
if (TD.StartRuntimeWorkItemIsEnabled())
|
|
{
|
|
TD.StartRuntimeWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
|
|
}
|
|
}
|
|
|
|
public abstract void TraceCompleted();
|
|
|
|
protected void TraceRuntimeWorkItemCompleted()
|
|
{
|
|
if (TD.CompleteRuntimeWorkItemIsEnabled())
|
|
{
|
|
TD.CompleteRuntimeWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id);
|
|
}
|
|
}
|
|
|
|
public abstract bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager);
|
|
|
|
public abstract void PostProcess(ActivityExecutor executor);
|
|
|
|
public bool FlushBookmarkScopeKeys(ActivityExecutor executor)
|
|
{
|
|
Fx.Assert(executor.BookmarkScopeManager.HasKeysToUpdate, "We should not have been called if we don't have pending keys.");
|
|
|
|
try
|
|
{
|
|
// disassociation is local-only so we don't need to yield
|
|
ICollection<InstanceKey> keysToDisassociate = executor.BookmarkScopeManager.GetKeysToDisassociate();
|
|
if (keysToDisassociate != null && keysToDisassociate.Count > 0)
|
|
{
|
|
executor.DisassociateKeys(keysToDisassociate);
|
|
}
|
|
|
|
// if we have keys to associate, provide them for an asynchronous association
|
|
ICollection<InstanceKey> keysToAssociate = executor.BookmarkScopeManager.GetKeysToAssociate();
|
|
|
|
// It could be that we only had keys to Disassociate. We should only do BeginAssociateKeys
|
|
// if we have keysToAssociate.
|
|
if (keysToAssociate != null && keysToAssociate.Count > 0)
|
|
{
|
|
if (associateCallback == null)
|
|
{
|
|
associateCallback = Fx.ThunkCallback(new AsyncCallback(OnAssociateComplete));
|
|
}
|
|
|
|
IAsyncResult result = executor.BeginAssociateKeys(keysToAssociate, associateCallback, new CallbackData(executor, this));
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
executor.EndAssociateKeys(result);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
this.workflowAbortException = e;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool FlushTracking(ActivityExecutor executor)
|
|
{
|
|
Fx.Assert(executor.HasPendingTrackingRecords, "We should not have been called if we don't have pending tracking records");
|
|
|
|
try
|
|
{
|
|
if (trackingCallback == null)
|
|
{
|
|
trackingCallback = Fx.ThunkCallback(new AsyncCallback(OnTrackingComplete));
|
|
}
|
|
|
|
IAsyncResult result = executor.BeginTrackPendingRecords(
|
|
trackingCallback,
|
|
new CallbackData(executor, this));
|
|
|
|
if (result.CompletedSynchronously)
|
|
{
|
|
executor.EndTrackPendingRecords(result);
|
|
}
|
|
else
|
|
{
|
|
// Completed async so we'll return false
|
|
return false;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
this.workflowAbortException = e;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
class CallbackData
|
|
{
|
|
public CallbackData(ActivityExecutor executor, WorkItem workItem)
|
|
{
|
|
this.Executor = executor;
|
|
this.WorkItem = workItem;
|
|
}
|
|
|
|
public ActivityExecutor Executor
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public WorkItem WorkItem
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
}
|
|
}
|
|
}
|