singrdk/base/Applications/Tests/Scheduler/Threading/TestManualResetEvent.cs

456 lines
14 KiB
C#
Raw Permalink Normal View History

2008-11-17 18:29:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
using System;
using System.Threading;
using Microsoft.Singularity.UnitTest;
namespace Microsoft.Singularity.Applications
{
/// <summary>
/// Test cases for manual reset event class
/// </summary>
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();
}
/// <summary>
/// After the event is reset, test to make sure WaitOne timeout in the same thread
/// </summary>
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");
}
/// <summary>
/// After the event is set, test to make sure WaitOne succeed in the same thread
/// </summary>
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");
}
/// <summary>
/// Set the event, and then WaitOne from different thread. The wait should succeed.
/// </summary>
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");
}
/// <summary>
/// Reset the event and then WaitOne from different thread. The wait should timeout
/// </summary>
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");
}
/// <summary>
/// test multiple threads WaitOne() on the same event which is initially set
/// </summary>
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");
}
/// <summary>
/// test multiple threads WaitOne() on the same event which is initially reset
/// the master thread then
/// </summary>
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");
}
/// <summary>
/// Test dead lock situation where two threads each waits the other to set an event
/// </summary>
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");
}
/// <summary>
/// Test the scenario where we dispose an event object then wait on it.
/// </summary>
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");
}
/// <summary>
/// Thread entry point to test waiting on an event.
/// increment the counter only if the wait timeout
/// </summary>
private static void NoSignalWaiterMain()
{
bool ret = evt.WaitOne(trivialTestWaitTime);
if (!ret) {
// increment counter if the wait timeout
Interlocked.Increment(ref counter);
}
}
/// <summary>
/// Thread entry point to test waiting on an event.
/// increment readyCount before wait begins and increment counter after wait succeeds
/// </summary>
private static void WaiterMain()
{
Interlocked.Increment(ref readyCount);
evt.WaitOne();
Interlocked.Increment(ref counter);
}
/// <summary>
/// Thread entry point to test dead lock
/// increment counter if wait timeout
/// </summary>
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);
}
}
/// <summary>
/// Thread entry point to test dead lock
/// increment counter if wait timeout
/// </summary>
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);
}
}
}
}