File: FrameworkFork\System.ServiceModel\Internals\System\Runtime\ActionItem.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Diagnostics.Contracts;
using System.Runtime.Diagnostics;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
 
namespace System.Runtime
{
    internal abstract class ActionItem
    {
        private bool _isScheduled;
        private bool _lowPriority;
 
        protected ActionItem()
        {
        }
 
        public bool LowPriority
        {
            get
            {
                return _lowPriority;
            }
            protected set
            {
                _lowPriority = value;
            }
        }
 
        public static void Schedule(Action<object> callback, object state)
        {
            Contract.Assert(callback != null, "Cannot schedule a null callback");
            Task.Factory.StartNew(callback, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
        }
 
        [Fx.Tag.SecurityNote(Critical = "Called after applying the user context on the stack or (potentially) " +
            "without any user context on the stack")]
        [SecurityCritical]
        protected abstract void Invoke();
 
        [Fx.Tag.SecurityNote(Critical = "Access critical field context and critical property " +
            "CallbackHelper.InvokeWithContextCallback, calls into critical method " +
            "PartialTrustHelpers.CaptureSecurityContextNoIdentityFlow, calls into critical method ScheduleCallback; " +
            "since the invoked method and the capturing of the security contex are de-coupled, can't " +
            "be treated as safe")]
        [SecurityCritical]
        protected void Schedule()
        {
            if (_isScheduled)
            {
                throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.ActionItemIsAlreadyScheduled));
            }
 
            _isScheduled = true;
            ScheduleCallback(CallbackHelper.InvokeCallbackAction);
        }
        [Fx.Tag.SecurityNote(Critical = "Calls into critical static method ScheduleCallback")]
        [SecurityCritical]
 
        private void ScheduleCallback(Action<object> callback)
        {
            Fx.Assert(callback != null, "Cannot schedule a null callback");
            Task.Factory.StartNew(callback, this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
        }
 
        [SecurityCritical]
        internal static class CallbackHelper
        {
            [Fx.Tag.SecurityNote(Critical = "Stores a delegate to a critical method")]
            private static Action<object> s_invokeCallback;
 
            public static Action<object> InvokeCallbackAction
            {
                get
                {
                    if (s_invokeCallback == null)
                    {
                        s_invokeCallback = new Action<object>(InvokeCallback);
                    }
                    return s_invokeCallback;
                }
            }
 
            [Fx.Tag.SecurityNote(Critical = "Called by the scheduler without any user context on the stack")]
            private static void InvokeCallback(object state)
            {
                ((ActionItem)state).Invoke();
                ((ActionItem)state)._isScheduled = false;
            }
        }
 
        internal class DefaultActionItem : ActionItem
        {
            [Fx.Tag.SecurityNote(Critical = "Stores a delegate that will be called later, at a particular context")]
            [SecurityCritical]
            private Action<object> _callback;
            [Fx.Tag.SecurityNote(Critical = "Stores an object that will be passed to the delegate that will be " +
                            "called later, at a particular context")]
            [SecurityCritical]
            private object _state;
 
            private bool _flowLegacyActivityId;
            private Guid _activityId;
            private EventTraceActivity _eventTraceActivity;
 
            [Fx.Tag.SecurityNote(Critical = "Access critical fields callback and state",
                Safe = "Doesn't leak information or resources")]
            [SecuritySafeCritical]
            public DefaultActionItem(Action<object> callback, object state, bool isLowPriority)
            {
                Fx.Assert(callback != null, "Shouldn't instantiate an object to wrap a null callback");
                base.LowPriority = isLowPriority;
                _callback = callback;
                _state = state;
                if (WaitCallbackActionItem.ShouldUseActivity)
                {
                    _flowLegacyActivityId = true;
                    _activityId = EtwDiagnosticTrace.ActivityId;
                }
                if (Fx.Trace.IsEnd2EndActivityTracingEnabled)
                {
                    _eventTraceActivity = EventTraceActivity.GetFromThreadOrCreate();
                    if (TraceCore.ActionItemScheduledIsEnabled(Fx.Trace))
                    {
                        TraceCore.ActionItemScheduled(Fx.Trace, _eventTraceActivity);
                    }
                }
            }
 
            [Fx.Tag.SecurityNote(Critical = "Implements a the critical abstract ActionItem.Invoke method, " +
                "Access critical fields callback and state")]
            [SecurityCritical]
            protected override void Invoke()
            {
                if (_flowLegacyActivityId || Fx.Trace.IsEnd2EndActivityTracingEnabled)
                {
                    TraceAndInvoke();
                }
                else
                {
                    _callback(_state);
                }
            }
            [Fx.Tag.SecurityNote(Critical = "Implements a the critical abstract Trace method, " +
                                "Access critical fields callback and state")]
            [SecurityCritical]
 
            private void TraceAndInvoke()
            {
                if (_flowLegacyActivityId)
                {
                    Guid currentActivityId = EtwDiagnosticTrace.ActivityId;
                    try
                    {
                        EtwDiagnosticTrace.ActivityId = _activityId;
                        _callback(_state);
                    }
                    finally
                    {
                        EtwDiagnosticTrace.ActivityId = currentActivityId;
                    }
                }
                else
                {
                    Guid previous = Guid.Empty;
                    _callback(_state);
                }
            }
        }
    }
}