Example #1
0
        public void TestParallelStateTransitions()
        {
            // The intention here is to cause maximum contention on the Status<> class's methods
            // and thereby provide sufficient confidence that the class provides non-blocking,
            // thread-safe state transitions.
            var control = new
                {
                    NumberOfThreadsPerState = 10,
                    WaitPulseMilliseconds = 200,
                    MainThreadWaitTimeBeforeShutdown = TimeSpan.FromSeconds(10)
                };

            Status<TestStates> state = new Status<TestStates>(default(TestStates));
            int onThreadsStarted = 0;
            int offThreadsStarted = 0;
            int undecidedThreadsStarted = 0;

            int transitionsOn = 0;
            int transitionsOff = 0;
            int transitionsUndecided = 0;
            int transitionsOnStateDone = 0;
            int transitionsOffStateDone = 0;
            int transitionsDone = 0;

            for (int i = 0; i < control.NumberOfThreadsPerState; i++)
            {
                // These background jobs transition the state from TestStates.Undecided
                // to TestStates.On - when successful it increments transitionsOn.
                ThreadPool.QueueUserWorkItem(new WaitCallback((unused_state) =>
                    {
                        state.SpinWaitForState(TestStates.Undecided, () => Thread.Sleep(control.WaitPulseMilliseconds));
                        Interlocked.Increment(ref onThreadsStarted);
                        while (state.IsLessThan(TestStates.ShutdownSignaled))
                        {
                            state.TryTransition(TestStates.On, TestStates.Undecided, () =>
                                {
                                    Interlocked.Increment(ref transitionsOn);
                                });
                        }
                        state.TryTransition(TestStates.OnStateDone, TestStates.ShutdownSignaled, () =>
                            {
                                Interlocked.Increment(ref transitionsOnStateDone);
                            });
                    }));
                // These background jobs transition the state from TestStates.On
                // to TestStates.Off - when successful it increments transitionsOff.
                ThreadPool.QueueUserWorkItem(new WaitCallback((unused_state) =>
                    {
                        state.SpinWaitForState(TestStates.On, () => Thread.Sleep(control.WaitPulseMilliseconds));
                        Interlocked.Increment(ref offThreadsStarted);

                        while(state.IsLessThan(TestStates.OnStateDone))
                        {
                            state.TryTransition(TestStates.Off, TestStates.On, () =>
                                {
                                    Interlocked.Increment(ref transitionsOff);
                                });
                        }
                        state.TryTransition(TestStates.OffStateDone, TestStates.OnStateDone, () =>
                        {
                            Interlocked.Increment(ref transitionsOffStateDone);
                        });
                    }));
                // These background jobs transition the state from TestStates.Off
                // to TestStates.Undecided - when successful it increments transitionsUndecided.
                ThreadPool.QueueUserWorkItem(new WaitCallback((unused_state) =>
                {
                    state.SpinWaitForState(TestStates.Off, () => Thread.Sleep(control.WaitPulseMilliseconds));
                    Interlocked.Increment(ref undecidedThreadsStarted);

                    while (state.IsLessThan(TestStates.OffStateDone))
                    {
                        state.TryTransition(TestStates.Undecided, TestStates.Off, () =>
                        {
                            Interlocked.Increment(ref transitionsUndecided);
                        });
                    }

                    state.TryTransition(TestStates.Done, TestStates.OffStateDone, () =>
                    {
                        Interlocked.Increment(ref transitionsDone);
                    });
                }));
            }

            // Signal the first group of threads that they should start.
            state.SetState(TestStates.Undecided);

            // Wait the prescribed amount of time...
            Thread.Sleep(control.MainThreadWaitTimeBeforeShutdown);
            // Signal the shutdown...
            state.SetState(TestStates.ShutdownSignaled);
            // Wait for background threads to shutdown...
            state.SpinWaitForState(TestStates.Done, () => Thread.Sleep(control.WaitPulseMilliseconds));

            Assert.IsTrue(Thread.VolatileRead(ref transitionsOn) >= Thread.VolatileRead(ref transitionsOff));
            Assert.IsTrue(Thread.VolatileRead(ref transitionsOff) >= Thread.VolatileRead(ref transitionsUndecided));
            Assert.AreEqual(1, Thread.VolatileRead(ref transitionsOnStateDone));
            Assert.AreEqual(1, Thread.VolatileRead(ref transitionsOffStateDone));
            Assert.AreEqual(1, Thread.VolatileRead(ref transitionsDone));

            Console.WriteLine(String.Concat("Threads transitioninig to On: ", onThreadsStarted, ", transitions = ", transitionsOn));
            Console.WriteLine(String.Concat("Threads transitioninig to Off: ", offThreadsStarted, ", transitions = ", transitionsOff));
            Console.WriteLine(String.Concat("Threads transitioninig to Undecided: ", undecidedThreadsStarted, ", transitions = ", transitionsUndecided));
        }