Merge pull request #505 from roji/shutdown_flow
[mono.git] / mcs / class / corlib / System.Threading / WaitHandle.cs
1 //
2 // System.Threading.WaitHandle.cs
3 //
4 // Author:
5 //      Dick Porter (dick@ximian.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com
7 //
8 // (C) 2002,2003 Ximian, Inc.   (http://www.ximian.com)
9 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
10 //
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:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
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.
29 //
30
31 using System;
32 using System.Reflection;
33 using System.Runtime.CompilerServices;
34 using System.Runtime.Remoting.Contexts;
35 using System.Security.Permissions;
36 using System.Runtime.InteropServices;
37 using Microsoft.Win32.SafeHandles;
38 using System.Runtime.ConstrainedExecution;
39
40 namespace System.Threading
41 {
42         [ComVisible (true)]
43         [StructLayout (LayoutKind.Sequential)]
44         public abstract class WaitHandle
45                 : MarshalByRefObject, IDisposable
46         {
47                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
48                 private static extern bool WaitAll_internal(WaitHandle[] handles, int ms, bool exitContext);
49                 
50                 static void CheckArray (WaitHandle [] handles, bool waitAll)
51                 {
52                         if (handles == null)
53                                 throw new ArgumentNullException ("waitHandles");
54
55                         int length = handles.Length;
56                         if (length > 64)
57                                 throw new NotSupportedException ("Too many handles");
58
59                         if (handles.Length == 0) {
60                                 // MS throws different exceptions from the different methods.
61                                 if (waitAll)
62                                         throw new ArgumentNullException ("waitHandles");
63                                 else
64                                         throw new ArgumentException ();
65                         }
66
67 #if false
68                         //
69                         // Although we should thrown an exception if this is an STA thread,
70                         // Mono does not know anything about STA threads, and just makes
71                         // things like Paint.NET not even possible to work.
72                         //
73                         // See bug #78455 for the bug this is supposed to fix. 
74                         // 
75                         if (waitAll && length > 1 && IsSTAThread)
76                                 throw new NotSupportedException ("WaitAll for multiple handles is not allowed on an STA thread.");
77 #endif
78                         foreach (WaitHandle w in handles) {
79                                 if (w == null)
80                                         throw new ArgumentNullException ("waitHandles", "null handle");
81
82                                 if (w.safe_wait_handle == null)
83                                         throw new ArgumentException ("null element found", "waitHandle");
84
85                         }
86                 }
87 #if false
88                 // usage of property is commented - see above
89                 static bool IsSTAThread {
90                         get {
91                                 bool isSTA = Thread.CurrentThread.ApartmentState ==
92                                         ApartmentState.STA;
93
94                                 // FIXME: remove this check after Thread.ApartmentState
95                                 // has been properly implemented.
96                                 if (!isSTA) {
97                                         Assembly asm = Assembly.GetEntryAssembly ();
98                                         if (asm != null)
99                                                 isSTA = asm.EntryPoint.GetCustomAttributes (typeof (STAThreadAttribute), false).Length > 0;
100                                 }
101
102                                 return isSTA;
103                         }
104                 }
105 #endif
106                 public static bool WaitAll(WaitHandle[] waitHandles)
107                 {
108                         CheckArray (waitHandles, true);
109                         return(WaitAll_internal(waitHandles, Timeout.Infinite, false));
110                 }
111
112                 public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext)
113                 {
114                         CheckArray (waitHandles, true);
115                         // check negative - except for -1 (which is Timeout.Infinite)
116                         if (millisecondsTimeout < Timeout.Infinite)
117                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
118
119                         try {
120                                 if (exitContext) {
121 #if MONOTOUCH
122                                         throw new NotSupportedException ("exitContext == true is not supported");
123 #else
124                                         SynchronizationAttribute.ExitContext ();
125 #endif
126                                 }
127                                 return(WaitAll_internal(waitHandles, millisecondsTimeout, false));
128                         }
129                         finally {
130                                 if (exitContext) SynchronizationAttribute.EnterContext ();
131                         }
132                 }
133
134                 public static bool WaitAll(WaitHandle[] waitHandles,
135                                            TimeSpan timeout,
136                                            bool exitContext)
137                 {
138                         CheckArray (waitHandles, true);
139                         long ms = (long) timeout.TotalMilliseconds;
140                         
141                         if (ms < -1 || ms > Int32.MaxValue)
142                                 throw new ArgumentOutOfRangeException ("timeout");
143
144                         try {
145                                 if (exitContext) {
146 #if MONOTOUCH
147                                         throw new NotSupportedException ("exitContext == true is not supported");
148 #else
149                                         SynchronizationAttribute.ExitContext ();
150 #endif
151                                 }
152                                 return (WaitAll_internal (waitHandles, (int) ms, exitContext));
153                         }
154                         finally {
155                                 if (exitContext) SynchronizationAttribute.EnterContext ();
156                         }
157                 }
158
159                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
160                 private static extern int WaitAny_internal(WaitHandle[] handles, int ms, bool exitContext);
161
162                 // LAMESPEC: Doesn't specify how to signal failures
163                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
164                 public static int WaitAny(WaitHandle[] waitHandles)
165                 {
166                         CheckArray (waitHandles, false);
167                         return(WaitAny_internal(waitHandles, Timeout.Infinite, false));
168                 }
169
170                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
171                 public static int WaitAny(WaitHandle[] waitHandles,
172                                           int millisecondsTimeout,
173                                           bool exitContext)
174                 {
175                         CheckArray (waitHandles, false);
176                         // check negative - except for -1 (which is Timeout.Infinite)
177                         if (millisecondsTimeout < Timeout.Infinite)
178                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
179
180                         try {
181                                 if (exitContext) {
182 #if MONOTOUCH
183                                         throw new NotSupportedException ("exitContext == true is not supported");
184 #else
185                                         SynchronizationAttribute.ExitContext ();
186 #endif
187                                 }
188                                 return(WaitAny_internal(waitHandles, millisecondsTimeout, exitContext));
189                         }
190                         finally {
191                                 if (exitContext) SynchronizationAttribute.EnterContext ();
192                         }
193                 }
194
195                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
196                 public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout)
197                 {
198                         return WaitAny (waitHandles, timeout, false);
199                 }
200
201                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
202                 public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout)
203                 {
204                         return WaitAny (waitHandles, millisecondsTimeout, false);
205                 }
206
207                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
208                 public static int WaitAny(WaitHandle[] waitHandles,
209                                           TimeSpan timeout, bool exitContext)
210                 {
211                         CheckArray (waitHandles, false);
212                         long ms = (long) timeout.TotalMilliseconds;
213                         
214                         if (ms < -1 || ms > Int32.MaxValue)
215                                 throw new ArgumentOutOfRangeException ("timeout");
216
217                         try {
218                                 if (exitContext) {
219 #if MONOTOUCH
220                                         throw new NotSupportedException ("exitContext == true is not supported");
221 #else
222                                         SynchronizationAttribute.ExitContext ();
223 #endif
224                                 }
225                                 return (WaitAny_internal(waitHandles, (int) ms, exitContext));
226                         }
227                         finally {
228                                 if (exitContext) SynchronizationAttribute.EnterContext ();
229                         }
230                 }
231
232                 protected WaitHandle()
233                 {
234                         // FIXME
235                 }
236
237                 public virtual void Close ()
238                 {
239                         Dispose(true);
240                 }
241
242 #if NET_4_0
243                 public void Dispose ()
244 #else           
245                 void IDisposable.Dispose ()
246 #endif
247                 {
248                         Close ();
249                 }
250
251                 public const int WaitTimeout = 258;
252
253                 //
254                 // In 2.0 we use SafeWaitHandles instead of IntPtrs
255                 //
256                 SafeWaitHandle safe_wait_handle;
257
258                 [Obsolete ("In the profiles > 2.x, use SafeHandle instead of Handle")]
259                 public virtual IntPtr Handle {
260                         get {
261                                 return safe_wait_handle.DangerousGetHandle ();
262                         }
263
264                         [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
265                         [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)]
266                         set {
267                                 if (value == InvalidHandle)
268                                         safe_wait_handle = new SafeWaitHandle (InvalidHandle, false);
269                                 else
270                                         safe_wait_handle = new SafeWaitHandle (value, true);
271                         }
272                 }
273                 
274                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
275                 private extern bool WaitOne_internal(IntPtr handle, int ms, bool exitContext);
276
277                 protected virtual void Dispose (bool explicitDisposing)
278                 {
279                         if (!disposed){
280
281                                 //
282                                 // This is only the case if the handle was never properly initialized
283                                 // most likely a bug in the derived class
284                                 //
285                                 if (safe_wait_handle == null)
286                                         return;
287
288                                 lock (this){
289                                         if (disposed)
290                                                 return;
291
292                                         disposed = true;
293                                         if (safe_wait_handle != null)
294                                                 safe_wait_handle.Dispose ();
295                                 }
296                         }
297                 }
298
299                 public SafeWaitHandle SafeWaitHandle {
300                         [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
301                         get {
302                                 return safe_wait_handle;
303                         }
304
305                         [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
306                         set {
307                                 if (value == null)
308                                         safe_wait_handle = new SafeWaitHandle (InvalidHandle, false);
309                                 else
310                                         safe_wait_handle = value;
311                         }
312                 }
313
314                 public static bool SignalAndWait (WaitHandle toSignal,
315                                                   WaitHandle toWaitOn)
316                 {
317                         return SignalAndWait (toSignal, toWaitOn, -1, false);
318                 }
319                 
320                 public static bool SignalAndWait (WaitHandle toSignal,
321                                                   WaitHandle toWaitOn,
322                                                   int millisecondsTimeout,
323                                                   bool exitContext)
324                 {
325                         if (toSignal == null)
326                                 throw new ArgumentNullException ("toSignal");
327                         if (toWaitOn == null)
328                                 throw new ArgumentNullException ("toWaitOn");
329
330                         if (millisecondsTimeout < -1)
331                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
332
333                         return SignalAndWait_Internal (toSignal.Handle, toWaitOn.Handle, millisecondsTimeout, exitContext);
334                 }
335                 
336                 public static bool SignalAndWait (WaitHandle toSignal,
337                                                   WaitHandle toWaitOn,
338                                                   TimeSpan timeout,
339                                                   bool exitContext)
340                 {
341                         double ms = timeout.TotalMilliseconds;
342                         if (ms > Int32.MaxValue)
343                                 throw new ArgumentOutOfRangeException ("timeout");
344
345                         return SignalAndWait (toSignal, toWaitOn, Convert.ToInt32 (ms), false);
346                 }
347
348                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
349                 static extern bool SignalAndWait_Internal (IntPtr toSignal, IntPtr toWaitOn, int ms, bool exitContext);
350
351                 public virtual bool WaitOne()
352                 {
353                         CheckDisposed ();
354                         bool release = false;
355                         try {
356                                 safe_wait_handle.DangerousAddRef (ref release);
357                                 return (WaitOne_internal(safe_wait_handle.DangerousGetHandle (), Timeout.Infinite, false));
358                         } finally {
359                                 if (release)
360                                         safe_wait_handle.DangerousRelease ();
361                         }
362                 }
363
364                 public virtual bool WaitOne(int millisecondsTimeout, bool exitContext)
365                 {
366                         CheckDisposed ();
367                         // check negative - except for -1 (which is Timeout.Infinite)
368                         if (millisecondsTimeout < Timeout.Infinite)
369                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
370
371                         bool release = false;
372                         try {
373                                 if (exitContext) {
374 #if !MONOTOUCH
375                                         SynchronizationAttribute.ExitContext ();
376 #endif
377                                 }
378                                 safe_wait_handle.DangerousAddRef (ref release);
379                                 return (WaitOne_internal(safe_wait_handle.DangerousGetHandle (), millisecondsTimeout, exitContext));
380                         } finally {
381 #if !MONOTOUCH
382                                 if (exitContext)
383                                         SynchronizationAttribute.EnterContext ();
384 #endif
385                                 if (release)
386                                         safe_wait_handle.DangerousRelease ();
387                         }
388                 }
389
390                 public virtual bool WaitOne (int millisecondsTimeout)
391                 {
392                         return WaitOne (millisecondsTimeout, false);
393                 }
394
395                 public virtual bool WaitOne (TimeSpan timeout)
396                 {
397                         return WaitOne (timeout, false);
398                 }
399
400                 public virtual bool WaitOne(TimeSpan timeout, bool exitContext)
401                 {
402                         CheckDisposed ();
403                         long ms = (long) timeout.TotalMilliseconds;
404                         if (ms < -1 || ms > Int32.MaxValue)
405                                 throw new ArgumentOutOfRangeException ("timeout");
406
407                         bool release = false;
408                         try {
409                                 if (exitContext) {
410 #if !MONOTOUCH
411                                         SynchronizationAttribute.ExitContext ();
412 #endif
413                                 }
414                                 safe_wait_handle.DangerousAddRef (ref release);
415                                 return (WaitOne_internal(safe_wait_handle.DangerousGetHandle (), (int) ms, exitContext));
416                         }
417                         finally {
418 #if !MONOTOUCH
419                                 if (exitContext)
420                                         SynchronizationAttribute.EnterContext ();
421 #endif
422                                 if (release)
423                                         safe_wait_handle.DangerousRelease ();
424                         }
425                 }
426
427                 internal void CheckDisposed ()
428                 {
429                         if (disposed || safe_wait_handle == null)
430                                 throw new ObjectDisposedException (GetType ().FullName);
431                 }
432
433                 public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout)
434                 {
435                         return WaitAll (waitHandles, millisecondsTimeout, false);
436                 }
437
438                 public static bool WaitAll(WaitHandle[] waitHandles, TimeSpan timeout)
439                 {
440                         return WaitAll (waitHandles, timeout, false);
441                 }
442                 
443                 protected static readonly IntPtr InvalidHandle = (IntPtr) (-1);
444                 bool disposed = false;
445         }
446 }