File: System\Activities\Runtime\WorkItem.cs
Project: ndp\cdf\src\NetFx40\System.Activities\System.Activities.csproj (System.Activities)
//-----------------------------------------------------------------------------
// 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;
            }
        }
    }
}