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

296 lines
10 KiB
C#

//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
namespace System.Threading
{
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// A monitor is used for synchronization. Only a single thread can
/// hold the monitor at any given time.
///
/// The monitor maintains two lists of threads: one for threads waiting
/// to enter the monitor, and one for threads waiting for a pulse within
/// the monitor.
/// </summary>
public sealed class Monitor {
/// <summary>
/// Private so that only we can create instances.
/// </summary>
private Monitor() {
this.Mutex = new Mutex();
this.WaitEvent = new AutoResetEvent(false);
this.Depth = 0;
}
/// <summary>
/// Attempt to enter the monitor, blocking until it is held.
/// </summary>
public static void Enter(Object obj) {
VTable.DebugBreak();
Monitor monitor = GetMonitorFromSyncBlock(obj);
monitor.Enter();
}
/// <summary>
/// Exit the monitor.
/// </summary>
public static void Exit(Object obj) {
Monitor monitor = GetMonitorFromSyncBlock(obj);
monitor.Exit();
}
/// <summary>
/// Wake up a thread waiting on the monitor.
/// </summary>
public static void Pulse(Object obj) {
Monitor monitor = GetMonitorFromSyncBlock(obj);
monitor.Pulse();
}
/// <summary>
/// Wake up all threads waiting on the monitor.
/// </summary>
public static void PulseAll(Object obj) {
Monitor monitor = GetMonitorFromSyncBlock(obj);
monitor.PulseAll();
}
/// <summary>
/// Attempt to enter the monitor, returning immediately if it is
/// already held by another thread.
/// </summary>
public static bool TryEnter(Object obj) {
Monitor monitor = GetMonitorFromSyncBlock(obj);
return monitor.TryEnter();
}
/// <summary>
/// Attempt to enter the monitor, returning if it can not be taken
/// within the specified timeout.
/// </summary>
public static bool TryEnter(Object obj, int millisecondTimeout) {
Monitor monitor = GetMonitorFromSyncBlock(obj);
return monitor.TryEnter(millisecondTimeout);
}
/// <summary>
/// Attempt to enter the monitor, returning if it can not be taken
/// within the specified timeout.
/// </summary>
public static bool TryEnter(Object obj, TimeSpan timeout) {
Monitor monitor = GetMonitorFromSyncBlock(obj);
return monitor.TryEnter(GetMillisecondTimeout(timeout));
}
/// <summary>
/// Wait to be woken up by a holder of the monitor.
/// </summary>
public static bool Wait(Object obj) {
Monitor monitor = GetMonitorFromSyncBlock(obj);
return monitor.Wait(Timeout.Infinite);
}
/// <summary>
/// Wait to be woken up by a holder of the monitor. Give up after
/// a specified timeout.
/// </summary>
public static bool Wait(Object obj, int millisecondsTimeout) {
Monitor monitor = GetMonitorFromSyncBlock(obj);
return monitor.Wait(millisecondsTimeout);
}
/// <summary>
/// Wait to be woken up by a holder of the monitor. Give up after
/// a specified timeout.
/// </summary>
public static bool Wait(Object obj, TimeSpan timeout) {
Monitor monitor = GetMonitorFromSyncBlock(obj);
return monitor.Wait(GetMillisecondTimeout(timeout));
}
/// <summary>
/// Wait to be woken up by a holder of the monitor. Give up after
/// a specified timeout.
///
/// Overload exists to match the CLR. Exit Context not supported.
/// </summary>
public static bool Wait(Object obj,
int millisecondsTimeout,
bool exitContext) {
if (exitContext)
throw new NotSupportedException("exitContext not supported!");
Monitor monitor = GetMonitorFromSyncBlock(obj);
return monitor.Wait(millisecondsTimeout);
}
/// <summary>
/// Wait to be woken up by a holder of the monitor. Give up after
/// a specified timeout.
///
/// Overload exists to match the CLR. Exit Context not supported.
/// </summary>
public static bool Wait(Object obj,
TimeSpan timeout,
bool exitContext) {
if (exitContext)
throw new NotSupportedException("exitContext not supported!");
Monitor monitor = GetMonitorFromSyncBlock(obj);
return monitor.Wait(GetMillisecondTimeout(timeout));
}
/// <summary>
/// Extract Milliseconds from a TimeSpan
/// </summary>
[Inline]
private static int GetMillisecondTimeout(TimeSpan timeout) {
int tm = (int)timeout.TotalMilliseconds;
if (tm < - 1 || tm > Int32.MaxValue) {
throw new ArgumentOutOfRangeException("timeout",
"ArgumentOutOfRange_NeedNonNegOrNegative1");
}
return tm;
}
/// <summary>
/// Enter the monitor, blocking until it is held.
/// </summary>
internal void Enter() {
TryEnter(Timeout.Infinite);
}
/// <summary>
/// Exit the monitor.
/// </summary>
internal void Exit() {
Thread currentThread = Thread.CurrentThread;
if (this.Mutex.GetBeneficiary() != currentThread) {
throw new SynchronizationLockException("Monitor not held on Exit");
}
this.Depth--;
if (this.Depth == 0) {
this.Mutex.ReleaseMutex();
}
}
/// <summary>
/// Wake up a single thread waiting on the monitor.
/// </summary>
internal void Pulse() {
Thread currentThread = Thread.CurrentThread;
if (this.Mutex.GetBeneficiary() != currentThread) {
throw new SynchronizationLockException("Monitor not held on Pulse");
}
this.WaitEvent.NotifyOne();
}
/// <summary>
/// Wake up all threads waiting on the monitor.
/// </summary>
internal void PulseAll() {
Thread currentThread = Thread.CurrentThread;
if (this.Mutex.GetBeneficiary() != currentThread) {
throw new SynchronizationLockException("Monitor not held on PulseAll");
}
this.WaitEvent.NotifyAll();
}
/// <summary>
/// Try to enter the monitor, returning immediately if it is
/// already held.
/// </summary>
internal bool TryEnter() {
return TryEnter(0);
}
/// <summary>
/// Try to enter the monitor, giving up if it cannot be
/// entered after a timeout.
/// </summary>
internal bool TryEnter(int timeout) {
Thread currentThread = Thread.CurrentThread;
if (this.Mutex.GetBeneficiary() == currentThread) {
this.Depth++;
return true;
}
this.Depth = 0;
return Mutex.WaitOne(timeout);
}
/// <summary>
/// Wait within the monitor for a Pulse.
/// </summary>
internal bool Wait(int timeout) {
Thread currentThread = Thread.CurrentThread;
if (this.Mutex.GetBeneficiary() != currentThread) {
throw new SynchronizationLockException("Monitor not held on Wait");
}
int rememberedDepth = this.Depth;
this.Mutex.ReleaseMutex();
bool waitSuccess = this.WaitEvent.WaitOne();
this.Mutex.WaitOne();
this.Depth = rememberedDepth;
return waitSuccess;
}
/// <summary>
/// Ensure that the passed object has a monitor (and associated
/// SyncBlock) allocated.
/// </summary>
internal static void CreateMonitor(Object obj) {
GetMonitorFromSyncBlock(obj);
}
/// <summary>
/// Look up the Monitor for the specified object in the SyncBlock
/// tables. If no Monitor exists for the object then one is created.
/// </summary>
private static Monitor GetMonitorFromSyncBlock(Object obj) {
if (obj == null) {
throw new ArgumentNullException();
}
SyncBlock[] table;
int index = SyncBlock.GetSyncBlockIndex(obj, out table);
if (index < 0 || table[index].Monitor == null) {
if (index < 0) {
index = SyncBlock.LazyCreateSyncBlock(obj, out table);
}
Monitor monitor = table[index].Monitor;
if (monitor == null) {
Interlocked.CompareExchange(ref table[index].Monitor, new Monitor(), null);
monitor = table[index].Monitor;
}
return monitor;
}
else {
return table[index].Monitor;
}
}
/// <summary>
/// The recursion depth of the current holder of the monitor.
/// </summary>
internal int Depth;
/// <summary>
/// The mutex that is held by the thread that holds the monitor
/// </summary>
internal Mutex Mutex;
/// <summary>
/// The event that is used for threads waiting for Pulses
/// </summary>
internal AutoResetEvent WaitEvent;
}
}