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