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