[w32handle] Fix deadlock on SignalAndWait (#4973)
[mono.git] / mcs / class / corlib / Test / System.Threading / WaitHandleTest.cs
1 //
2 // WaitHandleTest.cs
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29
30 using System;
31 using System.Collections.Generic;
32 using System.Threading;
33
34 using NUnit.Framework;
35
36 namespace MonoTests.System.Threading {
37
38         [TestFixture]
39         public class WaitHandleTest {
40
41                 TimeSpan Infinite = new TimeSpan (-10000);      // -10000 ticks == -1 ms
42                 TimeSpan SmallNegative = new TimeSpan (-2);     // between 0 and -1.0 (infinite) ms
43                 TimeSpan Negative = new TimeSpan (-20000);      // really negative
44
45                 WaitHandle [] TooLarge = new Mutex [65];
46                 WaitHandle [] Empty = new Mutex [1];
47                 WaitHandle [] Single = new Mutex [1] { new Mutex (true) };
48
49
50                 [Test]
51                 [ExpectedException (typeof (ArgumentNullException))]
52                 public void WaitAny_WaitHandle_Null ()
53                 {
54                         WaitHandle.WaitAny (null);
55                 }
56
57                 [Test]
58                 [ExpectedException (typeof (NotSupportedException))]
59                 public void WaitAny_WaitHandle_TooLarge ()
60                 {
61                         WaitHandle.WaitAny (TooLarge);
62                 }
63
64                 [Test]
65                 [ExpectedException (typeof (ArgumentNullException))]
66                 public void WaitAny_WaitHandle_Empty ()
67                 {
68                         WaitHandle.WaitAny (Empty);
69                 }
70
71                 [Test]
72                 public void WaitAny_WaitHandle ()
73                 {
74                         Assert.AreEqual (0, WaitHandle.WaitAny (Single), "WaitAny");
75                 }
76
77                 [Test]
78                 [ExpectedException (typeof (ArgumentNullException))]
79                 public void WaitAny_WaitHandleNull_Int ()
80                 {
81                         WaitHandle.WaitAny (null, -1);
82                 }
83
84                 [Test]
85                 [ExpectedException (typeof (NotSupportedException))]
86                 public void WaitAny_WaitHandle_TooLarge_Int ()
87                 {
88                         WaitHandle.WaitAny (TooLarge, -1);
89                 }
90
91                 [Test]
92                 [ExpectedException (typeof (ArgumentNullException))]
93                 public void WaitAny_WaitHandle_Empty_Int ()
94                 {
95                         WaitHandle.WaitAny (Empty, -1);
96                 }
97
98                 [Test]
99                 public void WaitAny_WaitHandle_Int ()
100                 {
101                         // -1 is infinite
102                         Assert.AreEqual (0, WaitHandle.WaitAny (Single, -1), "WaitAny");
103                 }
104
105                 [Test]
106                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
107                 public void WaitAny_WaitHandle_Int_Negative ()
108                 {
109                         Assert.AreEqual (0, WaitHandle.WaitAny (Single, -2), "WaitAny");
110                 }
111
112                 [Test]
113                 [ExpectedException (typeof (ArgumentNullException))]
114                 public void WaitAny_WaitHandleNull_TimeSpan ()
115                 {
116                         WaitHandle.WaitAny (null, Infinite);
117                 }
118
119                 [Test]
120                 [ExpectedException (typeof (NotSupportedException))]
121                 public void WaitAny_WaitHandle_TooLarge_TimeSpan ()
122                 {
123                         WaitHandle.WaitAny (TooLarge, Infinite);
124                 }
125
126                 [Test]
127                 [ExpectedException (typeof (ArgumentNullException))]
128                 public void WaitAny_WaitHandle_Empty_TimeSpan ()
129                 {
130                         WaitHandle.WaitAny (Empty, Infinite);
131                 }
132
133                 [Test]
134                 public void WaitAny_WaitHandle_TimeSpan ()
135                 {
136                         Assert.AreEqual (Timeout.Infinite, (int) Infinite.TotalMilliseconds, "Infinite");
137                         Assert.AreEqual (0, WaitHandle.WaitAny (Single, Infinite), "WaitAny-Infinite");
138                         Assert.AreEqual (0, WaitHandle.WaitAny (Single, SmallNegative), "WaitAny-SmallNegative");
139                 }
140
141                 [Test]
142                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
143                 public void WaitAny_WaitHandle_TimeSpan_Negative ()
144                 {
145                         Assert.AreEqual (0, WaitHandle.WaitAny (Single, Negative), "WaitAny");
146                 }
147
148                 [Test]
149                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
150                 public void WaitAny_WaitHandle_TimeSpan_MaxValue ()
151                 {
152                         Assert.AreEqual (0, WaitHandle.WaitAny (Single, TimeSpan.MaxValue), "WaitAny");
153                 }
154
155
156                 [Test]
157                 [ExpectedException (typeof (ArgumentNullException))]
158                 public void WaitAll_WaitHandle_Null ()
159                 {
160                         WaitHandle.WaitAll (null);
161                 }
162
163                 [Test]
164                 [ExpectedException (typeof (NotSupportedException))]
165                 public void WaitAll_WaitHandle_TooLarge ()
166                 {
167                         WaitHandle.WaitAll (TooLarge);
168                 }
169
170                 [Test]
171                 [ExpectedException (typeof (ArgumentNullException))]
172                 public void WaitAll_WaitHandle_Empty ()
173                 {
174                         WaitHandle.WaitAll (Empty);
175                 }
176
177                 [Test]
178                 public void WaitAll_WaitHandle ()
179                 {
180                         Assert.IsTrue (WaitHandle.WaitAll (Single), "WaitAll");
181                 }
182
183                 [Test]
184                 [ExpectedException (typeof (ArgumentNullException))]
185                 public void WaitAll_WaitHandleNull_Int ()
186                 {
187                         WaitHandle.WaitAll (null, -1);
188                 }
189
190                 [Test]
191                 [ExpectedException (typeof (NotSupportedException))]
192                 public void WaitAll_WaitHandle_TooLarge_Int ()
193                 {
194                         WaitHandle.WaitAll (TooLarge, -1);
195                 }
196
197                 [Test]
198                 [ExpectedException (typeof (ArgumentNullException))]
199                 public void WaitAll_WaitHandle_Empty_Int ()
200                 {
201                         WaitHandle.WaitAll (Empty, -1);
202                 }
203
204                 [Test]
205                 public void WaitAll_WaitHandle_Int ()
206                 {
207                         // -1 is infinite
208                         Assert.IsTrue (WaitHandle.WaitAll (Single, -1), "WaitAll");
209                 }
210
211                 [Test]
212                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
213                 public void WaitAll_WaitHandle_Int_Negative ()
214                 {
215                         Assert.IsTrue (WaitHandle.WaitAll (Single, -2), "WaitAll");
216                 }
217
218                 [Test]
219                 [ExpectedException (typeof (ArgumentNullException))]
220                 public void WaitAll_WaitHandleNull_TimeSpan ()
221                 {
222                         WaitHandle.WaitAll (null, Infinite);
223                 }
224
225                 [Test]
226                 [ExpectedException (typeof (NotSupportedException))]
227                 public void WaitAll_WaitHandle_TooLarge_TimeSpan ()
228                 {
229                         WaitHandle.WaitAll (TooLarge, Infinite);
230                 }
231
232                 [Test]
233                 [ExpectedException (typeof (ArgumentNullException))]
234                 public void WaitAll_WaitHandle_Empty_TimeSpan ()
235                 {
236                         WaitHandle.WaitAll (Empty, Infinite);
237                 }
238
239                 [Test]
240                 public void WaitAll_WaitHandle_TimeSpan ()
241                 {
242                         Assert.AreEqual (Timeout.Infinite, (int) Infinite.TotalMilliseconds, "Infinite");
243                         Assert.IsTrue (WaitHandle.WaitAll (Single, Infinite), "WaitAll-Infinite");
244                         Assert.IsTrue (WaitHandle.WaitAll (Single, SmallNegative), "WaitAll-SmallNegative");
245                 }
246
247                 [Test]
248                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
249                 public void WaitAll_WaitHandle_TimeSpan_Negative ()
250                 {
251                         Assert.IsTrue (WaitHandle.WaitAll (Single, Negative), "WaitAll");
252                 }
253
254                 [Test]
255                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
256                 public void WaitAll_WaitHandle_TimeSpan_MaxValue ()
257                 {
258                         Assert.IsTrue (WaitHandle.WaitAll (Single, TimeSpan.MaxValue), "WaitAll");
259                 }
260
261
262                 [Test]
263                 public void WaitOne ()
264                 {
265                         Assert.IsTrue (Single [0].WaitOne (), "WaitOne");
266                 }
267
268                 [Test]
269                 public void WaitOne_Int ()
270                 {
271                         // -1 is infinite
272                         Assert.IsTrue (Single [0].WaitOne (-1), "WaitOne");
273                 }
274
275                 [Test]
276                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
277                 public void WaitOne_Int_Negative ()
278                 {
279                         Assert.IsTrue (Single [0].WaitOne (-2), "WaitOne");
280                 }
281
282                 [Test]
283                 public void WaitOne_TimeSpan ()
284                 {
285                         Assert.AreEqual (Timeout.Infinite, (int) Infinite.TotalMilliseconds, "Infinite");
286                         Assert.IsTrue (Single [0].WaitOne (Infinite), "WaitOne-Infinite");
287                         Assert.IsTrue (Single [0].WaitOne (SmallNegative), "WaitOne-SmallNegative");
288                 }
289
290                 [Test]
291                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
292                 public void WaitOne_TimeSpan_Negative ()
293                 {
294                         Assert.IsTrue (Single [0].WaitOne (Negative), "WaitOne");
295                 }
296
297                 [Test]
298                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
299                 public void WaitOne_TimeSpan_MaxValue ()
300                 {
301                         Assert.IsTrue (Single [0].WaitOne (TimeSpan.MaxValue), "WaitOne");
302                 }
303
304                 [Test]
305                 [ExpectedException (typeof (ArgumentNullException))]
306                 public void WaitAll_Empty ()
307                 {
308                         WaitHandle.WaitAll (new WaitHandle [0]);
309                 }
310
311                 [Test]
312                 [ExpectedException (typeof (ArgumentException))]
313                 public void WaitAny_Empty ()
314                 {
315                         WaitHandle.WaitAny (new WaitHandle [0]);
316                 }
317
318                 [Test]
319                 public void InterrupedWaitAny ()
320                 {
321                         using (var m1 = new Mutex (true)) {
322                                 using (var m2 = new Mutex (true)) {
323                                         using (var done = new ManualResetEvent (false)) {
324                                                 var thread = new Thread (() =>
325                                                 {
326                                                         try {
327                                                                 WaitHandle.WaitAny (new WaitHandle [] { m1, m2 });
328                                                         } catch (ThreadInterruptedException) {
329                                                                 done.Set ();
330                                                         }
331                                                 });
332                                                 thread.Start ();
333                                                 Thread.Sleep (100); // wait a bit so the thread can enter its wait
334                                                 thread.Interrupt ();
335
336                                                 Assert.IsTrue (thread.Join (1000), "Join");
337                                                 Assert.IsTrue (done.WaitOne (1000), "done");
338
339                                                 m1.ReleaseMutex ();
340                                                 m2.ReleaseMutex ();
341                                         }
342                                 }
343                         }
344                 }
345                 
346                 [Test]
347                 public void InterrupedWaitAll ()
348                 {
349                         using (var m1 = new Mutex (true)) {
350                                 using (var m2 = new Mutex (true)) {
351                                         using (var done = new ManualResetEvent (false)) {
352                                                 var thread = new Thread (() =>
353                                                                          {
354                                                         try {
355                                                                 WaitHandle.WaitAll (new WaitHandle [] { m1, m2 });
356                                                         } catch (ThreadInterruptedException) {
357                                                                 done.Set ();
358                                                         }
359                                                 });
360                                                 thread.Start ();
361                                                 Thread.Sleep (100); // wait a bit so the thread can enter its wait
362                                                 thread.Interrupt ();
363
364                                                 Assert.IsTrue (thread.Join (1000), "Join");
365                                                 Assert.IsTrue (done.WaitOne (1000), "done");
366
367                                                 m1.ReleaseMutex ();
368                                                 m2.ReleaseMutex ();
369                                         }
370                                 }
371                         }
372                 }
373                 
374                 [Test]
375                 public void InterrupedWaitOne ()
376                 {
377                         using (var m1 = new Mutex (true)) {
378                                 using (var done = new ManualResetEvent (false)) {
379                                         var thread = new Thread (() =>
380                                                                  {
381                                                 try {
382                                                         m1.WaitOne ();
383                                                 } catch (ThreadInterruptedException) {
384                                                         done.Set ();
385                                                 }
386                                         });
387                                         thread.Start ();
388                                         Thread.Sleep (100); // wait a bit so the thread can enter its wait
389                                         thread.Interrupt ();
390
391                                         Assert.IsTrue (thread.Join (1000), "Join");
392                                         Assert.IsTrue (done.WaitOne (1000), "done");
393
394                                         m1.ReleaseMutex ();
395                                 }
396                         }
397                 }
398
399                 [Test]
400                 public void WaitOneWithAbandonedMutex ()
401                 {
402                         using (var m = new Mutex (false)) {
403                                 var thread1 = new Thread (() => {
404                                         m.WaitOne ();
405                                 });
406                                 thread1.Start ();
407                                 thread1.Join (1000);
408                                 try {
409                                         m.WaitOne ();
410                                         Assert.Fail ("Expected AbandonedMutexException");
411                                 } catch (AbandonedMutexException) {
412                                 }
413                                 // Current thread should own the Mutex now
414                                 var signalled = false;
415                                 var thread2 = new Thread (() => {
416                                         signalled = m.WaitOne (100);
417                                 });
418                                 thread2.Start ();
419                                 thread2.Join (1000);
420                                 Assert.IsFalse (signalled);
421
422                                 // Since this thread owns the Mutex releasing it shouldn't fail
423                                 m.ReleaseMutex ();
424                                 // The Mutex should now be unowned
425                                 try {
426                                         m.ReleaseMutex ();
427                                         Assert.Fail ("Expected ApplicationException");
428                                 } catch (ApplicationException) {
429                                 }
430                         }
431                 }
432
433                 [Test]
434                 public void WaitOneWithAbandonedMutexAndMultipleThreads ()
435                 {
436                         using (var m = new Mutex (true)) {
437                                 var nonAbandoned = 0;
438                                 var abandoned = 0;
439                                 var n = 0;
440                                 var threads = new List<Thread> ();
441                                 for (int i = 0; i < 50; i++) {
442                                         var thread = new Thread (() => {
443                                                 try {
444                                                         m.WaitOne ();
445                                                         nonAbandoned++;
446                                                 } catch (AbandonedMutexException) {
447                                                         abandoned++;
448                                                 }
449                                                 if (((n++) % 5) != 0)
450                                                         m.ReleaseMutex ();
451                                         });
452                                         thread.Start ();
453                                         threads.Add (thread);
454                                 }
455                                 m.ReleaseMutex ();
456                                 foreach (var thread in threads) {
457                                         if (!thread.Join (1000)) {
458                                                 Assert.Fail ("Timed out");
459                                         }
460                                 }
461                                 Assert.AreEqual (40, nonAbandoned);
462                                 Assert.AreEqual (10, abandoned);
463                         }
464                 }
465
466                 [Test]
467                 public void WaitAnyWithSecondMutexAbandoned ()
468                 {
469                         using (var m1 = new Mutex (false)) {
470                                 using (var m2 = new Mutex (false)) {
471                                         var mainProceed = false;
472                                         var thread2Proceed = false;
473                                         var thread1 = new Thread (() => {
474                                                 m2.WaitOne ();
475                                         });
476                                         var thread2 = new Thread (() => {
477                                                 m1.WaitOne ();
478                                                 mainProceed = true;
479                                                 while (!thread2Proceed) {
480                                                         Thread.Sleep (10);
481                                                 }
482                                                 m1.ReleaseMutex ();
483                                         });
484                                         thread1.Start ();
485                                         thread1.Join (1000);
486                                         thread2.Start ();
487                                         while (!mainProceed) {
488                                                 Thread.Sleep (10);
489                                         }
490                                         try {
491                                                 WaitHandle.WaitAny (new WaitHandle [] { m1, m2 });
492                                                 Assert.Fail ("Expected AbandonedMutexException");
493                                         } catch (AbandonedMutexException e) {
494                                                 Assert.AreEqual (1, e.MutexIndex);
495                                                 Assert.AreEqual (m2, e.Mutex);
496                                         } finally {
497                                                 thread2Proceed = true;
498                                                 thread2.Join (1000);
499                                         }
500
501                                         // Current thread should own the second Mutex now
502                                         var signalled = -1;
503                                         var thread3 = new Thread (() => {
504                                                 signalled = WaitHandle.WaitAny (new WaitHandle [] { m1, m2 }, 0);
505                                         });
506                                         thread3.Start ();
507                                         thread3.Join (1000);
508                                         Assert.AreEqual (0, signalled);
509
510                                         // Since this thread owns the second Mutex releasing it shouldn't fail
511                                         m2.ReleaseMutex ();
512                                         // Second Mutex should now be unowned
513                                         try {
514                                                 m2.ReleaseMutex ();
515                                                 Assert.Fail ("Expected ApplicationException");
516                                         } catch (ApplicationException) {
517                                         }
518                                         // .NET allows the first Mutex which is now abandoned to be released multiple times by this thread
519                                         m1.ReleaseMutex ();
520                                         m1.ReleaseMutex ();
521                                 }
522                         }
523                 }
524
525                 [Test]
526                 [ExpectedException (typeof (AbandonedMutexException))]
527                 public void WaitAllWithOneAbandonedMutex ()
528                 {
529                         using (var m1 = new Mutex (false)) {
530                                 using (var m2 = new Mutex (false)) {
531                                         var thread = new Thread (() => {
532                                                 m1.WaitOne ();
533                                         });
534                                         thread.Start ();
535                                         thread.Join (1000);
536                                         WaitHandle.WaitAll (new WaitHandle [] { m1, m2 });
537                                 }
538                         }
539                 }
540
541 #if MONO_FEATURE_THREAD_SUSPEND_RESUME
542                 [Test]
543                 public void WaitOneWithTimeoutAndSpuriousWake ()
544                 {
545                         /* This is to test that WaitEvent.WaitOne is not going to wait largely
546                          * more than its timeout. In this test, it shouldn't wait more than
547                          * 1500 milliseconds, with its timeout being 1000ms */
548
549                         using (ManualResetEvent mre = new ManualResetEvent (false))
550                         using (ManualResetEvent ready = new ManualResetEvent (false)) {
551                                 var thread = new Thread (() => {
552                                         ready.Set ();
553                                         mre.WaitOne (1000);
554                                 });
555
556                                 thread.Start ();
557                                 ready.WaitOne ();
558
559                                 Thread.Sleep (10); // wait a bit so we enter mre.WaitOne
560
561                                 DateTime end = DateTime.Now.AddMilliseconds (500);
562                                 while (DateTime.Now < end) {
563                                         thread.Suspend ();
564                                         thread.Resume ();
565                                 }
566
567                                 Assert.IsTrue (thread.Join (1000), "#1");
568                         }
569                 }
570
571                 [Test]
572                 public void WaitAnyWithTimeoutAndSpuriousWake ()
573                 {
574                         /* This is to test that WaitEvent.WaitAny is not going to wait largely
575                          * more than its timeout. In this test, it shouldn't wait more than
576                          * 1500 milliseconds, with its timeout being 1000ms */
577
578                         using (ManualResetEvent mre1 = new ManualResetEvent (false))
579                         using (ManualResetEvent mre2 = new ManualResetEvent (false))
580                         using (ManualResetEvent ready = new ManualResetEvent (false)) {
581                                 var thread = new Thread (() => {
582                                         ready.Set ();
583                                         WaitHandle.WaitAny (new [] { mre1, mre2 }, 1000);
584                                 });
585
586                                 thread.Start ();
587                                 ready.WaitOne ();
588
589                                 Thread.Sleep (10); // wait a bit so we enter WaitHandle.WaitAny ({mre1, mre2})
590
591                                 DateTime end = DateTime.Now.AddMilliseconds (500);
592                                 while (DateTime.Now < end) {
593                                         thread.Suspend ();
594                                         thread.Resume ();
595                                 }
596
597                                 Assert.IsTrue (thread.Join (1000), "#1");
598                         }
599                 }
600
601                 [Test]
602                 public void WaitAllWithTimeoutAndSpuriousWake ()
603                 {
604                         /* This is to test that WaitEvent.WaitAll is not going to wait largely
605                          * more than its timeout. In this test, it shouldn't wait more than
606                          * 1500 milliseconds, with its timeout being 1000ms */
607
608                         using (ManualResetEvent mre1 = new ManualResetEvent (false))
609                         using (ManualResetEvent mre2 = new ManualResetEvent (false))
610                         using (ManualResetEvent ready = new ManualResetEvent (false)) {
611                                 var thread = new Thread (() => {
612                                         ready.Set ();
613                                         WaitHandle.WaitAll (new [] { mre1, mre2 }, 1000);
614                                 });
615
616                                 thread.Start ();
617                                 ready.WaitOne ();
618
619                                 Thread.Sleep (10); // wait a bit so we enter WaitHandle.WaitAll ({mre1, mre2})
620
621                                 DateTime end = DateTime.Now.AddMilliseconds (500);
622                                 while (DateTime.Now < end) {
623                                         thread.Suspend ();
624                                         thread.Resume ();
625                                 }
626
627                                 Assert.IsTrue (thread.Join (1000), "#1");
628                         }
629                 }
630 #endif // MONO_FEATURE_THREAD_SUSPEND_RESUME
631
632                 [Test]
633                 public static void SignalAndWait()
634                 {
635                         using (var eventToSignal = new AutoResetEvent (false))
636                         using (var eventToWait = new AutoResetEvent (false))
637                         {
638                                 eventToWait.Set ();
639
640                                 Assert.IsTrue (WaitHandle.SignalAndWait (eventToSignal, eventToWait), "#1");
641                                 Assert.IsTrue (eventToSignal.WaitOne (), "#2");
642                         }
643                 }
644         }
645 }
646
647