344 lines
13 KiB
C#
344 lines
13 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
|
||
|
namespace System.Runtime
|
||
|
{
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.Diagnostics;
|
||
|
using System.Security;
|
||
|
using System.Threading;
|
||
|
|
||
|
abstract class ActionItem
|
||
|
{
|
||
|
#if FEATURE_COMPRESSEDSTACK
|
||
|
[Fx.Tag.SecurityNote(Critical = "Stores the security context, used later in binding back into")]
|
||
|
[SecurityCritical]
|
||
|
SecurityContext context;
|
||
|
#endif
|
||
|
bool isScheduled;
|
||
|
|
||
|
bool lowPriority;
|
||
|
|
||
|
protected ActionItem()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public bool LowPriority
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.lowPriority;
|
||
|
}
|
||
|
protected set
|
||
|
{
|
||
|
this.lowPriority = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static void Schedule(Action<object> callback, object state)
|
||
|
{
|
||
|
Schedule(callback, state, false);
|
||
|
}
|
||
|
[Fx.Tag.SecurityNote(Critical = "Calls into critical method ScheduleCallback",
|
||
|
Safe = "Schedule invoke of the given delegate under the current context")]
|
||
|
[SecuritySafeCritical]
|
||
|
public static void Schedule(Action<object> callback, object state, bool lowPriority)
|
||
|
{
|
||
|
Fx.Assert(callback != null, "A null callback was passed for Schedule!");
|
||
|
|
||
|
if (PartialTrustHelpers.ShouldFlowSecurityContext ||
|
||
|
WaitCallbackActionItem.ShouldUseActivity ||
|
||
|
Fx.Trace.IsEnd2EndActivityTracingEnabled)
|
||
|
{
|
||
|
new DefaultActionItem(callback, state, lowPriority).Schedule();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ScheduleCallback(callback, state, lowPriority);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[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));
|
||
|
}
|
||
|
|
||
|
this.isScheduled = true;
|
||
|
#if FEATURE_COMPRESSEDSTACK
|
||
|
if (PartialTrustHelpers.ShouldFlowSecurityContext)
|
||
|
{
|
||
|
this.context = PartialTrustHelpers.CaptureSecurityContextNoIdentityFlow();
|
||
|
}
|
||
|
if (this.context != null)
|
||
|
{
|
||
|
ScheduleCallback(CallbackHelper.InvokeWithContextCallback);
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
ScheduleCallback(CallbackHelper.InvokeWithoutContextCallback);
|
||
|
}
|
||
|
}
|
||
|
#if FEATURE_COMPRESSEDSTACK
|
||
|
[Fx.Tag.SecurityNote(Critical = "Access critical field context and critical property " +
|
||
|
"CallbackHelper.InvokeWithContextCallback, calls into critical method ScheduleCallback; " +
|
||
|
"since nothing is known about the given context, can't be treated as safe")]
|
||
|
[SecurityCritical]
|
||
|
protected void ScheduleWithContext(SecurityContext context)
|
||
|
{
|
||
|
if (context == null)
|
||
|
{
|
||
|
throw Fx.Exception.ArgumentNull("context");
|
||
|
}
|
||
|
if (isScheduled)
|
||
|
{
|
||
|
throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.ActionItemIsAlreadyScheduled));
|
||
|
}
|
||
|
|
||
|
this.isScheduled = true;
|
||
|
this.context = context.CreateCopy();
|
||
|
ScheduleCallback(CallbackHelper.InvokeWithContextCallback);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Access critical property CallbackHelper.InvokeWithoutContextCallback, " +
|
||
|
"Calls into critical method ScheduleCallback; not bound to a security context")]
|
||
|
[SecurityCritical]
|
||
|
protected void ScheduleWithoutContext()
|
||
|
{
|
||
|
if (isScheduled)
|
||
|
{
|
||
|
throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.ActionItemIsAlreadyScheduled));
|
||
|
}
|
||
|
|
||
|
this.isScheduled = true;
|
||
|
ScheduleCallback(CallbackHelper.InvokeWithoutContextCallback);
|
||
|
}
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Calls into critical methods IOThreadScheduler.ScheduleCallbackNoFlow, " +
|
||
|
"IOThreadScheduler.ScheduleCallbackLowPriNoFlow")]
|
||
|
[SecurityCritical]
|
||
|
static void ScheduleCallback(Action<object> callback, object state, bool lowPriority)
|
||
|
{
|
||
|
Fx.Assert(callback != null, "Cannot schedule a null callback");
|
||
|
if (lowPriority)
|
||
|
{
|
||
|
IOThreadScheduler.ScheduleCallbackLowPriNoFlow(callback, state);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IOThreadScheduler.ScheduleCallbackNoFlow(callback, state);
|
||
|
}
|
||
|
}
|
||
|
#if FEATURE_COMPRESSEDSTACK
|
||
|
[Fx.Tag.SecurityNote(Critical = "Extract the security context stored and reset the critical field")]
|
||
|
[SecurityCritical]
|
||
|
SecurityContext ExtractContext()
|
||
|
{
|
||
|
Fx.Assert(this.context != null, "Cannot bind to a null context; context should have been set by now");
|
||
|
Fx.Assert(this.isScheduled, "Context is extracted only while the object is scheduled");
|
||
|
SecurityContext result = this.context;
|
||
|
this.context = null;
|
||
|
return result;
|
||
|
}
|
||
|
#endif
|
||
|
[Fx.Tag.SecurityNote(Critical = "Calls into critical static method ScheduleCallback")]
|
||
|
[SecurityCritical]
|
||
|
void ScheduleCallback(Action<object> callback)
|
||
|
{
|
||
|
ScheduleCallback(callback, this, this.lowPriority);
|
||
|
}
|
||
|
|
||
|
[SecurityCritical]
|
||
|
static class CallbackHelper
|
||
|
{
|
||
|
#if FEATURE_COMPRESSEDSTACK
|
||
|
[Fx.Tag.SecurityNote(Critical = "Stores a delegate to a critical method")]
|
||
|
static Action<object> invokeWithContextCallback;
|
||
|
#endif
|
||
|
[Fx.Tag.SecurityNote(Critical = "Stores a delegate to a critical method")]
|
||
|
static Action<object> invokeWithoutContextCallback;
|
||
|
[Fx.Tag.SecurityNote(Critical = "Stores a delegate to a critical method")]
|
||
|
static ContextCallback onContextAppliedCallback;
|
||
|
|
||
|
#if FEATURE_COMPRESSEDSTACK
|
||
|
[Fx.Tag.SecurityNote(Critical = "Provides access to a critical field; Initialize it with " +
|
||
|
"a delegate to a critical method")]
|
||
|
public static Action<object> InvokeWithContextCallback
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (invokeWithContextCallback == null)
|
||
|
{
|
||
|
invokeWithContextCallback = new Action<object>(InvokeWithContext);
|
||
|
}
|
||
|
return invokeWithContextCallback;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
[Fx.Tag.SecurityNote(Critical = "Provides access to a critical field; Initialize it with " +
|
||
|
"a delegate to a critical method")]
|
||
|
public static Action<object> InvokeWithoutContextCallback
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (invokeWithoutContextCallback == null)
|
||
|
{
|
||
|
invokeWithoutContextCallback = new Action<object>(InvokeWithoutContext);
|
||
|
}
|
||
|
return invokeWithoutContextCallback;
|
||
|
}
|
||
|
}
|
||
|
[Fx.Tag.SecurityNote(Critical = "Provides access to a critical field; Initialize it with " +
|
||
|
"a delegate to a critical method")]
|
||
|
public static ContextCallback OnContextAppliedCallback
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (onContextAppliedCallback == null)
|
||
|
{
|
||
|
onContextAppliedCallback = new ContextCallback(OnContextApplied);
|
||
|
}
|
||
|
return onContextAppliedCallback;
|
||
|
}
|
||
|
}
|
||
|
#if FEATURE_COMPRESSEDSTACK
|
||
|
[Fx.Tag.SecurityNote(Critical = "Called by the scheduler without any user context on the stack")]
|
||
|
static void InvokeWithContext(object state)
|
||
|
{
|
||
|
SecurityContext context = ((ActionItem)state).ExtractContext();
|
||
|
SecurityContext.Run(context, OnContextAppliedCallback, state);
|
||
|
}
|
||
|
#endif
|
||
|
[Fx.Tag.SecurityNote(Critical = "Called by the scheduler without any user context on the stack")]
|
||
|
static void InvokeWithoutContext(object state)
|
||
|
{
|
||
|
((ActionItem)state).Invoke();
|
||
|
((ActionItem)state).isScheduled = false;
|
||
|
}
|
||
|
[Fx.Tag.SecurityNote(Critical = "Called after applying the user context on the stack")]
|
||
|
static void OnContextApplied(object o)
|
||
|
{
|
||
|
((ActionItem)o).Invoke();
|
||
|
((ActionItem)o).isScheduled = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class DefaultActionItem : ActionItem
|
||
|
{
|
||
|
[Fx.Tag.SecurityNote(Critical = "Stores a delegate that will be called later, at a particular context")]
|
||
|
[SecurityCritical]
|
||
|
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]
|
||
|
object state;
|
||
|
|
||
|
bool flowLegacyActivityId;
|
||
|
Guid activityId;
|
||
|
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;
|
||
|
this.callback = callback;
|
||
|
this.state = state;
|
||
|
if (WaitCallbackActionItem.ShouldUseActivity)
|
||
|
{
|
||
|
this.flowLegacyActivityId = true;
|
||
|
this.activityId = EtwDiagnosticTrace.ActivityId;
|
||
|
}
|
||
|
if (Fx.Trace.IsEnd2EndActivityTracingEnabled)
|
||
|
{
|
||
|
this.eventTraceActivity = EventTraceActivity.GetFromThreadOrCreate();
|
||
|
if (TraceCore.ActionItemScheduledIsEnabled(Fx.Trace))
|
||
|
{
|
||
|
TraceCore.ActionItemScheduled(Fx.Trace, this.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 (this.flowLegacyActivityId || Fx.Trace.IsEnd2EndActivityTracingEnabled)
|
||
|
{
|
||
|
TraceAndInvoke();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.callback(this.state);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Implements a the critical abstract Trace method, " +
|
||
|
"Access critical fields callback and state")]
|
||
|
[SecurityCritical]
|
||
|
void TraceAndInvoke()
|
||
|
{
|
||
|
//
|
||
|
if (this.flowLegacyActivityId)
|
||
|
{
|
||
|
Guid currentActivityId = EtwDiagnosticTrace.ActivityId;
|
||
|
try
|
||
|
{
|
||
|
EtwDiagnosticTrace.ActivityId = this.activityId;
|
||
|
this.callback(this.state);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
EtwDiagnosticTrace.ActivityId = currentActivityId;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Guid previous = Guid.Empty;
|
||
|
bool restoreActivityId = false;
|
||
|
try
|
||
|
{
|
||
|
if (this.eventTraceActivity != null)
|
||
|
{
|
||
|
previous = Trace.CorrelationManager.ActivityId;
|
||
|
restoreActivityId = true;
|
||
|
Trace.CorrelationManager.ActivityId = this.eventTraceActivity.ActivityId;
|
||
|
if (TraceCore.ActionItemCallbackInvokedIsEnabled(Fx.Trace))
|
||
|
{
|
||
|
TraceCore.ActionItemCallbackInvoked(Fx.Trace, this.eventTraceActivity);
|
||
|
}
|
||
|
}
|
||
|
this.callback(this.state);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (restoreActivityId)
|
||
|
{
|
||
|
Trace.CorrelationManager.ActivityId = previous;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|