/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // using System; using System.Threading; using Microsoft.Singularity.UnitTest; namespace Microsoft.Singularity.Applications { /// /// Test cases for manual reset event class /// internal sealed class TestManualResetEvent { internal static TestLog Expect; private const int MaxWaiters = 16; private static ManualResetEvent evt, evt1, evt2, evt3, evt4; private static readonly TimeSpan trivialTestWaitTime = TimeSpan.FromMilliseconds(100); private static int counter; private static int readyCount; internal static void Run() { TestTrivialReset(); TestTrivialSet(); TestThreadSet(); TestThreadReset(); TestMultipleWaitOneWithInitialSet(); TestMultipleWaitOneWithInitialReset(); TestDeadlock(); ///TODO: harden kernel code so it doesn't crash on this test, then enable the test //TestDispose(); } /// /// After the event is reset, test to make sure WaitOne timeout in the same thread /// private static void TestTrivialReset() { Console.Write(" test trivial reset"); // // reset by constructor // evt = new ManualResetEvent(false); Console.Write('.'); bool ret = evt.WaitOne(trivialTestWaitTime); if (ret) { Expect.Fail("wait should timeout"); return; } Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (ret) { Expect.Fail("wait should timeout again"); return; } evt.Close(); // // reset by constructor and Reset() // evt = new ManualResetEvent(false); evt.Reset(); Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (ret) { Expect.Fail("wait should timeout"); return; } Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (ret) { Expect.Fail("wait should timeout again"); return; } evt.Close(); // // set by constructor and then explicitly Reset() // evt = new ManualResetEvent(true); evt.Reset(); Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (ret) { Expect.Fail("wait should timeout"); return; } Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (ret) { Expect.Fail("wait should timeout again"); return; } evt.Close(); // // Set() and Reset() several times // evt = new ManualResetEvent(true); evt.Set(); evt.Reset(); evt.Set(); evt.Reset(); Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (ret) { Expect.Fail("wait should timeout"); return; } Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (ret) { Expect.Fail("wait should timeout again"); return; } evt.Close(); evt = null; Console.WriteLine("OK"); } /// /// After the event is set, test to make sure WaitOne succeed in the same thread /// private static void TestTrivialSet() { Console.Write(" test Trivial set "); // // set by constructor // evt = new ManualResetEvent(true); Console.Write('.'); bool ret = evt.WaitOne(trivialTestWaitTime); if (!ret) { Expect.Fail("wait should succeed"); return; } Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (!ret) { Expect.Fail("wait should succeed again"); return; } evt.Close(); // // set by constructor then explicitly Set() // evt = new ManualResetEvent(true); evt.Set(); Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (!ret) { Expect.Fail("wait should succeed"); return; } Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (!ret) { Expect.Fail("wait should succeed again"); return; } evt.Close(); // // reset by constructor then explicitly Set() // evt = new ManualResetEvent(false); evt.Set(); Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (!ret) { Expect.Fail("wait should succeed"); return; } Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (!ret) { Expect.Fail("wait should succeed again"); return; } evt.Close(); // // reset and set several times // evt = new ManualResetEvent(false); evt.Set(); evt.Reset(); evt.Set(); evt.Reset(); evt.Set(); Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (!ret) { Expect.Fail("wait should succeed"); return; } Console.Write('.'); ret = evt.WaitOne(trivialTestWaitTime); if (!ret) { Expect.Fail("wait should succeed again"); return; } evt.Close(); evt = null; Console.WriteLine("OK"); } /// /// Set the event, and then WaitOne from different thread. The wait should succeed. /// private static void TestThreadSet() { Console.Write(" test thread set "); evt = new ManualResetEvent(true); Thread t = new Thread(new ThreadStart(WaiterMain)); t.Start(); t.Join(); Console.Write('.'); evt.WaitOne(); evt.Reset(); Console.Write('.'); bool ret = evt.WaitOne(trivialTestWaitTime); if (ret) { Expect.Fail("wait should timeout"); return; } evt.Close(); evt = null; Console.WriteLine("OK"); } /// /// Reset the event and then WaitOne from different thread. The wait should timeout /// private static void TestThreadReset() { Console.Write(" test thread reset "); evt = new ManualResetEvent(false); counter = 0; Thread t = new Thread(new ThreadStart(NoSignalWaiterMain)); t.Start(); Console.Write('.'); t.Join(); // wait should timeout and therefore the counter should be 1 if (Thread.VolatileRead(ref counter) != 1) { Expect.Fail("wait should timeout"); return; } Console.Write('.'); evt.Set(); evt.WaitOne(); evt.Close(); evt = null; Console.WriteLine("OK"); } /// /// test multiple threads WaitOne() on the same event which is initially set /// private static void TestMultipleWaitOneWithInitialSet() { Console.Write(" test multiple wait one with initial set "); evt = new ManualResetEvent(true); for (int n = 1; n < MaxWaiters; n++) { Console.Write('.'); Thread[] threads = new Thread[n]; for (int i = 0; i < n; i++) { threads[i] = new Thread(new ThreadStart(WaiterMain)); ((!)threads[i]).Start(); } for (int i = 0; i < n; i++) { ((!)threads[i]).Join(); } } evt.Close(); evt = null; Console.WriteLine("OK"); } /// /// test multiple threads WaitOne() on the same event which is initially reset /// the master thread then /// private static void TestMultipleWaitOneWithInitialReset() { Console.Write(" test multiple wait one with initial reset "); evt = new ManualResetEvent(false); for (int n = 1; n < MaxWaiters; n++) { // reset counter counter = 0; readyCount = 0; // reset event evt.Reset(); Console.Write('.'); Thread[] threads = new Thread[n]; for (int i = 0; i < n; i++) { threads[i] = new Thread(new ThreadStart(WaiterMain)); ((!)threads[i]).Start(); Thread.Yield(); } // wait for all threads to be ready while (true) { if (Thread.VolatileRead(ref readyCount) == n) { break; } Thread.Yield(); } // make sure no wait leaking Expect.Equal(Thread.VolatileRead(ref counter), 0, "Detected event wait leaking!"); evt.Set(); for (int i = 0; i < n; i++) { ((!)threads[i]).Join(); } } evt.Close(); evt = null; Console.WriteLine("OK"); } /// /// Test dead lock situation where two threads each waits the other to set an event /// private static void TestDeadlock() { Console.Write(" test deadlock ..."); evt = new ManualResetEvent(false); evt3 = new ManualResetEvent(false); evt4 = new ManualResetEvent(false); counter = 0; Thread t1 = new Thread(new ThreadStart(DeadlockMain1)); Thread t2 = new Thread(new ThreadStart(DeadlockMain2)); t1.Start(); t2.Start(); evt3.WaitOne(); evt4.WaitOne(); evt.Set(); t1.Join(); t2.Join(); // wait in both thread should timeout, therefore the counter should be 2 if (Thread.VolatileRead(ref counter) != 2) { Expect.Fail("expecting deadlock"); return; } evt.Close(); evt = null; Console.WriteLine("OK"); } /// /// Test the scenario where we dispose an event object then wait on it. /// private static void TestDispose() { Console.Write(" test dispose "); evt = new ManualResetEvent(false); evt.Close(); try { evt.WaitOne(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); return; } Expect.Fail("expecting exception on disposed object"); } /// /// Thread entry point to test waiting on an event. /// increment the counter only if the wait timeout /// private static void NoSignalWaiterMain() { bool ret = evt.WaitOne(trivialTestWaitTime); if (!ret) { // increment counter if the wait timeout Interlocked.Increment(ref counter); } } /// /// Thread entry point to test waiting on an event. /// increment readyCount before wait begins and increment counter after wait succeeds /// private static void WaiterMain() { Interlocked.Increment(ref readyCount); evt.WaitOne(); Interlocked.Increment(ref counter); } /// /// Thread entry point to test dead lock /// increment counter if wait timeout /// private static void DeadlockMain1() { evt1 = new ManualResetEvent(false); evt3.Set(); evt.WaitOne(); bool ret = evt2.WaitOne(TimeSpan.FromSeconds(1)); if (!ret) { // increment counter if the wait timeout Interlocked.Increment(ref counter); } } /// /// Thread entry point to test dead lock /// increment counter if wait timeout /// private static void DeadlockMain2() { evt2 = new ManualResetEvent(false); evt4.Set(); evt.WaitOne(); bool ret = evt1.WaitOne(TimeSpan.FromSeconds(1)); if (!ret) { // increment counter if the wait timeout Interlocked.Increment(ref counter); } } } }