summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Threading/WaitHandle.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Threading/WaitHandle.cs')
-rw-r--r--src/mscorlib/src/System/Threading/WaitHandle.cs609
1 files changed, 609 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Threading/WaitHandle.cs b/src/mscorlib/src/System/Threading/WaitHandle.cs
new file mode 100644
index 0000000000..ff1eb46eb1
--- /dev/null
+++ b/src/mscorlib/src/System/Threading/WaitHandle.cs
@@ -0,0 +1,609 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: Class to represent all synchronization objects in the runtime (that allow multiple wait)
+**
+**
+=============================================================================*/
+
+namespace System.Threading
+{
+ using System.Threading;
+ using System.Runtime.Remoting;
+ using System;
+ using System.Security.Permissions;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
+ using Microsoft.Win32.SafeHandles;
+ using System.Runtime.Versioning;
+ using System.Runtime.ConstrainedExecution;
+ using System.Diagnostics.Contracts;
+ using System.Diagnostics.CodeAnalysis;
+ using Win32Native = Microsoft.Win32.Win32Native;
+
+[System.Runtime.InteropServices.ComVisible(true)]
+#if FEATURE_REMOTING
+ public abstract class WaitHandle : MarshalByRefObject, IDisposable {
+#else // FEATURE_REMOTING
+ public abstract class WaitHandle : IDisposable {
+#endif // FEATURE_REMOTING
+ public const int WaitTimeout = 0x102;
+
+ private const int MAX_WAITHANDLES = 64;
+
+#pragma warning disable 414 // Field is not used from managed.
+ private IntPtr waitHandle; // !!! DO NOT MOVE THIS FIELD. (See defn of WAITHANDLEREF in object.h - has hardcoded access to this field.)
+#pragma warning restore 414
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal volatile SafeWaitHandle safeWaitHandle;
+
+ internal bool hasThreadAffinity;
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private static IntPtr GetInvalidHandle()
+ {
+ return Win32Native.INVALID_HANDLE_VALUE;
+ }
+ protected static readonly IntPtr InvalidHandle = GetInvalidHandle();
+ private const int WAIT_OBJECT_0 = 0;
+ private const int WAIT_ABANDONED = 0x80;
+ private const int WAIT_FAILED = 0x7FFFFFFF;
+ private const int ERROR_TOO_MANY_POSTS = 0x12A;
+
+ internal enum OpenExistingResult
+ {
+ Success,
+ NameNotFound,
+ PathNotFound,
+ NameInvalid
+ }
+
+ protected WaitHandle()
+ {
+ Init();
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private void Init()
+ {
+ safeWaitHandle = null;
+ waitHandle = InvalidHandle;
+ hasThreadAffinity = false;
+ }
+
+
+ [Obsolete("Use the SafeWaitHandle property instead.")]
+ public virtual IntPtr Handle
+ {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get { return safeWaitHandle == null ? InvalidHandle : safeWaitHandle.DangerousGetHandle();}
+
+ [System.Security.SecurityCritical] // auto-generated_required
+#if !FEATURE_CORECLR
+ [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
+#endif
+ set
+ {
+ if (value == InvalidHandle)
+ {
+ // This line leaks a handle. However, it's currently
+ // not perfectly clear what the right behavior is here
+ // anyways. This preserves Everett behavior. We should
+ // ideally do these things:
+ // *) Expose a settable SafeHandle property on WaitHandle.
+ // *) Expose a settable OwnsHandle property on SafeHandle.
+ if (safeWaitHandle != null)
+ {
+ safeWaitHandle.SetHandleAsInvalid();
+ safeWaitHandle = null;
+ }
+ }
+ else
+ {
+ safeWaitHandle = new SafeWaitHandle(value, true);
+ }
+ waitHandle = value;
+ }
+ }
+
+
+ public SafeWaitHandle SafeWaitHandle
+ {
+ [System.Security.SecurityCritical] // auto-generated_required
+#if !FEATURE_CORECLR
+ [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
+#endif
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ get
+ {
+ if (safeWaitHandle == null)
+ {
+ safeWaitHandle = new SafeWaitHandle(InvalidHandle, false);
+ }
+ return safeWaitHandle;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated_required
+#if !FEATURE_CORECLR
+ [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
+#endif
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
+ set
+ {
+ // Set safeWaitHandle and waitHandle in a CER so we won't take
+ // a thread abort between the statements and leave the wait
+ // handle in an invalid state. Note this routine is not thread
+ // safe however.
+ RuntimeHelpers.PrepareConstrainedRegions();
+ try { }
+ finally
+ {
+ if (value == null)
+ {
+ safeWaitHandle = null;
+ waitHandle = InvalidHandle;
+ }
+ else
+ {
+ safeWaitHandle = value;
+ waitHandle = safeWaitHandle.DangerousGetHandle();
+ }
+ }
+ }
+ }
+
+ // Assembly-private version that doesn't do a security check. Reduces the
+ // number of link-time security checks when reading & writing to a file,
+ // and helps avoid a link time check while initializing security (If you
+ // call a Serialization method that requires security before security
+ // has started up, the link time check will start up security, run
+ // serialization code for some security attribute stuff, call into
+ // FileStream, which will then call Sethandle, which requires a link time
+ // security check.). While security has fixed that problem, we still
+ // don't need to do a linktime check here.
+ [System.Security.SecurityCritical] // auto-generated
+ internal void SetHandleInternal(SafeWaitHandle handle)
+ {
+ safeWaitHandle = handle;
+ waitHandle = handle.DangerousGetHandle();
+ }
+
+ public virtual bool WaitOne (int millisecondsTimeout, bool exitContext)
+ {
+ if (millisecondsTimeout < -1)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ Contract.EndContractBlock();
+ return WaitOne((long)millisecondsTimeout,exitContext);
+ }
+
+ public virtual bool WaitOne (TimeSpan timeout, bool exitContext)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (-1 > tm || (long) Int32.MaxValue < tm)
+ {
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ return WaitOne(tm,exitContext);
+ }
+
+ public virtual bool WaitOne ()
+ {
+ //Infinite Timeout
+ return WaitOne(-1,false);
+ }
+
+ public virtual bool WaitOne(int millisecondsTimeout)
+ {
+ return WaitOne(millisecondsTimeout, false);
+ }
+
+ public virtual bool WaitOne(TimeSpan timeout)
+ {
+ return WaitOne(timeout, false);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")]
+ private bool WaitOne(long timeout, bool exitContext)
+ {
+ return InternalWaitOne(safeWaitHandle, timeout, hasThreadAffinity, exitContext);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal static bool InternalWaitOne(SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext)
+ {
+ if (waitableSafeHandle == null)
+ {
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic"));
+ }
+ Contract.EndContractBlock();
+ int ret = WaitOneNative(waitableSafeHandle, (uint)millisecondsTimeout, hasThreadAffinity, exitContext);
+
+ if(AppDomainPauseManager.IsPaused)
+ AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS();
+
+ if (ret == WAIT_ABANDONED)
+ {
+ ThrowAbandonedMutexException();
+ }
+ return (ret != WaitTimeout);
+ }
+
+ [System.Security.SecurityCritical]
+ internal bool WaitOneWithoutFAS()
+ {
+ // version of waitone without fast application switch (FAS) support
+ // This is required to support the Wait which FAS needs (otherwise recursive dependency comes in)
+ if (safeWaitHandle == null)
+ {
+ throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_Generic"));
+ }
+ Contract.EndContractBlock();
+
+ long timeout = -1;
+ int ret = WaitOneNative(safeWaitHandle, (uint)timeout, hasThreadAffinity, false);
+ if (ret == WAIT_ABANDONED)
+ {
+ ThrowAbandonedMutexException();
+ }
+ return (ret != WaitTimeout);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern int WaitOneNative(SafeHandle waitableSafeHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext);
+
+ /*========================================================================
+ ** Waits for signal from all the objects.
+ ** timeout indicates how long to wait before the method returns.
+ ** This method will return either when all the object have been pulsed
+ ** or timeout milliseonds have elapsed.
+ ** If exitContext is true then the synchronization domain for the context
+ ** (if in a synchronized context) is exited before the wait and reacquired
+ ========================================================================*/
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ private static extern int WaitMultiple(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext, bool WaitAll);
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+ {
+ if (waitHandles == null)
+ {
+ throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_Waithandles"));
+ }
+ if(waitHandles.Length == 0)
+ {
+ //
+ // Some history: in CLR 1.0 and 1.1, we threw ArgumentException in this case, which was correct.
+ // Somehow, in 2.0, this became ArgumentNullException. This was not fixed until Silverlight 2,
+ // which went back to ArgumentException.
+ //
+ // Now we're in a bit of a bind. Backward-compatibility requires us to keep throwing ArgumentException
+ // in CoreCLR, and ArgumentNullException in the desktop CLR. This is ugly, but so is breaking
+ // user code.
+ //
+#if FEATURE_CORECLR
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyWaithandleArray"));
+#else
+ throw new ArgumentNullException(Environment.GetResourceString("Argument_EmptyWaithandleArray"));
+#endif
+ }
+ if (waitHandles.Length > MAX_WAITHANDLES)
+ {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_MaxWaitHandles"));
+ }
+ if (-1 > millisecondsTimeout)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ Contract.EndContractBlock();
+ WaitHandle[] internalWaitHandles = new WaitHandle[waitHandles.Length];
+ for (int i = 0; i < waitHandles.Length; i ++)
+ {
+ WaitHandle waitHandle = waitHandles[i];
+
+ if (waitHandle == null)
+ throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_ArrayElement"));
+
+#if FEATURE_REMOTING
+ if (RemotingServices.IsTransparentProxy(waitHandle))
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
+#endif
+
+ internalWaitHandles[i] = waitHandle;
+ }
+#if _DEBUG
+ // make sure we do not use waitHandles any more.
+ waitHandles = null;
+#endif
+
+#if FEATURE_LEGACYNETCF
+ // WinCE did not support "wait all." It turns out that this resulted in NetCF's WaitAll implementation always returning true.
+ // Unfortunately, some apps took a dependency on that, so we have to replicate the behavior here.
+ if (CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
+ return true;
+#endif
+
+ int ret = WaitMultiple(internalWaitHandles, millisecondsTimeout, exitContext, true /* waitall*/ );
+
+ if(AppDomainPauseManager.IsPaused)
+ AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS();
+
+ if ((WAIT_ABANDONED <= ret) && (WAIT_ABANDONED+internalWaitHandles.Length > ret))
+ {
+ //In the case of WaitAll the OS will only provide the
+ // information that mutex was abandoned.
+ // It won't tell us which one. So we can't set the Index or provide access to the Mutex
+ ThrowAbandonedMutexException();
+ }
+
+ GC.KeepAlive(internalWaitHandles);
+ return (ret != WaitTimeout);
+ }
+
+ public static bool WaitAll(
+ WaitHandle[] waitHandles,
+ TimeSpan timeout,
+ bool exitContext)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (-1 > tm || (long) Int32.MaxValue < tm)
+ {
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ return WaitAll(waitHandles,(int)tm, exitContext);
+ }
+
+
+ /*========================================================================
+ ** Shorthand for WaitAll with timeout = Timeout.Infinite and exitContext = true
+ ========================================================================*/
+ public static bool WaitAll(WaitHandle[] waitHandles)
+ {
+ return WaitAll(waitHandles, Timeout.Infinite, true);
+ }
+
+ public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout)
+ {
+ return WaitAll(waitHandles, millisecondsTimeout, true);
+ }
+
+ public static bool WaitAll(WaitHandle[] waitHandles, TimeSpan timeout)
+ {
+ return WaitAll(waitHandles, timeout, true);
+ }
+
+
+ /*========================================================================
+ ** Waits for notification from any of the objects.
+ ** timeout indicates how long to wait before the method returns.
+ ** This method will return either when either one of the object have been
+ ** signalled or timeout milliseonds have elapsed.
+ ** If exitContext is true then the synchronization domain for the context
+ ** (if in a synchronized context) is exited before the wait and reacquired
+ ========================================================================*/
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
+ {
+ if (waitHandles==null)
+ {
+ throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_Waithandles"));
+ }
+ if(waitHandles.Length == 0)
+ {
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyWaithandleArray"));
+ }
+ if (MAX_WAITHANDLES < waitHandles.Length)
+ {
+ throw new NotSupportedException(Environment.GetResourceString("NotSupported_MaxWaitHandles"));
+ }
+ if (-1 > millisecondsTimeout)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ Contract.EndContractBlock();
+ WaitHandle[] internalWaitHandles = new WaitHandle[waitHandles.Length];
+ for (int i = 0; i < waitHandles.Length; i ++)
+ {
+ WaitHandle waitHandle = waitHandles[i];
+
+ if (waitHandle == null)
+ throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_ArrayElement"));
+
+#if FEATURE_REMOTING
+ if (RemotingServices.IsTransparentProxy(waitHandle))
+ throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_WaitOnTransparentProxy"));
+#endif
+
+ internalWaitHandles[i] = waitHandle;
+ }
+#if _DEBUG
+ // make sure we do not use waitHandles any more.
+ waitHandles = null;
+#endif
+ int ret = WaitMultiple(internalWaitHandles, millisecondsTimeout, exitContext, false /* waitany*/ );
+
+ if(AppDomainPauseManager.IsPaused)
+ AppDomainPauseManager.ResumeEvent.WaitOneWithoutFAS();
+
+ if ((WAIT_ABANDONED <= ret) && (WAIT_ABANDONED+internalWaitHandles.Length > ret))
+ {
+ int mutexIndex = ret -WAIT_ABANDONED;
+ if(0 <= mutexIndex && mutexIndex < internalWaitHandles.Length)
+ {
+ ThrowAbandonedMutexException(mutexIndex,internalWaitHandles[mutexIndex]);
+ }
+ else
+ {
+ ThrowAbandonedMutexException();
+ }
+ }
+
+ GC.KeepAlive(internalWaitHandles);
+ return ret;
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(
+ WaitHandle[] waitHandles,
+ TimeSpan timeout,
+ bool exitContext)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (-1 > tm || (long) Int32.MaxValue < tm)
+ {
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ return WaitAny(waitHandles,(int)tm, exitContext);
+ }
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout)
+ {
+ return WaitAny(waitHandles, timeout, true);
+ }
+
+
+ /*========================================================================
+ ** Shorthand for WaitAny with timeout = Timeout.Infinite and exitContext = true
+ ========================================================================*/
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(WaitHandle[] waitHandles)
+ {
+ return WaitAny(waitHandles, Timeout.Infinite, true);
+ }
+
+ [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
+ public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout)
+ {
+ return WaitAny(waitHandles, millisecondsTimeout, true);
+ }
+
+ /*=================================================
+ ==
+ == SignalAndWait
+ ==
+ ==================================================*/
+
+ [System.Security.SecurityCritical] // auto-generated
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private static extern int SignalAndWaitOne(SafeWaitHandle waitHandleToSignal,SafeWaitHandle waitHandleToWaitOn, int millisecondsTimeout,
+ bool hasThreadAffinity, bool exitContext);
+
+ public static bool SignalAndWait(
+ WaitHandle toSignal,
+ WaitHandle toWaitOn)
+ {
+ return SignalAndWait(toSignal,toWaitOn,-1,false);
+ }
+
+ public static bool SignalAndWait(
+ WaitHandle toSignal,
+ WaitHandle toWaitOn,
+ TimeSpan timeout,
+ bool exitContext)
+ {
+ long tm = (long)timeout.TotalMilliseconds;
+ if (-1 > tm || (long) Int32.MaxValue < tm)
+ {
+ throw new ArgumentOutOfRangeException("timeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ return SignalAndWait(toSignal,toWaitOn,(int)tm,exitContext);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety.")]
+ public static bool SignalAndWait(
+ WaitHandle toSignal,
+ WaitHandle toWaitOn,
+ int millisecondsTimeout,
+ bool exitContext)
+ {
+ if(null == toSignal)
+ {
+ throw new ArgumentNullException("toSignal");
+ }
+ if(null == toWaitOn)
+ {
+ throw new ArgumentNullException("toWaitOn");
+ }
+ if (-1 > millisecondsTimeout)
+ {
+ throw new ArgumentOutOfRangeException("millisecondsTimeout", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegOrNegative1"));
+ }
+ Contract.EndContractBlock();
+
+ //NOTE: This API is not supporting Pause/Resume as it's not exposed in CoreCLR (not in WP or SL)
+ int ret = SignalAndWaitOne(toSignal.safeWaitHandle,toWaitOn.safeWaitHandle,millisecondsTimeout,
+ toWaitOn.hasThreadAffinity,exitContext);
+
+ if(WAIT_FAILED != ret && toSignal.hasThreadAffinity)
+ {
+ Thread.EndCriticalRegion();
+ Thread.EndThreadAffinity();
+ }
+
+ if(WAIT_ABANDONED == ret)
+ {
+ ThrowAbandonedMutexException();
+ }
+
+ if(ERROR_TOO_MANY_POSTS == ret)
+ {
+ throw new InvalidOperationException(Environment.GetResourceString("Threading.WaitHandleTooManyPosts"));
+ }
+
+ //Object was signaled
+ if(WAIT_OBJECT_0 == ret)
+ {
+ return true;
+ }
+
+ //Timeout
+ return false;
+ }
+
+ private static void ThrowAbandonedMutexException()
+ {
+ throw new AbandonedMutexException();
+ }
+
+ private static void ThrowAbandonedMutexException(int location, WaitHandle handle)
+ {
+ throw new AbandonedMutexException(location, handle);
+ }
+
+ public virtual void Close()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ protected virtual void Dispose(bool explicitDisposing)
+ {
+ if (safeWaitHandle != null)
+ {
+ safeWaitHandle.Close();
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}