File: src\System\Threading\Monitor.CoreCLR.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
//
/*=============================================================================
**
**
**
** Purpose: Synchronizes access to a shared resource or region of code in a multi-threaded
**             program.
**
**
=============================================================================*/
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
 
namespace System.Threading
{
    public static partial class Monitor
    {
        /*=========================================================================
        ** Obtain the monitor lock of obj. Will block if another thread holds the lock
        ** Will not block if the current thread holds the lock,
        ** however the caller must ensure that the same number of Exit
        ** calls are made as there were Enter calls.
        **
        ** Exceptions: ArgumentNullException if object is null.
        =========================================================================*/
        public static void Enter(object obj)
        {
            ArgumentNullException.ThrowIfNull(obj, null);
 
            if (!TryEnter_FastPath(obj))
            {
                Enter_Slowpath(obj);
            }
        }
 
        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern bool TryEnter_FastPath(object obj);
 
        // These must match the values in syncblk.h
        private enum EnterHelperResult
        {
            Contention = 0,
            Entered = 1,
            UseSlowPath = 2
        }
 
        // These must match the values in syncblk.h
        private enum LeaveHelperAction
        {
            None = 0,
            Signal = 1,
            Yield = 2,
            Contention = 3,
            Error = 4,
        };
 
        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern EnterHelperResult TryEnter_FastPath_WithTimeout(object obj, int timeout);
 
        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Monitor_Enter_Slowpath")]
        private static partial void Enter_Slowpath(ObjectHandleOnStack obj);
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void Enter_Slowpath(object obj)
        {
            Enter_Slowpath(ObjectHandleOnStack.Create(ref obj));
        }
 
        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Monitor_TryEnter_Slowpath")]
        private static partial int TryEnter_Slowpath(ObjectHandleOnStack obj, int timeout);
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static bool TryEnter_Slowpath(object obj)
        {
            if (TryEnter_Slowpath(ObjectHandleOnStack.Create(ref obj), 0) != 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static bool TryEnter_Slowpath(object obj, int timeout)
        {
            if (TryEnter_Slowpath(ObjectHandleOnStack.Create(ref obj), timeout) != 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
        // Use a ref bool instead of out to ensure that unverifiable code must
        // initialize this value to something.  If we used out, the value
        // could be uninitialized if we threw an exception in our prolog.
        // The JIT should inline this method to allow check of lockTaken argument to be optimized out
        // in the typical case. Note that the method has to be transparent for inlining to be allowed by the VM.
        public static void Enter(object obj, ref bool lockTaken)
        {
            if (lockTaken)
                ThrowLockTakenException();
 
            ArgumentNullException.ThrowIfNull(obj, null);
 
            if (!TryEnter_FastPath(obj))
            {
                Enter_Slowpath(obj);
            }
            lockTaken = true;
            Debug.Assert(lockTaken);
        }
 
        [DoesNotReturn]
        private static void ThrowLockTakenException()
        {
            throw new ArgumentException(SR.Argument_MustBeFalse, "lockTaken");
        }
 
        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern LeaveHelperAction Exit_FastPath(object obj);
 
        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Monitor_Exit_Slowpath")]
        private static partial void Exit_Slowpath(ObjectHandleOnStack obj, LeaveHelperAction exitBehavior);
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void Exit_Slowpath(LeaveHelperAction exitBehavior, object obj)
        {
            Exit_Slowpath(ObjectHandleOnStack.Create(ref obj), exitBehavior);
        }
 
        /*=========================================================================
        ** Release the monitor lock. If one or more threads are waiting to acquire the
        ** lock, and the current thread has executed as many Exits as
        ** Enters, one of the threads will be unblocked and allowed to proceed.
        **
        ** Exceptions: ArgumentNullException if object is null.
        **             SynchronizationLockException if the current thread does not
        **             own the lock.
        =========================================================================*/
        public static void Exit(object obj)
        {
            ArgumentNullException.ThrowIfNull(obj, null);
 
            LeaveHelperAction exitBehavior = Exit_FastPath(obj);
 
            if (exitBehavior == LeaveHelperAction.None)
                return;
 
            Exit_Slowpath(exitBehavior, obj);
        }
 
        // Used to implement synchronized methods on non Windows-X86 architectures
        internal static void ExitIfLockTaken(object obj, ref bool lockTaken)
        {
            ArgumentNullException.ThrowIfNull(obj, null);
 
            if (lockTaken)
            {
                LeaveHelperAction exitBehavior = Exit_FastPath(obj);
 
                if (exitBehavior == LeaveHelperAction.None)
                {
                    lockTaken = false;
                    return;
                }
 
                Exit_Slowpath(exitBehavior, obj);
                lockTaken = false;
            }
        }
 
        /*=========================================================================
        ** Similar to Enter, but will never block. That is, if the current thread can
        ** acquire the monitor lock without blocking, it will do so and TRUE will
        ** be returned. Otherwise FALSE will be returned.
        **
        ** Exceptions: ArgumentNullException if object is null.
        =========================================================================*/
        public static bool TryEnter(object obj)
        {
            ArgumentNullException.ThrowIfNull(obj, null);
 
            EnterHelperResult tryEnterResult = TryEnter_FastPath_WithTimeout(obj, 0);
            if (tryEnterResult == EnterHelperResult.Entered)
            {
                return true;
            }
            else if (tryEnterResult == EnterHelperResult.Contention)
            {
                return false;
            }
 
            return TryEnter_Slowpath(obj);
        }
 
        private static void TryEnter_Timeout_WithLockTaken(object obj, int millisecondsTimeout, ref bool lockTaken)
        {
            if (millisecondsTimeout >= -1)
            {
                EnterHelperResult tryEnterResult = TryEnter_FastPath_WithTimeout(obj, millisecondsTimeout);
                if (tryEnterResult == EnterHelperResult.Entered)
                {
                    lockTaken = true;
                    return;
                }
                else if (millisecondsTimeout == 0 && (tryEnterResult == EnterHelperResult.Contention))
                {
                    return;
                }
            }
 
            if (TryEnter_Slowpath(obj, millisecondsTimeout))
            {
                lockTaken = true;
            }
        }
 
        // The JIT should inline this method to allow check of lockTaken argument to be optimized out
        // in the typical case. Note that the method has to be transparent for inlining to be allowed by the VM.
        public static void TryEnter(object obj, ref bool lockTaken)
        {
            if (lockTaken)
                ThrowLockTakenException();
 
            ArgumentNullException.ThrowIfNull(obj, null);
 
            TryEnter_Timeout_WithLockTaken(obj, 0, ref lockTaken);
        }
 
        /*=========================================================================
        ** Version of TryEnter that will block, but only up to a timeout period
        ** expressed in milliseconds. If timeout == Timeout.Infinite the method
        ** becomes equivalent to Enter.
        **
        ** Exceptions: ArgumentNullException if object is null.
        **             ArgumentException if timeout < -1 (Timeout.Infinite).
        =========================================================================*/
        public static bool TryEnter(object obj, int millisecondsTimeout)
        {
            ArgumentNullException.ThrowIfNull(obj, null);
 
            if (millisecondsTimeout >= -1)
            {
                EnterHelperResult tryEnterResult = TryEnter_FastPath_WithTimeout(obj, millisecondsTimeout);
                if (tryEnterResult == EnterHelperResult.Entered)
                {
                    return true;
                }
                else if (millisecondsTimeout == 0 && (tryEnterResult == EnterHelperResult.Contention))
                {
                    return false;
                }
            }
 
            return TryEnter_Slowpath(obj, millisecondsTimeout);
        }
 
        // The JIT should inline this method to allow check of lockTaken argument to be optimized out
        // in the typical case. Note that the method has to be transparent for inlining to be allowed by the VM.
        public static void TryEnter(object obj, int millisecondsTimeout, ref bool lockTaken)
        {
            if (lockTaken)
                ThrowLockTakenException();
 
            ArgumentNullException.ThrowIfNull(obj, null);
 
            TryEnter_Timeout_WithLockTaken(obj, millisecondsTimeout, ref lockTaken);
        }
 
        public static bool IsEntered(object obj)
        {
            ArgumentNullException.ThrowIfNull(obj);
 
            return IsEnteredNative(obj);
        }
 
        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern bool IsEnteredNative(object obj);
 
        /*========================================================================
    ** Waits for notification from the object (via a Pulse/PulseAll).
    ** timeout indicates how long to wait before the method returns.
    ** This method acquires the monitor waithandle for the object
    ** If this thread holds the monitor lock for the object, it releases it.
    ** On exit from the method, it obtains the monitor lock back.
    **
        ** Exceptions: ArgumentNullException if object is null.
    ========================================================================*/
        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Monitor_Wait")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static partial bool Wait(ObjectHandleOnStack obj, int millisecondsTimeout);
 
        [UnsupportedOSPlatform("browser")]
        public static bool Wait(object obj, int millisecondsTimeout)
        {
            ArgumentNullException.ThrowIfNull(obj);
            ArgumentOutOfRangeException.ThrowIfLessThan(millisecondsTimeout, -1);
 
            return Wait(ObjectHandleOnStack.Create(ref obj), millisecondsTimeout);
        }
 
        /*========================================================================
        ** Sends a notification to a single waiting object.
        * Exceptions: SynchronizationLockException if this method is not called inside
        * a synchronized block of code.
        ========================================================================*/
        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Monitor_Pulse")]
        private static partial void Pulse(ObjectHandleOnStack obj);
 
        public static void Pulse(object obj)
        {
            ArgumentNullException.ThrowIfNull(obj);
 
            Pulse(ObjectHandleOnStack.Create(ref obj));
        }
        /*========================================================================
        ** Sends a notification to all waiting objects.
        ========================================================================*/
        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Monitor_PulseAll")]
        private static partial void PulseAll(ObjectHandleOnStack obj);
 
        public static void PulseAll(object obj)
        {
            ArgumentNullException.ThrowIfNull(obj);
 
            PulseAll(ObjectHandleOnStack.Create(ref obj));
        }
 
        /// <summary>
        /// Gets the number of times there was contention upon trying to take a <see cref="Monitor"/>'s lock so far.
        /// </summary>
        public static long LockContentionCount => GetLockContentionCount() + Lock.ContentionCount;
 
        [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Monitor_GetLockContentionCount")]
        private static partial long GetLockContentionCount();
    }
}