File: System\Activities\Hosting\WorkflowInstance.cs
Project: ndp\cdf\src\NetFx40\System.Activities\System.Activities.csproj (System.Activities)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.Activities.Hosting
{
    using System;
    using System.Text;
    using System.Activities.DynamicUpdate;
    using System.Activities.Runtime;
    using System.Activities.Tracking;
    using System.Activities.Validation;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Runtime;
    using System.Runtime.DurableInstancing;
    using System.Threading;
    using System.Xml.Linq;
 
    [Fx.Tag.XamlVisible(false)]
    public abstract class WorkflowInstance
    {
        static readonly IDictionary<string, LocationInfo> EmptyMappedVariablesDictionary = new ReadOnlyDictionaryInternal<string, LocationInfo>(new Dictionary<string, LocationInfo>(0));
 
        const int True = 1;
        const int False = 0;
 
        WorkflowInstanceControl controller;
        TrackingProvider trackingProvider;
        SynchronizationContext syncContext;
        LocationReferenceEnvironment hostEnvironment;
        ActivityExecutor executor;
        int isPerformingOperation;
        bool isInitialized;
        WorkflowInstanceExtensionCollection extensions;
 
        // Tracking for one-time actions per in-memory instance
        bool hasTrackedResumed;
        bool hasTrackedCompletion;
 
        bool isAborted;
        Exception abortedException;
 
#if DEBUG
        StackTrace abortStack;
#endif
 
        static WorkflowInstance()
        {
            try
            {
                using (TelemetryEventSource eventSource = new TelemetryEventSource())
                {
                    eventSource.V2Runtime();
                }
            }
            catch
            {
            }
        }
 
        protected WorkflowInstance(Activity workflowDefinition)
            : this(workflowDefinition, null)
        {
        }
 
        protected WorkflowInstance(Activity workflowDefinition, WorkflowIdentity definitionIdentity)
        {
            if (workflowDefinition == null)
            {
                throw FxTrace.Exception.ArgumentNull("workflowDefinition");
            }
 
            this.WorkflowDefinition = workflowDefinition;
            this.DefinitionIdentity = definitionIdentity;
        }
 
        public abstract Guid Id
        {
            get;
        }
 
        internal bool HasTrackingParticipant
        {
            get;
            private set;
        }
 
        internal bool HasTrackedStarted
        {
            get;
            private set;
        }
 
        internal bool HasPersistenceModule
        {
            get;
            private set;
        }
 
        public SynchronizationContext SynchronizationContext
        {
            get
            {
                return this.syncContext;
            }
            set
            {
                ThrowIfReadOnly();
                this.syncContext = value;
            }
        }
 
        public LocationReferenceEnvironment HostEnvironment
        {
            get
            {
                return this.hostEnvironment;
            }
            set
            {
                ThrowIfReadOnly();
                this.hostEnvironment = value;
            }
        }
 
        public Activity WorkflowDefinition
        {
            get;
            private set;
        }
 
        public WorkflowIdentity DefinitionIdentity
        {
            get;
            private set;
        }
 
        protected bool IsReadOnly
        {
            get
            {
                return this.isInitialized;
            }
        }
 
        protected internal abstract bool SupportsInstanceKeys
        {
            get;
        }
 
        // this is going away
        internal TrackingProvider TrackingProvider
        {
            get
            {
                Fx.Assert(HasTrackingParticipant, "we should only be called if we have a tracking participant");
                return this.trackingProvider;
            }
        }
 
        protected WorkflowInstanceControl Controller
        {
            get
            {
                if (!this.isInitialized)
                {
                    throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ControllerInvalidBeforeInitialize));
                }
 
                return this.controller;
            }
        }
 
        // host-facing access to our cascading ExtensionManager resolution
        protected internal T GetExtension<T>() where T : class
        {
            if (this.extensions != null)
            {
                return this.extensions.Find<T>();
            }
            else
            {
                return default(T);
            }
        }
 
        protected internal IEnumerable<T> GetExtensions<T>() where T : class
        {
            if (this.extensions != null)
            {
                return this.extensions.FindAll<T>();
            }
            else
            {
                return new T[0];
            }
        }
 
        // locks down the given extensions manager and runs cache metadata on the workflow definition
        protected void RegisterExtensionManager(WorkflowInstanceExtensionManager extensionManager)
        {
            ValidateWorkflow(extensionManager);
            this.extensions = WorkflowInstanceExtensionManager.CreateInstanceExtensions(this.WorkflowDefinition, extensionManager);
            if (this.extensions != null)
            {
                this.HasPersistenceModule = this.extensions.HasPersistenceModule;
            }
        }
 
        // dispose the extensions that implement IDisposable
        protected void DisposeExtensions()
        {
            if (this.extensions != null)
            {
                this.extensions.Dispose();
                this.extensions = null;
            }
        }
 
        protected static IList<ActivityBlockingUpdate> GetActivitiesBlockingUpdate(object deserializedRuntimeState, DynamicUpdateMap updateMap)
        {
            ActivityExecutor executor = deserializedRuntimeState as ActivityExecutor;
            if (executor == null)
            {
                throw FxTrace.Exception.Argument("deserializedRuntimeState", SR.InvalidRuntimeState);
            }
            if (updateMap == null)
            {
                throw FxTrace.Exception.ArgumentNull("updateMap");
            }
 
            DynamicUpdateMap rootMap = updateMap;
            if (updateMap.IsForImplementation)
            {
                rootMap = updateMap.AsRootMap();
            }
            IList<ActivityBlockingUpdate> result = executor.GetActivitiesBlockingUpdate(rootMap);
            if (result == null)
            {
                result = new List<ActivityBlockingUpdate>();
            }
 
            return result;
        }
 
        // used for Create scenarios where you are providing root information
        protected void Initialize(IDictionary<string, object> workflowArgumentValues, IList<Handle> workflowExecutionProperties)
        {
            ThrowIfAborted();
            ThrowIfReadOnly();
            this.executor = new ActivityExecutor(this);
 
            EnsureDefinitionReady();
            // workflowArgumentValues signals whether we are a new or loaded instance, so we can't pass in null.
            // workflowExecutionProperties is allowed to be null
            InitializeCore(workflowArgumentValues ?? ActivityUtilities.EmptyParameters, workflowExecutionProperties);
        }
 
        // used for Load scenarios where you are rehydrating a WorkflowInstance
        protected void Initialize(object deserializedRuntimeState)
        {
            Initialize(deserializedRuntimeState, null);
        }        
 
        protected void Initialize(object deserializedRuntimeState, DynamicUpdateMap updateMap)
        {
            ThrowIfAborted();
            ThrowIfReadOnly();
            this.executor = deserializedRuntimeState as ActivityExecutor;
 
            if (this.executor == null)
            {
                throw FxTrace.Exception.Argument("deserializedRuntimeState", SR.InvalidRuntimeState);
            }
            this.executor.ThrowIfNonSerializable();
 
            EnsureDefinitionReady();
 
            WorkflowIdentity originalDefinitionIdentity = this.executor.WorkflowIdentity;      
            bool success = false;
            Collection<ActivityBlockingUpdate> updateErrors = null;
            try
            {
                if (updateMap != null)
                {
                    // check if map is for implementaiton,                    
                    if (updateMap.IsForImplementation)
                    {
                        // if so, the definition root must be an activity 
                        // with no public/imported children and no public/imported delegates.
                        if (DynamicUpdateMap.CanUseImplementationMapAsRoot(this.WorkflowDefinition))
                        {
                            updateMap = updateMap.AsRootMap();
                        }
                        else
                        {
                            throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidImplementationAsWorkflowRoot));
                        }
                    }
 
                    updateMap.ThrowIfInvalid(this.WorkflowDefinition);
 
                    this.executor.WorkflowIdentity = this.DefinitionIdentity;
 
                    this.executor.UpdateInstancePhase1(updateMap, this.WorkflowDefinition, ref updateErrors);
                    ThrowIfDynamicUpdateErrorExists(updateErrors);
                }
 
                InitializeCore(null, null);
 
                if (updateMap != null)
                {
                    this.executor.UpdateInstancePhase2(updateMap, ref updateErrors);
                    ThrowIfDynamicUpdateErrorExists(updateErrors);
                    // Track that dynamic update is successful
                    if (this.Controller.TrackingEnabled)
                    {
                        this.Controller.Track(new WorkflowInstanceUpdatedRecord(this.Id, this.WorkflowDefinition.DisplayName, originalDefinitionIdentity, this.executor.WorkflowIdentity));
                    }
                }
 
                success = true;
            }
            catch (InstanceUpdateException updateException)
            {
                // Can't track through the controller because initialization failed
                if (this.HasTrackingParticipant && this.TrackingProvider.ShouldTrackWorkflowInstanceRecords)
                {
                    IList<ActivityBlockingUpdate> blockingActivities = updateException.BlockingActivities;
                    if (blockingActivities.Count == 0)
                    {
                        blockingActivities = new List<ActivityBlockingUpdate>
                        {
                            new ActivityBlockingUpdate(this.WorkflowDefinition, this.WorkflowDefinition.Id, updateException.Message)
                        }.AsReadOnly();
                    }
                    this.TrackingProvider.AddRecord(new WorkflowInstanceUpdatedRecord(this.Id, this.WorkflowDefinition.DisplayName, originalDefinitionIdentity, this.DefinitionIdentity, blockingActivities));
                }
                throw;
            }
            finally
            {
                if (updateMap != null && !success)
                {
                    executor.MakeNonSerializable();
                }
            }            
        }
 
        void ThrowIfDynamicUpdateErrorExists(Collection<ActivityBlockingUpdate> updateErrors)
        {
            if (updateErrors != null && updateErrors.Count > 0)
            {
                // update error found
                // exit early
 
                throw FxTrace.Exception.AsError(new InstanceUpdateException(updateErrors));
            }
        }
 
        void ValidateWorkflow(WorkflowInstanceExtensionManager extensionManager)
        {
            if (!WorkflowDefinition.IsRuntimeReady)
            {
                LocationReferenceEnvironment localEnvironment = this.hostEnvironment;
                if (localEnvironment == null)
                {
                    LocationReferenceEnvironment parentEnvironment = null;
                    if (extensionManager != null && extensionManager.SymbolResolver != null)
                    {
                        parentEnvironment = extensionManager.SymbolResolver.AsLocationReferenceEnvironment();
                    }
                    localEnvironment = new ActivityLocationReferenceEnvironment(parentEnvironment);
                }
                IList<ValidationError> validationErrors = null;
                ActivityUtilities.CacheRootMetadata(WorkflowDefinition, localEnvironment, ProcessActivityTreeOptions.FullCachingOptions, null, ref validationErrors);
                ActivityValidationServices.ThrowIfViolationsExist(validationErrors);
            }
        }
 
        void EnsureDefinitionReady()
        {
            if (this.extensions != null)
            {
                this.extensions.Initialize();
                if (this.extensions.HasTrackingParticipant)
                {
                    this.HasTrackingParticipant = true;
                    if (this.trackingProvider == null)
                    {
                        this.trackingProvider = new TrackingProvider(this.WorkflowDefinition);
                    }
                    else
                    {
                        // TrackingProvider could be non-null if an earlier initialization attempt failed.
                        // This happens when WorkflowApplication calls Abort after a load failure. In this
                        // case we want to preserve any pending tracking records (e.g. DU failure).
                        this.trackingProvider.ClearParticipants();
                    }
                    foreach (TrackingParticipant trackingParticipant in GetExtensions<TrackingParticipant>())
                    {
                        this.trackingProvider.AddParticipant(trackingParticipant);
                    }
                }
            }
            else
            {
                // need to ensure the workflow has been validated since the host isn't using extensions (and so didn't register anything)
                ValidateWorkflow(null);
            }
        }
 
        void InitializeCore(IDictionary<string, object> workflowArgumentValues, IList<Handle> workflowExecutionProperties)
        {
            Fx.Assert(this.WorkflowDefinition.IsRuntimeReady, "EnsureDefinitionReady should have been called");
            Fx.Assert(this.executor != null, "at this point, we better have an executor");
 
            // Do Argument validation for root activities
            WorkflowDefinition.HasBeenAssociatedWithAnInstance = true;
 
            if (workflowArgumentValues != null)
            {
                IDictionary<string, object> actualInputs = workflowArgumentValues;
 
                if (object.ReferenceEquals(actualInputs, ActivityUtilities.EmptyParameters))
                {
                    actualInputs = null;
                }
 
                if (this.WorkflowDefinition.RuntimeArguments.Count > 0 || (actualInputs != null && actualInputs.Count > 0))
                {
                    ActivityValidationServices.ValidateRootInputs(this.WorkflowDefinition, actualInputs);
                }
 
                this.executor.ScheduleRootActivity(this.WorkflowDefinition, actualInputs, workflowExecutionProperties);
            }
            else
            {
                this.executor.OnDeserialized(this.WorkflowDefinition, this);
            }
 
            this.executor.Open(this.SynchronizationContext);
            this.controller = new WorkflowInstanceControl(this, this.executor);
            this.isInitialized = true;
 
            if (this.extensions != null && this.extensions.HasWorkflowInstanceExtensions)
            {
                WorkflowInstanceProxy proxy = new WorkflowInstanceProxy(this);
 
                for (int i = 0; i < this.extensions.WorkflowInstanceExtensions.Count; i++)
                {
                    IWorkflowInstanceExtension extension = this.extensions.WorkflowInstanceExtensions[i];
                    extension.SetInstance(proxy);
                }
            }
        }
 
        protected void ThrowIfReadOnly()
        {
            if (this.isInitialized)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowInstanceIsReadOnly(this.Id)));
            }
        }
 
        protected internal abstract IAsyncResult OnBeginResumeBookmark(Bookmark bookmark, object value, TimeSpan timeout, AsyncCallback callback, object state);
        protected internal abstract BookmarkResumptionResult OnEndResumeBookmark(IAsyncResult result);
 
        protected internal abstract IAsyncResult OnBeginPersist(AsyncCallback callback, object state);
        protected internal abstract void OnEndPersist(IAsyncResult result);
 
        protected internal abstract void OnDisassociateKeys(ICollection<InstanceKey> keys);
 
        protected internal abstract IAsyncResult OnBeginAssociateKeys(ICollection<InstanceKey> keys, AsyncCallback callback, object state);
        protected internal abstract void OnEndAssociateKeys(IAsyncResult result);
 
        internal IAsyncResult BeginFlushTrackingRecordsInternal(AsyncCallback callback, object state)
        {
            return OnBeginFlushTrackingRecords(callback, state);
        }
 
        internal void EndFlushTrackingRecordsInternal(IAsyncResult result)
        {
            OnEndFlushTrackingRecords(result);
        }
 
        protected void FlushTrackingRecords(TimeSpan timeout)
        {
            if (this.HasTrackingParticipant)
            {
                this.TrackingProvider.FlushPendingRecords(timeout);
            }
        }
 
        protected IAsyncResult BeginFlushTrackingRecords(TimeSpan timeout, AsyncCallback callback, object state)
        {
            if (this.HasTrackingParticipant)
            {
                return this.TrackingProvider.BeginFlushPendingRecords(timeout, callback, state);
            }
            else
            {
                return new CompletedAsyncResult(callback, state);
            }
        }
 
        protected void EndFlushTrackingRecords(IAsyncResult result)
        {
            if (this.HasTrackingParticipant)
            {
                this.TrackingProvider.EndFlushPendingRecords(result);
            }
            else
            {
                CompletedAsyncResult.End(result);
            }
        }
 
        protected virtual IAsyncResult OnBeginFlushTrackingRecords(AsyncCallback callback, object state)
        {
            return this.Controller.BeginFlushTrackingRecords(ActivityDefaults.TrackingTimeout, callback, state);
        }
 
        protected virtual void OnEndFlushTrackingRecords(IAsyncResult result)
        {
            this.Controller.EndFlushTrackingRecords(result);
        }
 
        internal void NotifyPaused()
        {
            if (this.executor.State != ActivityInstanceState.Executing)
            {
                TrackCompletion();
            }
 
            OnNotifyPaused();
        }
 
        protected abstract void OnNotifyPaused();
 
        internal void NotifyUnhandledException(Exception exception, Activity source, string sourceInstanceId)
        {
            if (this.controller.TrackingEnabled)
            {
                ActivityInfo faultSourceInfo = new ActivityInfo(source.DisplayName, source.Id, sourceInstanceId, source.GetType().FullName);
                this.controller.Track(new WorkflowInstanceUnhandledExceptionRecord(this.Id, this.WorkflowDefinition.DisplayName, faultSourceInfo, exception, this.DefinitionIdentity));
            }
 
            OnNotifyUnhandledException(exception, source, sourceInstanceId);
        }
 
        protected abstract void OnNotifyUnhandledException(Exception exception, Activity source, string sourceInstanceId);
 
        protected internal abstract void OnRequestAbort(Exception reason);
 
        internal void OnDeserialized(bool hasTrackedStarted)
        {
            this.HasTrackedStarted = hasTrackedStarted;
        }
 
        void StartOperation(ref bool resetRequired)
        {
            StartReadOnlyOperation(ref resetRequired);
 
            // isRunning can only flip to true by an operation and therefore
            // we don't have to worry about this changing under us
            if (this.executor.IsRunning)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.RuntimeRunning));
            }
        }
 
        void StartReadOnlyOperation(ref bool resetRequired)
        {
            bool wasPerformingOperation = false;
            try
            {
            }
            finally
            {
                wasPerformingOperation = Interlocked.CompareExchange(ref this.isPerformingOperation, True, False) == True;
 
                if (!wasPerformingOperation)
                {
                    resetRequired = true;
                }
            }
 
            if (wasPerformingOperation)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.RuntimeOperationInProgress));
            }
        }
 
        void FinishOperation(ref bool resetRequired)
        {
            if (resetRequired)
            {
                this.isPerformingOperation = False;
            }
        }
 
        internal void Abort(Exception reason)
        {
            if (!this.isAborted)
            {
                this.isAborted = true;
                if (reason != null)
                {
                    this.abortedException = reason;
                }
 
                if (this.extensions != null)
                {
                    this.extensions.Cancel();
                }
 
                if (this.controller.TrackingEnabled)
                {
                    // During abort we only track this one record
                    if (reason != null)
                    {
                        string message = reason.Message;
                        if (reason.InnerException != null)
                        {
                            message = SR.WorkflowAbortedReason(reason.Message, reason.InnerException.Message);
                        }
                        this.controller.Track(new WorkflowInstanceAbortedRecord(this.Id, this.WorkflowDefinition.DisplayName, message, this.DefinitionIdentity));
                    }
                }
#if DEBUG
                if (!Fx.FastDebug)
                {
                    if (reason != null)
                    {
                        reason.ToString();
                    }
                    this.abortStack = new StackTrace();
                }
#endif
            }
        }
 
        void ValidatePrepareForSerialization()
        {
            ThrowIfAborted();
            if (!this.Controller.IsPersistable)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.PrepareForSerializationRequiresPersistability));
            }
        }
 
        void ValidateScheduleResumeBookmark()
        {
            ThrowIfAborted();
            ThrowIfNotIdle();
        }
 
        void ValidateGetBookmarks()
        {
            ThrowIfAborted();
        }
 
        void ValidateGetMappedVariables()
        {
            ThrowIfAborted();
        }
 
        void ValidatePauseWhenPersistable()
        {
            ThrowIfAborted();
            if (this.Controller.IsPersistable)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.PauseWhenPersistableInvalidIfPersistable));
            }
        }
 
        void Terminate(Exception reason)
        {
            // validate we're in an ok state
            ThrowIfAborted();
 
            // terminate the runtime
            this.executor.Terminate(reason);
 
            // and track if necessary
            TrackCompletion();
 
        }
 
        void TrackCompletion()
        {
            if (this.controller.TrackingEnabled && !this.hasTrackedCompletion)
            {
                ActivityInstanceState completionState = this.executor.State;
 
                if (completionState == ActivityInstanceState.Faulted)
                {
                    Fx.Assert(this.executor.TerminationException != null, "must have a termination exception if we're faulted");
                    this.controller.Track(new WorkflowInstanceTerminatedRecord(this.Id, this.WorkflowDefinition.DisplayName, this.executor.TerminationException.Message, this.DefinitionIdentity));
                }
                else if (completionState == ActivityInstanceState.Closed)
                {
                    this.controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Completed, this.DefinitionIdentity));
                }
                else
                {
                    Fx.AssertAndThrow(completionState == ActivityInstanceState.Canceled, "Cannot be executing a workflow instance when WorkflowState was completed.");
                    this.controller.Track(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Canceled, this.DefinitionIdentity));
                }
                this.hasTrackedCompletion = true;
            }
        }
 
        void TrackResumed()
        {
            // track if necessary
            if (!this.hasTrackedResumed)
            {
                if (this.Controller.TrackingEnabled)
                {
                    if (!this.HasTrackedStarted)
                    {
                        this.TrackingProvider.AddRecord(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Started, this.DefinitionIdentity));
                        this.HasTrackedStarted = true;
                    }
                    else
                    {
                        this.TrackingProvider.AddRecord(new WorkflowInstanceRecord(this.Id, this.WorkflowDefinition.DisplayName, WorkflowInstanceStates.Resumed, this.DefinitionIdentity));
                    }
                }
                this.hasTrackedResumed = true;
            }
        }
 
        void Run()
        {
            // validate we're in an ok state
            ThrowIfAborted();
 
            TrackResumed();
 
            // and let the scheduler go
            this.executor.MarkSchedulerRunning();
        }
 
        void ScheduleCancel()
        {
            // validate we're in an ok state
            ThrowIfAborted();
 
            TrackResumed();
 
            this.executor.CancelRootActivity();
        }
 
        BookmarkResumptionResult ScheduleBookmarkResumption(Bookmark bookmark, object value)
        {
            // validate we're in an ok state
            ValidateScheduleResumeBookmark();
 
            TrackResumed();
 
            return this.executor.TryResumeHostBookmark(bookmark, value);
        }
 
        BookmarkResumptionResult ScheduleBookmarkResumption(Bookmark bookmark, object value, BookmarkScope scope)
        {
            // validate we're in an ok state
            ValidateScheduleResumeBookmark();
 
            TrackResumed();
 
            return this.executor.TryResumeBookmark(bookmark, value, scope);
        }
 
 
        void ThrowIfAborted()
        {
            if (this.isAborted || (this.executor != null && this.executor.IsAbortPending))
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WorkflowInstanceAborted(this.Id)));
            }
        }
 
        void ThrowIfNotIdle()
        {
            if (!this.executor.IsIdle)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.BookmarksOnlyResumableWhileIdle));
            }
        }
 
        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.NestedTypesShouldNotBeVisible,
            Justification = "these are effectively protected methods, but encapsulated in a struct to avoid naming conflicts")]
        protected struct WorkflowInstanceControl
        {
            ActivityExecutor executor;
            WorkflowInstance instance;
 
            internal WorkflowInstanceControl(WorkflowInstance instance, ActivityExecutor executor)
            {
                this.instance = instance;
                this.executor = executor;
            }
 
            public bool IsPersistable
            {
                get
                {
                    return this.executor.IsPersistable;
                }
            }
 
            public bool HasPendingTrackingRecords
            {
                get
                {
                    return this.instance.HasTrackingParticipant && this.instance.TrackingProvider.HasPendingRecords;
                }
            }
 
            public bool TrackingEnabled
            {
                get
                {
                    return this.instance.HasTrackingParticipant && this.instance.TrackingProvider.ShouldTrackWorkflowInstanceRecords;
                }
            }
 
            public WorkflowInstanceState State
            {
                get
                {
                    WorkflowInstanceState result;
 
                    if (this.instance.isAborted)
                    {
                        result = WorkflowInstanceState.Aborted;
                    }
                    else if (!this.executor.IsIdle)
                    {
                        result = WorkflowInstanceState.Runnable;
                    }
                    else
                    {
                        if (this.executor.State == ActivityInstanceState.Executing)
                        {
                            result = WorkflowInstanceState.Idle;
                        }
                        else
                        {
                            result = WorkflowInstanceState.Complete;
                        }
                    }
 
                    return result;
                }
            }
 
            public override bool Equals(object obj)
            {
                if (!(obj is WorkflowInstanceControl))
                {
                    return false;
                }
 
                WorkflowInstanceControl other = (WorkflowInstanceControl)obj;
                return other.instance == this.instance;
            }
 
            public override int GetHashCode()
            {
                return this.instance.GetHashCode();
            }
 
            public static bool operator ==(WorkflowInstanceControl left, WorkflowInstanceControl right)
            {
                return left.Equals(right);
            }
 
            public static bool operator !=(WorkflowInstanceControl left, WorkflowInstanceControl right)
            {
                return !left.Equals(right);
            }
 
            public ReadOnlyCollection<BookmarkInfo> GetBookmarks()
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartReadOnlyOperation(ref resetRequired);
 
                    this.instance.ValidateGetBookmarks();
 
                    return this.executor.GetAllBookmarks();
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public ReadOnlyCollection<BookmarkInfo> GetBookmarks(BookmarkScope scope)
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartReadOnlyOperation(ref resetRequired);
 
                    this.instance.ValidateGetBookmarks();
 
                    return this.executor.GetBookmarks(scope);
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public IDictionary<string, LocationInfo> GetMappedVariables()
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartReadOnlyOperation(ref resetRequired);
 
                    this.instance.ValidateGetMappedVariables();
 
                    IDictionary<string, LocationInfo> mappedLocations = this.instance.executor.GatherMappableVariables();
                    if (mappedLocations != null)
                    {
                        mappedLocations = new ReadOnlyDictionaryInternal<string, LocationInfo>(mappedLocations);
                    }
                    else
                    {
                        mappedLocations = WorkflowInstance.EmptyMappedVariablesDictionary;
                    }
                    return mappedLocations;
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public void Run()
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartOperation(ref resetRequired);
 
                    this.instance.Run();
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
 
                this.executor.Run();
            }
 
            public void RequestPause()
            {
                // No validations for this because we do not
                // require calls to Pause to be synchronized
                // by the caller
                this.executor.PauseScheduler();
            }
 
            // Calls Pause when IsPersistable goes from false->true
            public void PauseWhenPersistable()
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartOperation(ref resetRequired);
 
                    this.instance.ValidatePauseWhenPersistable();
 
                    this.executor.PauseWhenPersistable();
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public void ScheduleCancel()
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartOperation(ref resetRequired);
 
                    this.instance.ScheduleCancel();
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public void Terminate(Exception reason)
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartOperation(ref resetRequired);
 
                    this.instance.Terminate(reason);
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public BookmarkResumptionResult ScheduleBookmarkResumption(Bookmark bookmark, object value)
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartOperation(ref resetRequired);
 
                    return this.instance.ScheduleBookmarkResumption(bookmark, value);
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public BookmarkResumptionResult ScheduleBookmarkResumption(Bookmark bookmark, object value, BookmarkScope scope)
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartOperation(ref resetRequired);
 
                    return this.instance.ScheduleBookmarkResumption(bookmark, value, scope);
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public void Abort()
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartOperation(ref resetRequired);
 
                    // No validations
 
                    this.executor.Dispose();
 
                    this.instance.Abort(null);
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public void Abort(Exception reason)
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartOperation(ref resetRequired);
 
                    // No validations
 
                    this.executor.Abort(reason);
 
                    this.instance.Abort(reason);
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            [SuppressMessage(FxCop.Category.Design, FxCop.Rule.ConsiderPassingBaseTypesAsParameters,
                Justification = "Only want to allow WorkflowInstanceRecord subclasses for WorkflowInstance-level tracking")]
            public void Track(WorkflowInstanceRecord instanceRecord)
            {
                if (this.instance.HasTrackingParticipant)
                {
                    this.instance.TrackingProvider.AddRecord(instanceRecord);
                }
            }
 
            public void FlushTrackingRecords(TimeSpan timeout)
            {
                this.instance.FlushTrackingRecords(timeout);
            }
 
            public IAsyncResult BeginFlushTrackingRecords(TimeSpan timeout, AsyncCallback callback, object state)
            {
                return this.instance.BeginFlushTrackingRecords(timeout, callback, state);
            }
 
            public void EndFlushTrackingRecords(IAsyncResult result)
            {
                this.instance.EndFlushTrackingRecords(result);
            }
 
            public object PrepareForSerialization()
            {
                bool resetRequired = false;
 
                try
                {
                    this.instance.StartReadOnlyOperation(ref resetRequired);
 
                    this.instance.ValidatePrepareForSerialization();
 
                    return this.executor.PrepareForSerialization();
                }
                finally
                {
                    this.instance.FinishOperation(ref resetRequired);
                }
            }
 
            public ActivityInstanceState GetCompletionState()
            {
                return this.executor.State;
            }
 
            [SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters,
                Justification = "Arch approved design. Requires the out argument for extra information provided")]
            public ActivityInstanceState GetCompletionState(out Exception terminationException)
            {
                terminationException = this.executor.TerminationException;
                return this.executor.State;
            }
 
            [SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters,
                Justification = "Arch approved design. Requires the out argument for extra information provided")]
            public ActivityInstanceState GetCompletionState(out IDictionary<string, object> outputs, out Exception terminationException)
            {
                outputs = this.executor.WorkflowOutputs;
                terminationException = this.executor.TerminationException;
                return this.executor.State;
            }
 
            public Exception GetAbortReason()
            {
                return this.instance.abortedException;
            }
        }
    }
}