2 // System.Threading.WaitHandle.cs
5 // Dick Porter (dick@ximian.com)
6 // Gonzalo Paniagua Javier (gonzalo@ximian.com
8 // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System
.Reflection
;
33 using System
.Runtime
.CompilerServices
;
35 using System
.Runtime
.Remoting
.Contexts
;
37 using System
.Runtime
.InteropServices
;
38 using Microsoft
.Win32
.SafeHandles
;
39 using System
.Runtime
.ConstrainedExecution
;
41 namespace System
.Threading
43 [StructLayout (LayoutKind
.Sequential
)]
44 public abstract partial class WaitHandle
46 protected static readonly IntPtr InvalidHandle
= (IntPtr
) (-1);
48 internal const int MaxWaitHandles
= 64;
50 // We rely on the reference source implementation of WaitHandle, and it delegates to a function named
51 // WaitOneNative to perform the actual operation of waiting on a handle.
52 // This native operation actually has to call back into managed code and invoke .Wait
53 // on the current SynchronizationContext. As such, our implementation of this "native" method
54 // is actually managed code, and the real native icall being used is Wait_internal.
55 static int WaitOneNative (SafeHandle waitableSafeHandle
, uint millisecondsTimeout
, bool hasThreadAffinity
, bool exitContext
)
59 var context
= SynchronizationContext
.Current
;
62 waitableSafeHandle
.DangerousAddRef (ref release
);
66 SynchronizationAttribute
.ExitContext ();
70 // HACK: Documentation (and public posts by experts like Joe Duffy) suggests that
71 // users must first call SetWaitNotificationRequired to flag that a given synchronization
72 // context overrides .Wait. Because invoking the Wait method is somewhat expensive, we use
73 // the notification-required flag to determine whether or not we should invoke the managed
75 // Another option would be to check whether this context uses the default Wait implementation,
76 // but I don't know of a cheap way to do this that handles derived types correctly.
77 // If the thread does not have a synchronization context set at all, we can safely just
78 // jump directly to invoking Wait_internal.
79 if ((context
!= null) && context
.IsWaitNotificationRequired ()) {
81 new IntPtr
[] { waitableSafeHandle.DangerousGetHandle () }
,
83 (int)millisecondsTimeout
89 IntPtr handle
= waitableSafeHandle
.DangerousGetHandle ();
90 return Wait_internal (&handle
, 1, false, (int)millisecondsTimeout
);
95 waitableSafeHandle
.DangerousRelease ();
99 SynchronizationAttribute
.EnterContext ();
105 static int WaitMultiple(WaitHandle
[] waitHandles
, int millisecondsTimeout
, bool exitContext
, bool WaitAll
)
107 if (waitHandles
.Length
> MaxWaitHandles
)
110 int release_last
= -1;
111 var context
= SynchronizationContext
.Current
;
116 SynchronizationAttribute
.ExitContext ();
119 for (int i
= 0; i
< waitHandles
.Length
; ++i
) {
121 /* we have to put it in a finally block, to avoid having a ThreadAbortException
122 * between the return from DangerousAddRef and the assignement to release_last */
123 bool release
= false;
124 waitHandles
[i
].SafeWaitHandle
.DangerousAddRef (ref release
);
129 if ((context
!= null) && context
.IsWaitNotificationRequired ()) {
130 IntPtr
[] handles
= new IntPtr
[waitHandles
.Length
];
131 for (int i
= 0; i
< waitHandles
.Length
; ++i
)
132 handles
[i
] = waitHandles
[i
].SafeWaitHandle
.DangerousGetHandle ();
134 return context
.Wait (
137 (int)millisecondsTimeout
141 IntPtr
* handles
= stackalloc IntPtr
[waitHandles
.Length
];
143 for (int i
= 0; i
< waitHandles
.Length
; ++i
)
144 handles
[i
] = waitHandles
[i
].SafeWaitHandle
.DangerousGetHandle ();
146 return Wait_internal (handles
, waitHandles
.Length
, WaitAll
, millisecondsTimeout
);
150 for (int i
= release_last
; i
>= 0; --i
) {
151 waitHandles
[i
].SafeWaitHandle
.DangerousRelease ();
156 SynchronizationAttribute
.EnterContext ();
161 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
162 internal unsafe static extern int Wait_internal(IntPtr
* handles
, int numHandles
, bool waitAll
, int ms
);
164 static int SignalAndWaitOne (SafeWaitHandle waitHandleToSignal
,SafeWaitHandle waitHandleToWaitOn
, int millisecondsTimeout
, bool hasThreadAffinity
, bool exitContext
)
166 bool releaseHandleToSignal
= false, releaseHandleToWaitOn
= false;
168 waitHandleToSignal
.DangerousAddRef (ref releaseHandleToSignal
);
169 waitHandleToWaitOn
.DangerousAddRef (ref releaseHandleToWaitOn
);
171 return SignalAndWait_Internal (waitHandleToSignal
.DangerousGetHandle (), waitHandleToWaitOn
.DangerousGetHandle (), millisecondsTimeout
);
173 if (releaseHandleToSignal
)
174 waitHandleToSignal
.DangerousRelease ();
175 if (releaseHandleToWaitOn
)
176 waitHandleToWaitOn
.DangerousRelease ();
180 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
181 static extern int SignalAndWait_Internal (IntPtr toSignal
, IntPtr toWaitOn
, int ms
);
183 internal static int ToTimeoutMilliseconds(TimeSpan timeout
)
185 var timeoutMilliseconds
= (long)timeout
.TotalMilliseconds
;
186 if (timeoutMilliseconds
< -1 || timeoutMilliseconds
> int.MaxValue
)
188 throw new ArgumentOutOfRangeException(nameof(timeout
), SR
.ArgumentOutOfRange_NeedNonNegOrNegative1
);
190 return (int)timeoutMilliseconds
;