You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			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;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| }
 |