// // Copyright (c) Microsoft Corporation. All rights reserved. // namespace System.Threading { using System.Runtime.CompilerServices; using System.Runtime.InteropServices; /// /// 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. /// public sealed class Monitor { /// /// Private so that only we can create instances. /// private Monitor() { this.Mutex = new Mutex(); this.WaitEvent = new AutoResetEvent(false); this.Depth = 0; } /// /// Attempt to enter the monitor, blocking until it is held. /// public static void Enter(Object obj) { VTable.DebugBreak(); Monitor monitor = GetMonitorFromSyncBlock(obj); monitor.Enter(); } /// /// Exit the monitor. /// public static void Exit(Object obj) { Monitor monitor = GetMonitorFromSyncBlock(obj); monitor.Exit(); } /// /// Wake up a thread waiting on the monitor. /// public static void Pulse(Object obj) { Monitor monitor = GetMonitorFromSyncBlock(obj); monitor.Pulse(); } /// /// Wake up all threads waiting on the monitor. /// public static void PulseAll(Object obj) { Monitor monitor = GetMonitorFromSyncBlock(obj); monitor.PulseAll(); } /// /// Attempt to enter the monitor, returning immediately if it is /// already held by another thread. /// public static bool TryEnter(Object obj) { Monitor monitor = GetMonitorFromSyncBlock(obj); return monitor.TryEnter(); } /// /// Attempt to enter the monitor, returning if it can not be taken /// within the specified timeout. /// public static bool TryEnter(Object obj, int millisecondTimeout) { Monitor monitor = GetMonitorFromSyncBlock(obj); return monitor.TryEnter(millisecondTimeout); } /// /// Attempt to enter the monitor, returning if it can not be taken /// within the specified timeout. /// public static bool TryEnter(Object obj, TimeSpan timeout) { Monitor monitor = GetMonitorFromSyncBlock(obj); return monitor.TryEnter(GetMillisecondTimeout(timeout)); } /// /// Wait to be woken up by a holder of the monitor. /// public static bool Wait(Object obj) { Monitor monitor = GetMonitorFromSyncBlock(obj); return monitor.Wait(Timeout.Infinite); } /// /// Wait to be woken up by a holder of the monitor. Give up after /// a specified timeout. /// public static bool Wait(Object obj, int millisecondsTimeout) { Monitor monitor = GetMonitorFromSyncBlock(obj); return monitor.Wait(millisecondsTimeout); } /// /// Wait to be woken up by a holder of the monitor. Give up after /// a specified timeout. /// public static bool Wait(Object obj, TimeSpan timeout) { Monitor monitor = GetMonitorFromSyncBlock(obj); return monitor.Wait(GetMillisecondTimeout(timeout)); } /// /// 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. /// 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); } /// /// 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. /// 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)); } /// /// Extract Milliseconds from a TimeSpan /// [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; } /// /// Enter the monitor, blocking until it is held. /// internal void Enter() { TryEnter(Timeout.Infinite); } /// /// Exit the monitor. /// 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(); } } /// /// Wake up a single thread waiting on the monitor. /// internal void Pulse() { Thread currentThread = Thread.CurrentThread; if (this.Mutex.GetBeneficiary() != currentThread) { throw new SynchronizationLockException("Monitor not held on Pulse"); } this.WaitEvent.NotifyOne(); } /// /// Wake up all threads waiting on the monitor. /// internal void PulseAll() { Thread currentThread = Thread.CurrentThread; if (this.Mutex.GetBeneficiary() != currentThread) { throw new SynchronizationLockException("Monitor not held on PulseAll"); } this.WaitEvent.NotifyAll(); } /// /// Try to enter the monitor, returning immediately if it is /// already held. /// internal bool TryEnter() { return TryEnter(0); } /// /// Try to enter the monitor, giving up if it cannot be /// entered after a timeout. /// 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); } /// /// Wait within the monitor for a Pulse. /// 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; } /// /// Ensure that the passed object has a monitor (and associated /// SyncBlock) allocated. /// internal static void CreateMonitor(Object obj) { GetMonitorFromSyncBlock(obj); } /// /// Look up the Monitor for the specified object in the SyncBlock /// tables. If no Monitor exists for the object then one is created. /// 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) { monitor = new Monitor(); table[index].Monitor = monitor; } return monitor; } else { return table[index].Monitor; } } /// /// The recursion depth of the current holder of the monitor. /// internal int Depth; /// /// The mutex that is held by the thread that holds the monitor /// internal Mutex Mutex; /// /// The event that is used for threads waiting for Pulses /// internal AutoResetEvent WaitEvent; } }