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