File: System\Activities\Runtime\CompletionCallbackWrapper.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.Activities.DynamicUpdate;
    using System.Collections.ObjectModel;
    using System.Runtime;
    using System.Runtime.Serialization;
    using System.Security;
 
    // can't add FuncCompletionCallbackWrapper<T> since we don't know what to close the generic with
    [KnownType(typeof(ActivityCompletionCallbackWrapper))]
    [KnownType(typeof(DelegateCompletionCallbackWrapper))]
    [DataContract]
    abstract class CompletionCallbackWrapper : CallbackWrapper
    {
        static Type completionCallbackType = typeof(CompletionCallback);
        static Type[] completionCallbackParameters = new Type[] { typeof(NativeActivityContext), typeof(ActivityInstance) };
 
        bool checkForCancelation;
 
        bool needsToGatherOutputs;
 
        protected CompletionCallbackWrapper(Delegate callback, ActivityInstance owningInstance)
            : base(callback, owningInstance)
        {
        }
 
        protected bool NeedsToGatherOutputs
        {
            get
            {
                return this.needsToGatherOutputs;
            }
 
            set
            {
                this.needsToGatherOutputs = value;
            }
        }
 
        [DataMember(EmitDefaultValue = false, Name = "checkForCancelation")]
        internal bool SerializedCheckForCancelation
        {
            get { return this.checkForCancelation; }
            set { this.checkForCancelation = value; }
        }
 
        [DataMember(EmitDefaultValue = false, Name = "needsToGatherOutputs")]
        internal bool SerializedNeedsToGatherOutputs
        {
            get { return this.needsToGatherOutputs; }
            set { this.needsToGatherOutputs = value; }
        }
 
        public void CheckForCancelation()
        {
            this.checkForCancelation = true;
        }
 
        protected virtual void GatherOutputs(ActivityInstance completedInstance)
        {
            // No-op in the base class
        }
 
        internal WorkItem CreateWorkItem(ActivityInstance completedInstance, ActivityExecutor executor)
        {
            // We use the property to guard against the virtual method call
            // since we don't need it in the common case
            if (this.NeedsToGatherOutputs)
            {
                this.GatherOutputs(completedInstance);
            }
 
            CompletionWorkItem workItem;
 
            if (this.checkForCancelation)
            {
                workItem = new CompletionWithCancelationCheckWorkItem(this, completedInstance);
            }
            else
            {
                workItem = executor.CompletionWorkItemPool.Acquire();
                workItem.Initialize(this, completedInstance);
            }
 
            if (completedInstance.InstanceMap != null)
            {
                completedInstance.InstanceMap.AddEntry(workItem);
            }
 
            return workItem;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Because any implementation will be calling EnsureCallback",
            Safe = "Safe because the method needs to be part of an Activity and we are casting to the callback type and it has a very specific signature. The author of the callback is buying into being invoked from PT.")]
        [SecuritySafeCritical]
        protected internal abstract void Invoke(NativeActivityContext context, ActivityInstance completedInstance);
 
        [DataContract]
        public class CompletionWorkItem : ActivityExecutionWorkItem, ActivityInstanceMap.IActivityReference
        {
            CompletionCallbackWrapper callbackWrapper;
            ActivityInstance completedInstance;
 
            // Called by the Pool.
            public CompletionWorkItem()
            {
                this.IsPooled = true;
            }
 
            // Only used by non-pooled base classes.
            protected CompletionWorkItem(CompletionCallbackWrapper callbackWrapper, ActivityInstance completedInstance)
                : base(callbackWrapper.ActivityInstance)
            {
                this.callbackWrapper = callbackWrapper;
                this.completedInstance = completedInstance;
            }
 
            protected ActivityInstance CompletedInstance
            {
                get
                {
                    return this.completedInstance;
                }
            }
 
            [DataMember(Name = "callbackWrapper")]
            internal CompletionCallbackWrapper SerializedCallbackWrapper
            {
                get { return this.callbackWrapper; }
                set { this.callbackWrapper = value; }
            }
 
            [DataMember(Name = "completedInstance")]
            internal ActivityInstance SerializedCompletedInstance
            {
                get { return this.completedInstance; }
                set { this.completedInstance = value; }
            }
 
            public void Initialize(CompletionCallbackWrapper callbackWrapper, ActivityInstance completedInstance)
            {
                base.Reinitialize(callbackWrapper.ActivityInstance);
                this.callbackWrapper = callbackWrapper;
                this.completedInstance = completedInstance;
            }
 
            protected override void ReleaseToPool(ActivityExecutor executor)
            {
                base.ClearForReuse();
                this.callbackWrapper = null;
                this.completedInstance = null;
            
                executor.CompletionWorkItemPool.Release(this);
            }
 
            public override void TraceCompleted()
            {
                if (TD.CompleteCompletionWorkItemIsEnabled())
                {
                    TD.CompleteCompletionWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id, this.completedInstance.Activity.GetType().ToString(), this.completedInstance.Activity.DisplayName, this.completedInstance.Id);
                }
            }
 
            public override void TraceScheduled()
            {
                if (TD.ScheduleCompletionWorkItemIsEnabled())
                {
                    TD.ScheduleCompletionWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id, this.completedInstance.Activity.GetType().ToString(), this.completedInstance.Activity.DisplayName, this.completedInstance.Id);
                }
            }
 
            public override void TraceStarting()
            {
                if (TD.StartCompletionWorkItemIsEnabled())
                {
                    TD.StartCompletionWorkItem(this.ActivityInstance.Activity.GetType().ToString(), this.ActivityInstance.Activity.DisplayName, this.ActivityInstance.Id, this.completedInstance.Activity.GetType().ToString(), this.completedInstance.Activity.DisplayName, this.completedInstance.Id);
                }
            }
 
            public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
            {
                NativeActivityContext context = executor.NativeActivityContextPool.Acquire();
 
                Fx.Assert(this.completedInstance.Activity != null, "Activity definition should always be associated with an activity instance.");
 
                try
                {
                    context.Initialize(this.ActivityInstance, executor, bookmarkManager);
                    this.callbackWrapper.Invoke(context, this.completedInstance);
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
 
                    this.ExceptionToPropagate = e;
                }
                finally
                {
                    context.Dispose();
                    executor.NativeActivityContextPool.Release(context);
 
                    if (this.ActivityInstance.InstanceMap != null)
                    {
                        this.ActivityInstance.InstanceMap.RemoveEntry(this);
                    }
                }
 
                return true;
            }
 
            Activity ActivityInstanceMap.IActivityReference.Activity
            {
                get 
                {
                    return this.completedInstance.Activity;
                }
            }
 
            void ActivityInstanceMap.IActivityReference.Load(Activity activity, ActivityInstanceMap instanceMap)
            {
                if (this.completedInstance.Activity == null)
                {
                    ((ActivityInstanceMap.IActivityReference)this.completedInstance).Load(activity, instanceMap);
                }
            }
 
        }
 
        [DataContract]
        class CompletionWithCancelationCheckWorkItem : CompletionWorkItem
        {
            public CompletionWithCancelationCheckWorkItem(CompletionCallbackWrapper callbackWrapper, ActivityInstance completedInstance)
                : base(callbackWrapper, completedInstance)
            {
            }
 
            public override bool Execute(ActivityExecutor executor, BookmarkManager bookmarkManager)
            {
                if (this.CompletedInstance.State != ActivityInstanceState.Closed && this.ActivityInstance.IsPerformingDefaultCancelation)
                {
                    this.ActivityInstance.MarkCanceled();
                }
 
                return base.Execute(executor, bookmarkManager);
            }
        }
    }
}