// // WaitHandleTest.cs // // Author: // Sebastien Pouliot // // Copyright (C) 2009 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Threading; using NUnit.Framework; namespace MonoTests.System.Threading { [TestFixture] public class WaitHandleTest { TimeSpan Infinite = new TimeSpan (-10000); // -10000 ticks == -1 ms TimeSpan SmallNegative = new TimeSpan (-2); // between 0 and -1.0 (infinite) ms TimeSpan Negative = new TimeSpan (-20000); // really negative WaitHandle [] TooLarge = new Mutex [65]; WaitHandle [] Empty = new Mutex [1]; WaitHandle [] Single = new Mutex [1] { new Mutex (true) }; [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAny_WaitHandle_Null () { WaitHandle.WaitAny (null); } [Test] [ExpectedException (typeof (NotSupportedException))] public void WaitAny_WaitHandle_TooLarge () { WaitHandle.WaitAny (TooLarge); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAny_WaitHandle_Empty () { WaitHandle.WaitAny (Empty); } [Test] public void WaitAny_WaitHandle () { Assert.AreEqual (0, WaitHandle.WaitAny (Single), "WaitAny"); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAny_WaitHandleNull_Int () { WaitHandle.WaitAny (null, -1); } [Test] [ExpectedException (typeof (NotSupportedException))] public void WaitAny_WaitHandle_TooLarge_Int () { WaitHandle.WaitAny (TooLarge, -1); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAny_WaitHandle_Empty_Int () { WaitHandle.WaitAny (Empty, -1); } [Test] public void WaitAny_WaitHandle_Int () { // -1 is infinite Assert.AreEqual (0, WaitHandle.WaitAny (Single, -1), "WaitAny"); } [Test] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void WaitAny_WaitHandle_Int_Negative () { Assert.AreEqual (0, WaitHandle.WaitAny (Single, -2), "WaitAny"); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAny_WaitHandleNull_TimeSpan () { WaitHandle.WaitAny (null, Infinite); } [Test] [ExpectedException (typeof (NotSupportedException))] public void WaitAny_WaitHandle_TooLarge_TimeSpan () { WaitHandle.WaitAny (TooLarge, Infinite); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAny_WaitHandle_Empty_TimeSpan () { WaitHandle.WaitAny (Empty, Infinite); } [Test] public void WaitAny_WaitHandle_TimeSpan () { Assert.AreEqual (Timeout.Infinite, (int) Infinite.TotalMilliseconds, "Infinite"); Assert.AreEqual (0, WaitHandle.WaitAny (Single, Infinite), "WaitAny-Infinite"); Assert.AreEqual (0, WaitHandle.WaitAny (Single, SmallNegative), "WaitAny-SmallNegative"); } [Test] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void WaitAny_WaitHandle_TimeSpan_Negative () { Assert.AreEqual (0, WaitHandle.WaitAny (Single, Negative), "WaitAny"); } [Test] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void WaitAny_WaitHandle_TimeSpan_MaxValue () { Assert.AreEqual (0, WaitHandle.WaitAny (Single, TimeSpan.MaxValue), "WaitAny"); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAll_WaitHandle_Null () { WaitHandle.WaitAll (null); } [Test] [ExpectedException (typeof (NotSupportedException))] public void WaitAll_WaitHandle_TooLarge () { WaitHandle.WaitAll (TooLarge); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAll_WaitHandle_Empty () { WaitHandle.WaitAll (Empty); } [Test] public void WaitAll_WaitHandle () { Assert.IsTrue (WaitHandle.WaitAll (Single), "WaitAll"); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAll_WaitHandleNull_Int () { WaitHandle.WaitAll (null, -1); } [Test] [ExpectedException (typeof (NotSupportedException))] public void WaitAll_WaitHandle_TooLarge_Int () { WaitHandle.WaitAll (TooLarge, -1); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAll_WaitHandle_Empty_Int () { WaitHandle.WaitAll (Empty, -1); } [Test] public void WaitAll_WaitHandle_Int () { // -1 is infinite Assert.IsTrue (WaitHandle.WaitAll (Single, -1), "WaitAll"); } [Test] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void WaitAll_WaitHandle_Int_Negative () { Assert.IsTrue (WaitHandle.WaitAll (Single, -2), "WaitAll"); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAll_WaitHandleNull_TimeSpan () { WaitHandle.WaitAll (null, Infinite); } [Test] [ExpectedException (typeof (NotSupportedException))] public void WaitAll_WaitHandle_TooLarge_TimeSpan () { WaitHandle.WaitAll (TooLarge, Infinite); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAll_WaitHandle_Empty_TimeSpan () { WaitHandle.WaitAll (Empty, Infinite); } [Test] public void WaitAll_WaitHandle_TimeSpan () { Assert.AreEqual (Timeout.Infinite, (int) Infinite.TotalMilliseconds, "Infinite"); Assert.IsTrue (WaitHandle.WaitAll (Single, Infinite), "WaitAll-Infinite"); Assert.IsTrue (WaitHandle.WaitAll (Single, SmallNegative), "WaitAll-SmallNegative"); } [Test] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void WaitAll_WaitHandle_TimeSpan_Negative () { Assert.IsTrue (WaitHandle.WaitAll (Single, Negative), "WaitAll"); } [Test] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void WaitAll_WaitHandle_TimeSpan_MaxValue () { Assert.IsTrue (WaitHandle.WaitAll (Single, TimeSpan.MaxValue), "WaitAll"); } [Test] public void WaitOne () { Assert.IsTrue (Single [0].WaitOne (), "WaitOne"); } [Test] public void WaitOne_Int () { // -1 is infinite Assert.IsTrue (Single [0].WaitOne (-1), "WaitOne"); } [Test] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void WaitOne_Int_Negative () { Assert.IsTrue (Single [0].WaitOne (-2), "WaitOne"); } [Test] public void WaitOne_TimeSpan () { Assert.AreEqual (Timeout.Infinite, (int) Infinite.TotalMilliseconds, "Infinite"); Assert.IsTrue (Single [0].WaitOne (Infinite), "WaitOne-Infinite"); Assert.IsTrue (Single [0].WaitOne (SmallNegative), "WaitOne-SmallNegative"); } [Test] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void WaitOne_TimeSpan_Negative () { Assert.IsTrue (Single [0].WaitOne (Negative), "WaitOne"); } [Test] [ExpectedException (typeof (ArgumentOutOfRangeException))] public void WaitOne_TimeSpan_MaxValue () { Assert.IsTrue (Single [0].WaitOne (TimeSpan.MaxValue), "WaitOne"); } [Test] [ExpectedException (typeof (ArgumentNullException))] public void WaitAll_Empty () { WaitHandle.WaitAll (new WaitHandle [0]); } [Test] [ExpectedException (typeof (ArgumentException))] public void WaitAny_Empty () { WaitHandle.WaitAny (new WaitHandle [0]); } [Test] public void InterrupedWaitAny () { using (var m1 = new Mutex (true)) { using (var m2 = new Mutex (true)) { using (var done = new ManualResetEvent (false)) { var thread = new Thread (() => { try { WaitHandle.WaitAny (new WaitHandle [] { m1, m2 }); } catch (ThreadInterruptedException) { done.Set (); } }); thread.Start (); Thread.Sleep (100); // wait a bit so the thread can enter its wait thread.Interrupt (); Assert.IsTrue (thread.Join (1000), "Join"); Assert.IsTrue (done.WaitOne (1000), "done"); m1.ReleaseMutex (); m2.ReleaseMutex (); } } } } [Test] public void InterrupedWaitAll () { using (var m1 = new Mutex (true)) { using (var m2 = new Mutex (true)) { using (var done = new ManualResetEvent (false)) { var thread = new Thread (() => { try { WaitHandle.WaitAll (new WaitHandle [] { m1, m2 }); } catch (ThreadInterruptedException) { done.Set (); } }); thread.Start (); Thread.Sleep (100); // wait a bit so the thread can enter its wait thread.Interrupt (); Assert.IsTrue (thread.Join (1000), "Join"); Assert.IsTrue (done.WaitOne (1000), "done"); m1.ReleaseMutex (); m2.ReleaseMutex (); } } } } [Test] public void InterrupedWaitOne () { using (var m1 = new Mutex (true)) { using (var done = new ManualResetEvent (false)) { var thread = new Thread (() => { try { m1.WaitOne (); } catch (ThreadInterruptedException) { done.Set (); } }); thread.Start (); Thread.Sleep (100); // wait a bit so the thread can enter its wait thread.Interrupt (); Assert.IsTrue (thread.Join (1000), "Join"); Assert.IsTrue (done.WaitOne (1000), "done"); m1.ReleaseMutex (); } } } [Test] public void WaitOneWithAbandonedMutex () { using (var m = new Mutex (false)) { var thread1 = new Thread (() => { m.WaitOne (); }); thread1.Start (); thread1.Join (1000); try { m.WaitOne (); Assert.Fail ("Expected AbandonedMutexException"); } catch (AbandonedMutexException) { } // Current thread should own the Mutex now var signalled = false; var thread2 = new Thread (() => { signalled = m.WaitOne (100); }); thread2.Start (); thread2.Join (1000); Assert.IsFalse (signalled); // Since this thread owns the Mutex releasing it shouldn't fail m.ReleaseMutex (); // The Mutex should now be unowned try { m.ReleaseMutex (); Assert.Fail ("Expected ApplicationException"); } catch (ApplicationException) { } } } [Test] public void WaitOneWithAbandonedMutexAndMultipleThreads () { using (var m = new Mutex (true)) { var nonAbandoned = 0; var abandoned = 0; var n = 0; var threads = new List (); for (int i = 0; i < 50; i++) { var thread = new Thread (() => { try { m.WaitOne (); nonAbandoned++; } catch (AbandonedMutexException) { abandoned++; } if (((n++) % 5) != 0) m.ReleaseMutex (); }); thread.Start (); threads.Add (thread); } m.ReleaseMutex (); foreach (var thread in threads) { if (!thread.Join (1000)) { Assert.Fail ("Timed out"); } } Assert.AreEqual (40, nonAbandoned); Assert.AreEqual (10, abandoned); } } [Test] public void WaitAnyWithSecondMutexAbandoned () { using (var m1 = new Mutex (false)) { using (var m2 = new Mutex (false)) { var mainProceed = false; var thread2Proceed = false; var thread1 = new Thread (() => { m2.WaitOne (); }); var thread2 = new Thread (() => { m1.WaitOne (); mainProceed = true; while (!thread2Proceed) { Thread.Sleep (10); } m1.ReleaseMutex (); }); thread1.Start (); thread1.Join (1000); thread2.Start (); while (!mainProceed) { Thread.Sleep (10); } try { WaitHandle.WaitAny (new WaitHandle [] { m1, m2 }); Assert.Fail ("Expected AbandonedMutexException"); } catch (AbandonedMutexException e) { Assert.AreEqual (1, e.MutexIndex); Assert.AreEqual (m2, e.Mutex); } finally { thread2Proceed = true; thread2.Join (1000); } // Current thread should own the second Mutex now var signalled = -1; var thread3 = new Thread (() => { signalled = WaitHandle.WaitAny (new WaitHandle [] { m1, m2 }, 0); }); thread3.Start (); thread3.Join (1000); Assert.AreEqual (0, signalled); // Since this thread owns the second Mutex releasing it shouldn't fail m2.ReleaseMutex (); // Second Mutex should now be unowned try { m2.ReleaseMutex (); Assert.Fail ("Expected ApplicationException"); } catch (ApplicationException) { } // .NET allows the first Mutex which is now abandoned to be released multiple times by this thread m1.ReleaseMutex (); m1.ReleaseMutex (); } } } [Test] [ExpectedException (typeof (AbandonedMutexException))] public void WaitAllWithOneAbandonedMutex () { using (var m1 = new Mutex (false)) { using (var m2 = new Mutex (false)) { var thread = new Thread (() => { m1.WaitOne (); }); thread.Start (); thread.Join (1000); WaitHandle.WaitAll (new WaitHandle [] { m1, m2 }); } } } #if MONO_FEATURE_THREAD_SUSPEND_RESUME [Test] public void WaitOneWithTimeoutAndSpuriousWake () { /* This is to test that WaitEvent.WaitOne is not going to wait largely * more than its timeout. In this test, it shouldn't wait more than * 1500 milliseconds, with its timeout being 1000ms */ using (ManualResetEvent mre = new ManualResetEvent (false)) using (ManualResetEvent ready = new ManualResetEvent (false)) { var thread = new Thread (() => { ready.Set (); mre.WaitOne (1000); }); thread.Start (); ready.WaitOne (); Thread.Sleep (10); // wait a bit so we enter mre.WaitOne DateTime end = DateTime.Now.AddMilliseconds (500); while (DateTime.Now < end) { thread.Suspend (); thread.Resume (); } Assert.IsTrue (thread.Join (1000), "#1"); } } [Test] public void WaitAnyWithTimeoutAndSpuriousWake () { /* This is to test that WaitEvent.WaitAny is not going to wait largely * more than its timeout. In this test, it shouldn't wait more than * 1500 milliseconds, with its timeout being 1000ms */ using (ManualResetEvent mre1 = new ManualResetEvent (false)) using (ManualResetEvent mre2 = new ManualResetEvent (false)) using (ManualResetEvent ready = new ManualResetEvent (false)) { var thread = new Thread (() => { ready.Set (); WaitHandle.WaitAny (new [] { mre1, mre2 }, 1000); }); thread.Start (); ready.WaitOne (); Thread.Sleep (10); // wait a bit so we enter WaitHandle.WaitAny ({mre1, mre2}) DateTime end = DateTime.Now.AddMilliseconds (500); while (DateTime.Now < end) { thread.Suspend (); thread.Resume (); } Assert.IsTrue (thread.Join (1000), "#1"); } } [Test] public void WaitAllWithTimeoutAndSpuriousWake () { /* This is to test that WaitEvent.WaitAll is not going to wait largely * more than its timeout. In this test, it shouldn't wait more than * 1500 milliseconds, with its timeout being 1000ms */ using (ManualResetEvent mre1 = new ManualResetEvent (false)) using (ManualResetEvent mre2 = new ManualResetEvent (false)) using (ManualResetEvent ready = new ManualResetEvent (false)) { var thread = new Thread (() => { ready.Set (); WaitHandle.WaitAll (new [] { mre1, mre2 }, 1000); }); thread.Start (); ready.WaitOne (); Thread.Sleep (10); // wait a bit so we enter WaitHandle.WaitAll ({mre1, mre2}) DateTime end = DateTime.Now.AddMilliseconds (500); while (DateTime.Now < end) { thread.Suspend (); thread.Resume (); } Assert.IsTrue (thread.Join (1000), "#1"); } } #endif // MONO_FEATURE_THREAD_SUSPEND_RESUME [Test] public static void SignalAndWait() { using (var eventToSignal = new AutoResetEvent (false)) using (var eventToWait = new AutoResetEvent (false)) { eventToWait.Set (); Assert.IsTrue (WaitHandle.SignalAndWait (eventToSignal, eventToWait), "#1"); Assert.IsTrue (eventToSignal.WaitOne (), "#2"); } } } }