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