singrdk/base/Kernel/System/Threading/InterruptAwareWaitHandle.cs

189 lines
6.8 KiB
C#
Raw Normal View History

2008-11-17 18:29:00 -05:00
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Runtime.CompilerServices;
using Microsoft.Singularity;
using Microsoft.Singularity.Scheduling;
namespace System.Threading
{
///
/// <summary>
/// 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.
/// </summary>
///
[NoCCtor]
[CLSCompliant(false)]
public abstract class InterruptAwareWaitHandle : WaitHandleBase
{
///
/// <summary>
/// Constructor
/// </summary>
///
/// <param name="initialState">Initial state of an handle </param>
/// <param name="signalStateAfterImediateWait">
/// Value represents a state of a handle when wait satisfied right a way
/// </param>
/// <param name="spinLockType">The spin lock type of the wait handle</param>
///
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);
}
///
/// <summary>
/// Signal wait handle by waking up a single waiter or if there are no waiters
/// set handle to specified stated.
/// </summary>
///
/// <param name="signalStateIfNoWaiters">
/// Set the wait handle into specified state if no waiters present
/// </param>
///
[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);
}
///
/// <summary>
/// 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
/// </summary>
///
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;
}
///
/// <summary>
/// Post wait is to disassociate thread from all handlers
/// </summary>
///
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);
}
///
/// <summary>
/// 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.
/// </summary>
///
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();
}
/// <summary>
/// This field is an array of length 1 containing 'this'.
/// It is used to avoid allocation when calling WaitAny from WaitOne.
/// </summary>
private InterruptAwareWaitHandle[] singleHandle;
}
}