2008-03-05 09:52:00 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Microsoft Research Singularity
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// Note:
|
|
|
|
//
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Threading;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
using Microsoft.Singularity;
|
2008-03-05 09:52:00 -05:00
|
|
|
using Microsoft.Singularity.UnitTest;
|
|
|
|
|
|
|
|
namespace Microsoft.Singularity.Applications
|
|
|
|
{
|
|
|
|
/// <remarks>
|
|
|
|
/// A test of Monitor.PulseAll. A collection of threads waits on
|
|
|
|
/// a single monitor for a pulse all.
|
|
|
|
/// </remarks>
|
2008-11-17 18:29:00 -05:00
|
|
|
[TestClass]
|
|
|
|
public class PulseAllTest : TestClass
|
|
|
|
{
|
|
|
|
[TestMethod]
|
|
|
|
public void FewThreadsTest()
|
|
|
|
{
|
|
|
|
(new Pulser(8, 2, Expect)).RunTest();
|
|
|
|
}
|
|
|
|
|
|
|
|
[TestMethod]
|
|
|
|
public void ManyThreadsTest()
|
|
|
|
{
|
|
|
|
(new Pulser(50, 2, Expect)).RunTest();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal sealed class Pulser
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
TestLog Expect;
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
int waiterCount = 50;
|
|
|
|
int timeoutSeconds = 2;
|
|
|
|
|
|
|
|
int passedBarrier = 0;
|
|
|
|
bool waiterRunning = false;
|
|
|
|
volatile bool timedOut = false;
|
|
|
|
volatile bool stop = false;
|
|
|
|
volatile int generation = 0;
|
|
|
|
|
|
|
|
ManualResetEvent! controllerReady = new ManualResetEvent(false);
|
|
|
|
ManualResetEvent! finishedEvent = new ManualResetEvent(false);
|
|
|
|
AutoResetEvent! barrierEvent = new AutoResetEvent(false);
|
|
|
|
object! monitor = new object();
|
|
|
|
bool []! visited;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
|
|
|
|
public Pulser(int threadCount, int timeoutSeconds, TestLog expect)
|
|
|
|
{
|
|
|
|
this.waiterCount = threadCount;
|
|
|
|
this.visited = new bool [threadCount];
|
|
|
|
this.timeoutSeconds = timeoutSeconds;
|
|
|
|
this.Expect = expect;
|
|
|
|
}
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
private static void Yield()
|
|
|
|
{
|
|
|
|
#if SINGULARITY
|
|
|
|
Thread.Yield();
|
|
|
|
#else
|
|
|
|
Thread.Sleep(0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
public void WatchdogThreadMain()
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
TimeSpan delta = TimeSpan.FromMilliseconds(500);
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
int now = this.generation;
|
|
|
|
int last = 0;
|
|
|
|
do {
|
|
|
|
last = now;
|
|
|
|
if (finishedEvent.WaitOne(delta)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Yield();
|
|
|
|
now = this.generation;
|
2008-11-17 18:29:00 -05:00
|
|
|
Expect.NotEqual(last, now, "progress was made in the last cycle");
|
|
|
|
} while (true);
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
public void ControllerThreadMain()
|
|
|
|
{
|
|
|
|
const int iterations = 1000;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
const int YieldFudge = 150;
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
// Signal worker threads to start
|
|
|
|
controllerReady.Set();
|
|
|
|
Yield();
|
|
|
|
|
|
|
|
for (int i = 1; i <= iterations; i++) {
|
|
|
|
barrierEvent.WaitOne();
|
|
|
|
lock (this.monitor) {
|
|
|
|
// Reset check state
|
|
|
|
this.passedBarrier = 0;
|
|
|
|
for (int j = 0; j < visited.Length; j++) {
|
|
|
|
visited[j] = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set stop flag if end is reached
|
|
|
|
this.stop = (i == iterations);
|
|
|
|
|
|
|
|
// Wake up waiters
|
|
|
|
Monitor.PulseAll(this.monitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finishedEvent.Set();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void WaiterThreadMain()
|
|
|
|
{
|
|
|
|
while (!stop) {
|
|
|
|
Monitor.Enter(this.monitor);
|
|
|
|
try {
|
2008-11-17 18:29:00 -05:00
|
|
|
Expect.False(waiterRunning, "Only one waiter is in the monitor");
|
2008-03-05 09:52:00 -05:00
|
|
|
waiterRunning = true;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
Expect.False(this.visited[this.passedBarrier],
|
|
|
|
"Thread is before the barrier");
|
2008-03-05 09:52:00 -05:00
|
|
|
this.visited[this.passedBarrier] = true;
|
|
|
|
|
|
|
|
this.passedBarrier++;
|
|
|
|
this.generation++;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
Expect.LessOrEqual(this.passedBarrier, waiterCount,
|
|
|
|
"Not too many waiters passed the barrier");
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
if (this.passedBarrier == this.waiterCount) {
|
|
|
|
barrierEvent.Set();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.waiterRunning = false;
|
|
|
|
Monitor.Wait(this.monitor);
|
|
|
|
this.waiterRunning = true;
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
this.waiterRunning = false;
|
|
|
|
Monitor.Exit(this.monitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void RunTest()
|
|
|
|
{
|
|
|
|
Thread watchdog =
|
|
|
|
new Thread(new ThreadStart(WatchdogThreadMain));
|
|
|
|
Thread controller =
|
|
|
|
new Thread(new ThreadStart(ControllerThreadMain));
|
|
|
|
|
|
|
|
watchdog.Start();
|
|
|
|
controller.Start();
|
|
|
|
|
|
|
|
controllerReady.WaitOne();
|
|
|
|
for (int i = 0; i < waiterCount; i++) {
|
|
|
|
Thread t = new Thread(new ThreadStart(WaiterThreadMain));
|
|
|
|
t.Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!controller.Join(TimeSpan.FromMilliseconds(100))) {
|
2008-11-17 18:29:00 -05:00
|
|
|
Expect.False(this.timedOut, "Completed before timeout");
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
watchdog.Join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|