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