|
using System;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using MS.Internal.WindowsBase;
using MS.Internal;
namespace System.Windows.Threading
{
/// <summary>
/// DispatcherOperation represents a delegate that has been
/// posted to the Dispatcher queue.
/// </summary>
public class DispatcherOperation
{
/// <SecurityNote>
/// Critical: Accesses a critical field.
/// TreatAsSafe: Initializing critical field to a known safe value.
/// </SecurityNote>
[SecuritySafeCritical]
static DispatcherOperation()
{
_invokeInSecurityContext = new ContextCallback(InvokeInSecurityContext);
}
/// <SecurityNote>
/// Critical: accesses _executionContext
/// </SecurityNote>
[SecurityCritical]
internal DispatcherOperation(
Dispatcher dispatcher,
Delegate method,
DispatcherPriority priority,
object args,
int numArgs,
DispatcherOperationTaskSource taskSource,
bool useAsyncSemantics)
{
_dispatcher = dispatcher;
_method = method;
_priority = priority;
_numArgs = numArgs;
_args = args;
_executionContext = CulturePreservingExecutionContext.Capture();
_taskSource = taskSource;
_taskSource.Initialize(this);
_useAsyncSemantics = useAsyncSemantics;
}
/// <SecurityNote>
/// Critical: calls critical constructor
/// </SecurityNote>
[SecurityCritical]
internal DispatcherOperation(
Dispatcher dispatcher,
Delegate method,
DispatcherPriority priority,
object args,
int numArgs) : this(
dispatcher,
method,
priority,
args,
numArgs,
new DispatcherOperationTaskSource<object>(),
false)
{
}
/// <SecurityNote>
/// Critical: calls critical constructor
/// </SecurityNote>
[SecurityCritical]
internal DispatcherOperation(
Dispatcher dispatcher,
DispatcherPriority priority,
Action action) : this(
dispatcher,
action,
priority,
null,
0,
new DispatcherOperationTaskSource<object>(),
true)
{
}
/// <SecurityNote>
/// Critical: calls critical constructor
/// </SecurityNote>
[SecurityCritical]
internal DispatcherOperation(
Dispatcher dispatcher,
DispatcherPriority priority,
Delegate method,
object[] args) : this(
dispatcher,
method,
priority,
args,
-1,
new DispatcherOperationTaskSource<object>(),
true)
{
}
/// <summary>
/// Returns the Dispatcher that this operation was posted to.
/// </summary>
public Dispatcher Dispatcher
{
get
{
return _dispatcher;
}
}
/// <summary>
/// Gets or sets the priority of this operation within the
/// Dispatcher queue.
/// </summary>
public DispatcherPriority Priority // NOTE: should be Priority
{
get
{
return _priority;
}
set
{
Dispatcher.ValidatePriority(value, "value");
if(value != _priority && _dispatcher.SetPriority(this, value))
{
_priority = value;
}
}
}
/// <summary>
/// The status of this operation.
/// </summary>
public DispatcherOperationStatus Status
{
get
{
return _status;
}
}
/// <summary>
/// Returns a Task representing the operation.
/// </summary>
public Task Task
{
get
{
return _taskSource.GetTask();
}
}
/// <summary>
/// Returns an awaiter for awaiting the completion of the operation.
/// </summary>
/// <remarks>
/// This method is intended to be used by compilers.
/// </remarks>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public TaskAwaiter GetAwaiter()
{
return Task.GetAwaiter();
}
/// <summary>
/// Waits for this operation to complete.
/// </summary>
/// <returns>
/// The status of the operation. To obtain the return value
/// of the invoked delegate, use the the Result property.
/// </returns>
public DispatcherOperationStatus Wait()
{
return Wait(TimeSpan.FromMilliseconds(-1));
}
/// <summary>
/// Waits for this operation to complete.
/// </summary>
/// <param name="timeout">
/// The maximum amount of time to wait.
/// </param>
/// <returns>
/// The status of the operation. To obtain the return value
/// of the invoked delegate, use the the Result property.
/// </returns>
/// <SecurityNote>
/// Critical: This code calls into PushFrame which has a link demand
/// PublicOk: The act of waiting for operation to complete is safe.
/// </SecurityNote>
[SecurityCritical]
public DispatcherOperationStatus Wait(TimeSpan timeout)
{
if((_status == DispatcherOperationStatus.Pending || _status == DispatcherOperationStatus.Executing) &&
timeout.TotalMilliseconds != 0)
{
if(_dispatcher.Thread == Thread.CurrentThread)
{
if(_status == DispatcherOperationStatus.Executing)
{
// We are the dispatching thread, and the current operation state is
// executing, which means that the operation is in the middle of
// executing (on this thread) and is trying to wait for the execution
// to complete. Unfortunately, the thread will now deadlock, so
// we throw an exception instead.
throw new InvalidOperationException(SR.Get(SRID.ThreadMayNotWaitOnOperationsAlreadyExecutingOnTheSameThread));
}
// We are the dispatching thread for this operation, so
// we can't block. We will push a frame instead.
DispatcherOperationFrame frame = new DispatcherOperationFrame(this, timeout);
Dispatcher.PushFrame(frame);
}
else
{
// We are some external thread, so we can just block. Of
// course this means that the Dispatcher (queue)for this
// thread (if any) is now blocked. The COM STA model
// suggests that we should pump certain messages so that
// back-communication can happen. Underneath us, the CLR
// will pump the STA apartment for us, and we will allow
// the UI thread for a context to call
// Invoke(Priority.Max, ...) without going through the
// blocked queue.
DispatcherOperationEvent wait = new DispatcherOperationEvent(this, timeout);
wait.WaitOne();
}
}
if(_useAsyncSemantics)
{
if(_status == DispatcherOperationStatus.Completed ||
_status == DispatcherOperationStatus.Aborted)
{
// We know the operation has completed, so it safe to ask
// the task for the Awaiter, and the awaiter for the result.
// We don't actually care about the result, but this gives the
// Task the chance to throw any captured exceptions.
Task.GetAwaiter().GetResult();
}
}
return _status;
}
/// <summary>
/// Aborts this operation.
/// </summary>
/// <returns>
/// False if the operation could not be aborted (because the
/// operation was already in progress)
/// </returns>
/// <remarks>
/// Aborting an operation will try to remove an operation from the
/// Dispatcher queue so that it is never invoked. If successful,
/// the associated task is also marked as canceled.
/// </remarks>
public bool Abort()
{
bool removed = false;
if (_dispatcher != null)
{
removed = _dispatcher.Abort(this);
if (removed)
{
// Mark the task as canceled so that continuations will be invoked.
_taskSource.SetCanceled();
// Raise the aborted event.
EventHandler aborted = _aborted;
if (aborted != null)
{
aborted(this, EventArgs.Empty);
}
}
}
return removed;
}
/// <summary>
/// Name of this operation.
/// </summary>
/// <returns>
/// Returns a string representation of the operation to be invoked.
/// </returns>
internal String Name
{
get
{
return _method.Method.DeclaringType + "." + _method.Method.Name;
}
}
/// <summary>
/// ID of this operation.
/// </summary>
/// <returns>
/// Returns a "roaming" ID. This ID changes as the object is relocated by the GC.
/// However ETW tools listening for events containing these "roaming" IDs will be
/// able to account for the movements by listening for CLR's GC ETW events, and
/// will therefore be able to track this identity across the lifetime of the object.
/// </returns>
internal long Id
{
[SecurityCritical]
get
{
long addr;
unsafe
{
// we need a non-readonly field of a pointer-compatible type (using _priority)
fixed (DispatcherPriority* pb = &this._priority)
{
addr = (long) pb;
}
}
return addr;
}
}
/// <summary>
/// Returns the result of the operation if it has completed.
/// </summary>
public object Result
{
get
{
if(_useAsyncSemantics)
{
// New semantics require waiting for the operation to
// complete.
//
// Use DispatcherOperation.Wait instead of Task.Wait to handle
// waiting on the same thread.
Wait();
if(_status == DispatcherOperationStatus.Completed ||
_status == DispatcherOperationStatus.Aborted)
{
// We know the operation has completed, and the
// _taskSource has been completed, so it safe to ask
// the task for the Awaiter, and the awaiter for the result.
// We don't actually care about the result, but this gives the
// Task the chance to throw any captured exceptions.
Task.GetAwaiter().GetResult();
}
}
return _result;
}
}
/// <summary>
/// An event that is raised when the operation is aborted or canceled.
/// </summary>
public event EventHandler Aborted
{
add
{
lock (DispatcherLock)
{
_aborted = (EventHandler) Delegate.Combine(_aborted, value);
}
}
remove
{
lock(DispatcherLock)
{
_aborted = (EventHandler) Delegate.Remove(_aborted, value);
}
}
}
/// <summary>
/// An event that is raised when the operation completes.
/// </summary>
/// <remarks>
/// Completed indicates that the operation was invoked and has
/// either completed successfully or faulted. Note that a canceled
/// or aborted operation is never is never considered completed.
/// </remarks>
public event EventHandler Completed
{
add
{
lock (DispatcherLock)
{
_completed = (EventHandler) Delegate.Combine(_completed, value);
}
}
remove
{
lock(DispatcherLock)
{
_completed = (EventHandler) Delegate.Remove(_completed, value);
}
}
}
// Note: this is called by the Dispatcher to actually invoke the operation.
// Invoke --> InvokeInSecurityContext --> InvokeImpl
/// <SecurityNote>
/// Critical: This code calls into ExecutionContext.Run which is link demand protected
/// accesses _executionContext
/// </SecurityNote>
[SecurityCritical]
internal void Invoke()
{
// Mark this operation as executing.
_status = DispatcherOperationStatus.Executing;
// Invoke the operation under the execution context that was
// current when the operation was created.
if(_executionContext != null)
{
CulturePreservingExecutionContext.Run(_executionContext, _invokeInSecurityContext, this);
// Release any resources held by the execution context.
_executionContext.Dispose();
_executionContext = null;
}
else
{
// _executionContext can be null if someone called
// ExecutionContext.SupressFlow before calling BeginInvoke/Invoke.
// In this case we'll just call the invokation directly.
// SupressFlow is a privileged operation, so this is not a
// security hole.
_invokeInSecurityContext(this);
}
EventHandler handler; // either completed or aborted
lock(DispatcherLock)
{
if(_exception != null && _exception is OperationCanceledException)
{
// A new way to abort/cancel an operation is to raise an
// OperationCanceledException exception. This only works
// from the new APIs; the old APIs would flow the exception
// up through the Dispatcher.UnhandledException handling.
//
// Note that programmatically calling
// DispatcherOperation.Abort sets the status and raises the
// Aborted event itself.
handler = _aborted;
_status = DispatcherOperationStatus.Aborted;
}
else
{
// The operation either completed, or a new version threw an
// exception and we caught it. There is no seperate event
// for this, so we raise the same Completed event for both.
handler = _completed;
_status = DispatcherOperationStatus.Completed;
}
}
if(handler != null)
{
handler(this, EventArgs.Empty);
}
}
// Note: this is called by the Dispatcher to actually invoke the completions for the operation.
/// <SecurityNote>
/// Critical: This code invokes the completions for the task associated with an operation.
/// This may cause reentrancy, and effects program correctness, so must be
/// done carefully.
/// </SecurityNote>
[SecurityCritical]
internal void InvokeCompletions()
{
switch(_status)
{
case DispatcherOperationStatus.Aborted:
_taskSource.SetCanceled();
break;
case DispatcherOperationStatus.Completed:
if(_exception != null)
{
_taskSource.SetException(_exception);
}
else
{
_taskSource.SetResult(_result);
}
break;
default:
Invariant.Assert(false, "Operation should be either Aborted or Completed!");
break;
}
}
// Invoke --> InvokeInSecurityContext --> InvokeImpl
/// <SecurityNote>
/// Critical: This code can execute arbitrary code
/// </SecurityNote>
[SecurityCritical]
private static void InvokeInSecurityContext(Object state)
{
DispatcherOperation operation = (DispatcherOperation) state;
operation.InvokeImpl();
}
// Invoke --> InvokeInSecurityContext --> InvokeImpl
/// <SecurityNote>
/// Critical: This code calls into SynchronizationContext.SetSynchronizationContext which link demands
/// </SecurityNote>
[SecurityCritical]
private void InvokeImpl()
{
SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current;
try
{
// We are executing under the "foreign" execution context, but the
// SynchronizationContext must be for the correct dispatcher and
// priority.
DispatcherSynchronizationContext newSynchronizationContext;
if(BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance())
{
newSynchronizationContext = Dispatcher._defaultDispatcherSynchronizationContext;
}
else
{
if(BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority())
{
newSynchronizationContext = new DispatcherSynchronizationContext(_dispatcher, _priority);
}
else
{
newSynchronizationContext = new DispatcherSynchronizationContext(_dispatcher, DispatcherPriority.Normal);
}
}
SynchronizationContext.SetSynchronizationContext(newSynchronizationContext);
// Win32 considers timers to be low priority. Avalon does not, since different timers
// are associated with different priorities. So we promote the timers before we
// invoke any work items.
_dispatcher.PromoteTimers(Environment.TickCount);
if(_useAsyncSemantics)
{
try
{
_result = InvokeDelegateCore();
}
catch(Exception e)
{
// Remember this for the later call to InvokeCompletions.
_exception = e;
}
}
else
{
// Invoke the delegate and route exceptions through the dispatcher events.
_result = _dispatcher.WrappedInvoke(_method, _args, _numArgs, null);
// Note: we do not catch exceptions, they flow out the the Dispatcher.UnhandledException handling.
}
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
protected virtual object InvokeDelegateCore()
{
Action action = (Action) _method;
action();
return null;
}
private class DispatcherOperationFrame : DispatcherFrame
{
// Note: we pass "exitWhenRequested=false" to the base
// DispatcherFrame construsctor because we do not want to exit
// this frame if the dispatcher is shutting down. This is
// because we may need to invoke operations during the shutdown process.
public DispatcherOperationFrame(DispatcherOperation op, TimeSpan timeout) : base(false)
{
_operation = op;
// We will exit this frame once the operation is completed or aborted.
_operation.Aborted += new EventHandler(OnCompletedOrAborted);
_operation.Completed += new EventHandler(OnCompletedOrAborted);
// We will exit the frame if the operation is not completed within
// the requested timeout.
if(timeout.TotalMilliseconds > 0)
{
_waitTimer = new Timer(new TimerCallback(OnTimeout),
null,
timeout,
TimeSpan.FromMilliseconds(-1));
}
// Some other thread could have aborted the operation while we were
// setting up the handlers. We check the state again and mark the
// frame as "should not continue" if this happened.
if(_operation._status != DispatcherOperationStatus.Pending)
{
Exit();
}
}
private void OnCompletedOrAborted(object sender, EventArgs e)
{
Exit();
}
private void OnTimeout(object arg)
{
Exit();
}
private void Exit()
{
Continue = false;
if(_waitTimer != null)
{
_waitTimer.Dispose();
}
_operation.Aborted -= new EventHandler(OnCompletedOrAborted);
_operation.Completed -= new EventHandler(OnCompletedOrAborted);
}
private DispatcherOperation _operation;
private Timer _waitTimer;
}
private class DispatcherOperationEvent
{
public DispatcherOperationEvent(DispatcherOperation op, TimeSpan timeout)
{
_operation = op;
_timeout = timeout;
_event = new ManualResetEvent(false);
_eventClosed = false;
lock(DispatcherLock)
{
// We will set our event once the operation is completed or aborted.
_operation.Aborted += new EventHandler(OnCompletedOrAborted);
_operation.Completed += new EventHandler(OnCompletedOrAborted);
// Since some other thread is dispatching this operation, it could
// have been dispatched while we were setting up the handlers.
// We check the state again and set the event ourselves if this
// happened.
if(_operation._status != DispatcherOperationStatus.Pending && _operation._status != DispatcherOperationStatus.Executing)
{
_event.Set();
}
}
}
private void OnCompletedOrAborted(object sender, EventArgs e)
{
lock(DispatcherLock)
{
if(!_eventClosed)
{
_event.Set();
}
}
}
public void WaitOne()
{
_event.WaitOne(_timeout, false);
lock(DispatcherLock)
{
if(!_eventClosed)
{
// Cleanup the events.
_operation.Aborted -= new EventHandler(OnCompletedOrAborted);
_operation.Completed -= new EventHandler(OnCompletedOrAborted);
// Close the event immediately instead of waiting for a GC
// because the Dispatcher is a a high-activity component and
// we could run out of events.
_event.Close();
_eventClosed = true;
}
}
}
private object DispatcherLock
{
get { return _operation.DispatcherLock; }
}
private DispatcherOperation _operation;
private TimeSpan _timeout;
private ManualResetEvent _event;
private bool _eventClosed;
}
private object DispatcherLock
{
get { return _dispatcher._instanceLock; }
}
/// <SecurityNote>
/// Obtained under an elevation.
/// </SecurityNote>
[SecurityCritical]
private CulturePreservingExecutionContext _executionContext;
private static readonly ContextCallback _invokeInSecurityContext;
private readonly Dispatcher _dispatcher;
private DispatcherPriority _priority;
internal readonly Delegate _method;
private readonly object _args;
private readonly int _numArgs;
internal DispatcherOperationStatus _status; // set from Dispatcher
private object _result;
private Exception _exception;
internal PriorityItem<DispatcherOperation> _item; // The Dispatcher sets this when it enques/deques the item.
EventHandler _aborted;
EventHandler _completed;
internal readonly DispatcherOperationTaskSource _taskSource; // also used from Dispatcher
private readonly bool _useAsyncSemantics;
}
/// <summary>
/// DispatcherOperation represents a delegate that has been
/// posted to the Dispatcher queue.
/// </summary>
public class DispatcherOperation<TResult> : DispatcherOperation
{
/// <SecurityNote>
/// Critical: calls critical constructor
/// </SecurityNote>
[SecurityCritical]
internal DispatcherOperation(
Dispatcher dispatcher,
DispatcherPriority priority,
Func<TResult> func) : base(
dispatcher,
func,
priority,
null,
0,
new DispatcherOperationTaskSource<TResult>(),
true)
{
}
/// <summary>
/// Returns a Task representing the operation.
/// </summary>
public new Task<TResult> Task
{
get
{
// Just upcast the base Task to what it really is.
return (Task<TResult>)((DispatcherOperation)this).Task;
}
}
/// <summary>
/// Returns an awaiter for awaiting the completion of the operation.
/// </summary>
/// <remarks>
/// This method is intended to be used by compilers.
/// </remarks>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new TaskAwaiter<TResult> GetAwaiter()
{
return Task.GetAwaiter();
}
/// <summary>
/// Returns the result of the operation if it has completed.
/// </summary>
public new TResult Result
{
get
{
return (TResult) ((DispatcherOperation)this).Result;
}
}
protected override object InvokeDelegateCore()
{
Func<TResult> func = (Func<TResult>) _method;
return func();
}
}
/// <summary>
/// A convenient delegate to use for dispatcher operations.
/// </summary>
public delegate object DispatcherOperationCallback(object arg);
}
|