// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== using System; using System.Runtime.CompilerServices; using Microsoft.Singularity; using Microsoft.Singularity.Scheduling; namespace System.Threading { /// /// /// Base class for all interrupt aware synchronization objects /// /// This class is interrupt aware: interrupts are disabled during the period when the /// spin lock is held. /// /// [NoCCtor] [CLSCompliant(false)] public abstract class InterruptAwareWaitHandle : WaitHandleBase { /// /// /// Constructor /// /// /// Initial state of an handle /// /// Value represents a state of a handle when wait satisfied right a way /// /// The spin lock type of the wait handle /// protected InterruptAwareWaitHandle( SignalState initialState, SignalState signalStateAfterImediateWait, SpinLock.Types spinLockType) : base(initialState, signalStateAfterImediateWait) { this.singleHandle = new InterruptAwareWaitHandle[1] { this }; // Initialize waithandle spinlock this.myLock = new SpinLock(spinLockType); } /// /// /// Signal wait handle by waking up a single waiter or if there are no waiters /// set handle to specified stated. /// /// /// /// Set the wait handle into specified state if no waiters present /// /// [NoHeapAllocation] protected void InterruptAwareSignalOne(SignalState signalStateIfNoWaiters) { // Disable interrupt bool interruptFlag = Processor.DisableInterrupts(); // Single a waiting thread and put it in the deferredWakeupQueue ThreadQueueStruct deferredWakeupQueue = SignalOneWithNoWakeup(signalStateIfNoWaiters); // Restore interrupt if necessary Processor.RestoreInterrupts(interruptFlag); // Wakeup a waiter if we need to WakeupOneWaiter(deferredWakeupQueue); } /// /// /// Associate a thread with wait handles if one of the waits satisfied return /// waithandle id - actual unblocker. if none of the states satisfied return UninitWait /// indicating that thread has to proceede with blocking /// /// private static int InterruptAwarePreWaitAnyInternal( Thread currentThread, WaitHandleBase[] waitHandles, ThreadEntry[] entries, int waitHandlesCount) { // Disable interrupt bool interruptFlag = Processor.DisableInterrupts(); int unblockedBy = PreWaitAnyInternal( currentThread, waitHandles, entries, waitHandlesCount); // Restore interrupt Processor.RestoreInterrupts(interruptFlag); return unblockedBy; } /// /// /// Post wait is to disassociate thread from all handlers /// /// protected static void InterruptAwarePostWaitAnyInternal( Thread currentThread, WaitHandleBase[] waitHandles, ThreadEntry[] entries, int waitHandlesCount) { // Disable interrupt bool interruptFlag = Processor.DisableInterrupts(); PostWaitAnyInternal(currentThread, waitHandles, entries, waitHandlesCount); // Restore interrupt Processor.RestoreInterrupts(interruptFlag); } /// /// /// Wait on a set of handles until one of them becomes signaled with a specified time out. /// /// !!! If you change this method, please review WaitHandle.WaitAny() /// and see if the changes need to be propagated there. /// /// public void InterruptAwareWaitOne() { // Retrieve current thread information Thread currentThread = Thread.CurrentThread; Thread target = null; int unblockedBy; ThreadEntry[] entries = currentThread.GetWaitEntries(1); // Before we attempting to enqueue ourselves into the wait queues make sure // we disable abort currentThread.DelayStop(true); // Perepare for a wait - enqueue ourselves into every wait handle unblockedBy = InterruptAwarePreWaitAnyInternal(currentThread, singleHandle, entries, 1); // If we are in the process of blocking: Block if (UninitWait == unblockedBy) { // Allow thread to be aborted at this point currentThread.DelayStop(false); // Write out log record Monitoring.Log(Monitoring.Provider.Thread, (ushort)ThreadEvent.WaitAny, 0, 0, 0, (uint)currentThread.threadIndex, 0, 0); // Let scheduler know that we are blocking Kernel.TheScheduler.OnThreadBlocked(currentThread, SchedulerTime.MaxValue); // Our thread is about to run so we can disassociate it from wait handles InterruptAwarePostWaitAnyInternal(currentThread, singleHandle, entries, 1); // Thread has the unblocked information unblockedBy = currentThread.UnblockedBy; } // Assert post condition: since there is no timeout, and we are waiting // on a single handle, the unblockedBy must be 0 VTable.Assert(unblockedBy == 0); // Complete wait CompleteWait(currentThread); // When we were signalged delay abort has been set - now we can turn it off // For mutex complete wait will add delay abort. It will remain on until // mutex is released currentThread.DelayStop(false); // Make sure that we pay attention to abort currentThread.ProcessAbortIfRequired(); } /// /// This field is an array of length 1 containing 'this'. /// It is used to avoid allocation when calling WaitAny from WaitOne. /// private InterruptAwareWaitHandle[] singleHandle; } }