File: System\Activities\AsyncOperationContext.cs
Project: ndp\cdf\src\NetFx40\System.Activities\System.Activities.csproj (System.Activities)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.Activities
{
    using System;
    using System.Activities.Runtime;
    using System.Runtime;
    using System.Activities.Hosting;
 
    class AsyncOperationContext
    {
        static AsyncCallback onResumeAsyncCodeActivityBookmark;
     
        ActivityExecutor executor;
        ActivityInstance owningActivityInstance;
        bool hasCanceled;
        bool hasCompleted;
 
        internal AsyncOperationContext(ActivityExecutor executor, ActivityInstance owningActivityInstance)
        {
            this.executor = executor;
            this.owningActivityInstance = owningActivityInstance;
        }
 
        internal bool IsStillActive
        {
            get
            {
                return !this.hasCanceled && !this.hasCompleted;
            }
        }
 
        public object UserState
        {
            get;
            set;
        }
 
        public bool HasCalledAsyncCodeActivityCancel
        {
            get;
            set;
        }
 
        public bool IsAborting
        {
            get;
            set;
        }
 
        bool ShouldCancel()
        {
            return this.IsStillActive;
        }
 
        bool ShouldComplete()
        {
            if (this.hasCanceled)
            {
                return false;
            }
 
            if (this.hasCompleted)
            {
                throw FxTrace.Exception.AsError(new InvalidOperationException(SR.OperationAlreadyCompleted));
            }
 
            return true;
        }
 
        internal void CancelOperation()
        {
            if (ShouldCancel())
            {
                this.executor.CompleteOperation(this.owningActivityInstance);
            }
 
            this.hasCanceled = true;
        }
 
        public void CompleteOperation()
        {
            if (ShouldComplete())
            {
                this.executor.CompleteOperation(this.owningActivityInstance);
 
                this.hasCompleted = true;
            }
        }
 
        // used by AsyncCodeActivity to efficiently complete a "true" async operation
        internal void CompleteAsyncCodeActivity(CompleteData completeData)
        {
            Fx.Assert(completeData != null, "caller must validate this is not null");
 
            if (!this.ShouldComplete())
            {
                // nothing to do here
                return;
            }
 
            if (onResumeAsyncCodeActivityBookmark == null)
            {
                onResumeAsyncCodeActivityBookmark = Fx.ThunkCallback(new AsyncCallback(OnResumeAsyncCodeActivityBookmark));
            }
 
            try
            {
                IAsyncResult result = this.executor.BeginResumeBookmark(Bookmark.AsyncOperationCompletionBookmark,
                    completeData, TimeSpan.MaxValue, onResumeAsyncCodeActivityBookmark, this.executor);
                if (result.CompletedSynchronously)
                {
                    this.executor.EndResumeBookmark(result);
                }
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e))
                {
                    throw;
                }
 
                this.executor.AbortWorkflowInstance(e);
            }
        }
 
        static void OnResumeAsyncCodeActivityBookmark(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
            {
                return;
            }
 
            ActivityExecutor executor = (ActivityExecutor)result.AsyncState;
 
            try
            {
                executor.EndResumeBookmark(result);
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e))
                {
                    throw;
                }
 
                executor.AbortWorkflowInstance(e);
            }
        }
 
        internal abstract class CompleteData
        {
            AsyncOperationContext context;
            bool isCancel;
 
            protected CompleteData(AsyncOperationContext context, bool isCancel)
            {
                Fx.Assert(context != null, "Cannot have a null context.");
                this.context = context;
                this.isCancel = isCancel;
            }
 
            protected ActivityExecutor Executor
            {
                get
                {
                    return this.context.executor;
                }
            }
 
            public ActivityInstance Instance
            {
                get
                {
                    return this.context.owningActivityInstance;
                }
            }
 
            protected AsyncOperationContext AsyncContext
            {
                get
                {
                    return this.context;
                }
            }
 
            // This method will throw if the complete/cancel is now invalid, it will return
            // true if the complete/cancel should proceed, and return false if the complete/cancel
            // should be ignored.
            bool ShouldCallExecutor()
            {
                if (this.isCancel)
                {
                    return this.context.ShouldCancel();
                }
                else
                {
                    return this.context.ShouldComplete();
                }
            }
 
            // This must be called from a workflow thread
            public void CompleteOperation()
            {
                if (ShouldCallExecutor())
                {
                    OnCallExecutor();
 
                    // We only update hasCompleted if we just did the completion work.
                    // Calling Cancel followed by Complete does not mean you've completed.
                    if (!this.isCancel)
                    {
                        this.context.hasCompleted = true;
                    }
                }
 
                // We update hasCanceled even if we skipped the actual work.
                // Calling Complete followed by Cancel does imply that you have canceled.
                if (this.isCancel)
                {
                    this.context.hasCanceled = true;
                }
            }
 
            protected abstract void OnCallExecutor();
        }
    }
}