public void TestCheckingWaitPulseOwnership()
        {
            var monitor = new MonitorSimple();
            var finished = 0;

            AssertEx.Throws<SynchronizationLockException>(() => { monitor.Wait(); });
            AssertEx.Throws<SynchronizationLockException>(() => { monitor.Pulse(); });
            AssertEx.Throws<SynchronizationLockException>(() => { monitor.PulseAll(); });

            var th = new Thread(() =>
            {
                monitor.Enter();
                while (finished == 0) { }
            }) { IsBackground = true };
            th.Start();
            WaitAlittle();

            // other thread has lock
            AssertEx.Throws<SynchronizationLockException>(() => { monitor.Wait(); });
            AssertEx.Throws<SynchronizationLockException>(() => { monitor.Pulse(); });
            AssertEx.Throws<SynchronizationLockException>(() => { monitor.PulseAll(); });

            Thread.VolatileWrite(ref finished, 1);
        }
        public void TestWait()
        {
            var e = new ManualResetEvent(false);
            var m = new MonitorSimple();

            var testValue = 0;

            var th1 = new Thread(() =>
            {
                m.Enter();
                testValue = 1;
                e.WaitOne();

                m.Wait();
                testValue = 2;
                m.Exit();
            }) { Name = "th1" };

            var th2 = new Thread(() =>
            {
                m.Enter();
                testValue = 100;
                m.Exit();
            }) { Name = "th2" };

            th1.Start();

            WaitAlittle();

            th2.Start();

            WaitAlittle();

            Assert.AreEqual(1, testValue);

            e.Set();

            WaitAlittle();

            Assert.AreEqual(100, testValue);

            m.Enter();
            m.PulseAll();
            m.Exit();

            WaitAlittle();

            Assert.AreEqual(2, testValue);
        }
        public void TestStressWait()
        {
            const int iterations = 1000000;
            const int doHardWorkBeforeEachNthProducing = iterations / 100;
            var threadCount = Environment.ProcessorCount;

            var src = 0;
            var dst = 0;
            var addingCompleted = false;

            var m = new MonitorSimple();

            var readers = RunSimultanously(threadCount, () =>
            {
                while (true)
                {
                    using (m.GetLocked())
                    using (m.GetLocked())
                    using (m.GetLocked())
                    {
                        while (src == 0 && !addingCompleted)
                            m.Wait();

                        if (addingCompleted && src == 0)
                            break;

                        src = src - 1;
                        dst = dst + 1;
                    }
                }
            }, false);

            RunSimultanously(threadCount, () =>
            {
                for (var i = 0; i < iterations; i++)
                {
                    if (i % doHardWorkBeforeEachNthProducing == 0)
                        DoHardWork();

                    using (m.GetLocked())
                    using (m.GetLocked()) //for extra stressing
                    {
                        src = src + 1;

                        // such a weird construct is for extra stressing
                        if (i % 2 == 0)
                            m.Pulse();
                        else
                            m.PulseAll();
                    }
                }
            }, true);

            using (m.GetLocked())
            {
                addingCompleted = true;
                m.PulseAll();
            }

            readers.ForEach(r => r.Join());

            var expected = Enumerable.Repeat(1, iterations).Sum() * threadCount;

            Assert.AreEqual(expected, dst);
        }