284 lines
9.1 KiB
C#
284 lines
9.1 KiB
C#
|
//----------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//----------------------------------------------------------------
|
||
|
namespace System.Runtime
|
||
|
{
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Security;
|
||
|
using System.Threading;
|
||
|
|
||
|
[Fx.Tag.SynchronizationPrimitive(Fx.Tag.BlocksUsing.MonitorWait, SupportsAsync = true, ReleaseMethod = "Set")]
|
||
|
class AsyncWaitHandle
|
||
|
{
|
||
|
static Action<object> timerCompleteCallback;
|
||
|
|
||
|
List<AsyncWaiter> asyncWaiters;
|
||
|
bool isSignaled;
|
||
|
EventResetMode resetMode;
|
||
|
|
||
|
[Fx.Tag.SynchronizationObject(Kind = Fx.Tag.SynchronizationKind.MonitorWait)]
|
||
|
object syncObject;
|
||
|
|
||
|
int syncWaiterCount;
|
||
|
|
||
|
public AsyncWaitHandle()
|
||
|
: this(EventResetMode.AutoReset)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public AsyncWaitHandle(EventResetMode resetMode)
|
||
|
{
|
||
|
this.resetMode = resetMode;
|
||
|
this.syncObject = new object();
|
||
|
}
|
||
|
|
||
|
public bool WaitAsync(Action<object, TimeoutException> callback, object state, TimeSpan timeout)
|
||
|
{
|
||
|
if (!this.isSignaled || (this.isSignaled && this.resetMode == EventResetMode.AutoReset))
|
||
|
{
|
||
|
lock (syncObject)
|
||
|
{
|
||
|
if (this.isSignaled && this.resetMode == EventResetMode.AutoReset)
|
||
|
{
|
||
|
this.isSignaled = false;
|
||
|
}
|
||
|
else if (!this.isSignaled)
|
||
|
{
|
||
|
AsyncWaiter waiter = new AsyncWaiter(this, callback, state);
|
||
|
|
||
|
if (this.asyncWaiters == null)
|
||
|
{
|
||
|
this.asyncWaiters = new List<AsyncWaiter>();
|
||
|
}
|
||
|
|
||
|
this.asyncWaiters.Add(waiter);
|
||
|
|
||
|
if (timeout != TimeSpan.MaxValue)
|
||
|
{
|
||
|
if (timerCompleteCallback == null)
|
||
|
{
|
||
|
timerCompleteCallback = new Action<object>(OnTimerComplete);
|
||
|
}
|
||
|
waiter.SetTimer(timerCompleteCallback, waiter, timeout);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void OnTimerComplete(object state)
|
||
|
{
|
||
|
AsyncWaiter waiter = (AsyncWaiter)state;
|
||
|
AsyncWaitHandle thisPtr = waiter.Parent;
|
||
|
bool callWaiter = false;
|
||
|
|
||
|
lock (thisPtr.syncObject)
|
||
|
{
|
||
|
// If still in the waiting list (that means it hasn't been signaled)
|
||
|
if (thisPtr.asyncWaiters != null && thisPtr.asyncWaiters.Remove(waiter))
|
||
|
{
|
||
|
waiter.TimedOut = true;
|
||
|
callWaiter = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
waiter.CancelTimer();
|
||
|
|
||
|
if (callWaiter)
|
||
|
{
|
||
|
waiter.Call();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Fx.Tag.Blocking]
|
||
|
public bool Wait(TimeSpan timeout)
|
||
|
{
|
||
|
if (!this.isSignaled || (this.isSignaled && this.resetMode == EventResetMode.AutoReset))
|
||
|
{
|
||
|
lock (syncObject)
|
||
|
{
|
||
|
if (this.isSignaled && this.resetMode == EventResetMode.AutoReset)
|
||
|
{
|
||
|
this.isSignaled = false;
|
||
|
}
|
||
|
else if (!this.isSignaled)
|
||
|
{
|
||
|
bool decrementRequired = false;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
this.syncWaiterCount++;
|
||
|
decrementRequired = true;
|
||
|
}
|
||
|
|
||
|
if (timeout == TimeSpan.MaxValue)
|
||
|
{
|
||
|
if (!Monitor.Wait(syncObject, Timeout.Infinite))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!Monitor.Wait(syncObject, timeout))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (decrementRequired)
|
||
|
{
|
||
|
this.syncWaiterCount--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public void Set()
|
||
|
{
|
||
|
List<AsyncWaiter> toCallList = null;
|
||
|
AsyncWaiter toCall = null;
|
||
|
|
||
|
if (!this.isSignaled)
|
||
|
{
|
||
|
lock (syncObject)
|
||
|
{
|
||
|
if (!this.isSignaled)
|
||
|
{
|
||
|
if (this.resetMode == EventResetMode.ManualReset)
|
||
|
{
|
||
|
this.isSignaled = true;
|
||
|
Monitor.PulseAll(syncObject);
|
||
|
toCallList = this.asyncWaiters;
|
||
|
this.asyncWaiters = null;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (this.syncWaiterCount > 0)
|
||
|
{
|
||
|
Monitor.Pulse(syncObject);
|
||
|
}
|
||
|
else if (this.asyncWaiters != null && this.asyncWaiters.Count > 0)
|
||
|
{
|
||
|
toCall = this.asyncWaiters[0];
|
||
|
this.asyncWaiters.RemoveAt(0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.isSignaled = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (toCallList != null)
|
||
|
{
|
||
|
foreach (AsyncWaiter waiter in toCallList)
|
||
|
{
|
||
|
waiter.CancelTimer();
|
||
|
waiter.Call();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (toCall != null)
|
||
|
{
|
||
|
toCall.CancelTimer();
|
||
|
toCall.Call();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Reset()
|
||
|
{
|
||
|
// Doesn't matter if this changes during processing of another method
|
||
|
this.isSignaled = false;
|
||
|
}
|
||
|
|
||
|
class AsyncWaiter : ActionItem
|
||
|
{
|
||
|
[Fx.Tag.SecurityNote(Critical = "Store the delegate to be invoked")]
|
||
|
[SecurityCritical]
|
||
|
Action<object, TimeoutException> callback;
|
||
|
[Fx.Tag.SecurityNote(Critical = "Stores the state object to be passed to the callback")]
|
||
|
[SecurityCritical]
|
||
|
object state;
|
||
|
IOThreadTimer timer;
|
||
|
TimeSpan originalTimeout;
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Access critical members", Safe = "Doesn't leak information")]
|
||
|
[SecuritySafeCritical]
|
||
|
public AsyncWaiter(AsyncWaitHandle parent, Action<object, TimeoutException> callback, object state)
|
||
|
{
|
||
|
this.Parent = parent;
|
||
|
this.callback = callback;
|
||
|
this.state = state;
|
||
|
}
|
||
|
|
||
|
public AsyncWaitHandle Parent
|
||
|
{
|
||
|
get;
|
||
|
private set;
|
||
|
}
|
||
|
|
||
|
public bool TimedOut
|
||
|
{
|
||
|
get;
|
||
|
set;
|
||
|
}
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Calls into critical method Schedule", Safe = "Invokes the given delegate under the current context")]
|
||
|
[SecuritySafeCritical]
|
||
|
public void Call()
|
||
|
{
|
||
|
Schedule();
|
||
|
}
|
||
|
|
||
|
[Fx.Tag.SecurityNote(Critical = "Overriding an inherited critical method, access critical members")]
|
||
|
[SecurityCritical]
|
||
|
protected override void Invoke()
|
||
|
{
|
||
|
this.callback(this.state,
|
||
|
this.TimedOut ? new TimeoutException(InternalSR.TimeoutOnOperation(this.originalTimeout)) : null);
|
||
|
}
|
||
|
|
||
|
public void SetTimer(Action<object> callback, object state, TimeSpan timeout)
|
||
|
{
|
||
|
if (this.timer != null)
|
||
|
{
|
||
|
throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.MustCancelOldTimer));
|
||
|
}
|
||
|
|
||
|
this.originalTimeout = timeout;
|
||
|
this.timer = new IOThreadTimer(callback, state, false);
|
||
|
|
||
|
this.timer.Set(timeout);
|
||
|
}
|
||
|
|
||
|
public void CancelTimer()
|
||
|
{
|
||
|
if (this.timer != null)
|
||
|
{
|
||
|
this.timer.Cancel();
|
||
|
this.timer = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|