public void TestWaitThrowsIfRecursiveEnter()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                using (var waiter2 = testInst.Enter(10))
                {
                    using (var waiter = testInst.Enter(10))
                    {
                        waiter.Wait();
                    }
                }
            }
        }
        public void TestNotificationReceived()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                int result = 0;
                var task   = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(60000))
                    {
                        if (waiter.Wait())
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);
                Assert.AreEqual(0, Volatile.Read(ref result));

                lock (syncObj)
                {
                    testInst.Pulse();
                }
                TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result));
            }
        }
        public void TestPredicateCalledOnTimeout()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                int result = 0;
                int state  = 0;
                int called = 0;
                var task   = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(100))
                    {
                        if (waiter.Wait((s) => { Interlocked.Increment(ref called); return(Volatile.Read(ref state) > 0); }, new object()))
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);
                TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref result));
                TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref called));
            }
        }
        public void TestExceptionPassedFromPredicate()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                try
                {
                    using (var waiter = testInst.Enter(100000))
                    {
                        try
                        {
                            object state  = new object();
                            bool   result = waiter.Wait((s) => { throw new ApplicationException("test"); }, state);
                            Assert.IsTrue(result);
                        }
                        catch (Exception)
                        {
                            Assert.AreEqual(1, testInst.WaiterCount);
                            throw;
                        }
                    }
                }
                catch (Exception)
                {
                    Assert.AreEqual(0, testInst.WaiterCount);
                    throw;
                }
            }
        }
        public void TestInterruptOnDispose()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                int exitCount = 0;
                var task      = Task.Run(() =>
                {
                    try
                    {
                        using (var waiter = testInst.Enter())
                        {
                            waiter.Wait(_ => false, (object)null);
                        }
                    }
                    catch (OperationInterruptedException) { }
                    Interlocked.Increment(ref exitCount);
                });


                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);

                testInst.Dispose();
                TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref exitCount));

                task.Wait();
            }
        }
        public void TestAllThreadWakeUpOnSignalAll()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                int         exitCount = 0;
                int         state     = 0;
                List <Task> tasks     = new List <Task>();
                for (int i = 0; i < 6; i++)
                {
                    var task = Task.Run(() =>
                    {
                        using (var waiter = testInst.Enter())
                        {
                            waiter.Wait(_ => { return(Volatile.Read(ref state) > 0); }, (object)null);
                            Interlocked.Increment(ref exitCount);
                        }
                    });
                    tasks.Add(task);
                }


                TimingAssert.AreEqual(10000, 6, () => testInst.WaiterCount);
                Interlocked.Increment(ref state);

                lock (syncObj)
                {
                    testInst.PulseAll();
                }
                TimingAssert.AreEqual(10000, 0, () => testInst.WaiterCount);
                TimingAssert.AreEqual(10000, 6, () => Volatile.Read(ref exitCount));
            }
        }
        public void TestLongPredicateEstimatesOnceWithSmallTimeout()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                int result     = 0;
                int estimCount = 0;
                var task       = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(200))
                    {
                        if (waiter.Wait(_ => { Interlocked.Increment(ref estimCount); Thread.Sleep(500); return(false); }, (object)null))
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                lock (syncObj)
                {
                    testInst.Pulse();
                }
                TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref result));
                Assert.AreEqual(1, Volatile.Read(ref estimCount));
            }
        }
        public void TestTimeoutWorks()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                int result = 0;
                var task   = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(500))
                    {
                        if (waiter.Wait(_ => false, (object)null))
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                lock (syncObj)
                {
                    testInst.Pulse();
                }
                TimingAssert.AreEqual(10000, 2, () => Volatile.Read(ref result));
            }
        }
        public void TestPredicateCalledInsideLock()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                int result         = 0;
                int estimCount     = 0;
                int inMonitorCount = 0;
                int stopEstim      = 0;
                var task           = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(60000))
                    {
                        if (waiter.Wait(_ =>
                        {
                            if (Monitor.IsEntered(syncObj))
                            {
                                Interlocked.Increment(ref inMonitorCount);
                            }
                            Interlocked.Increment(ref estimCount);
                            return(Volatile.Read(ref stopEstim) != 0);
                        }, (object)null))
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);
                for (int i = 0; i < 20; i++)
                {
                    lock (syncObj)
                    {
                        testInst.Pulse();
                    }
                    Thread.Sleep(10);
                }
                Interlocked.Increment(ref stopEstim);
                lock (syncObj)
                {
                    testInst.Pulse();
                }

                TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result));
                Assert.IsTrue(Volatile.Read(ref estimCount) > 1);
                Assert.AreEqual(Volatile.Read(ref inMonitorCount), Volatile.Read(ref estimCount));
            }
        }
        public void TestStatePassedCorrectly()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                using (var waiter = testInst.Enter(100000))
                {
                    object state  = new object();
                    bool   result = waiter.Wait((s) => { Assert.AreEqual(state, s); return(true); }, state);
                    Assert.IsTrue(result);
                }
            }
        }
        public void TestWaitWithPredicateThrowsIfExternalLockTakenRecursively()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                lock (syncObj)
                {
                    using (var waiter = testInst.Enter(10))
                    {
                        waiter.Wait(_ => false, (object)null);
                    }
                }
            }
        }
        public void TestAlwaysPositivePredicate()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                using (var waiter = testInst.Enter(100000))
                {
                    int  called = 0;
                    bool result = waiter.Wait((s) => { called++; return(true); }, new object());
                    Assert.IsTrue(result);
                    Assert.AreEqual(1, called);
                }
            }
        }
            public bool TryAdd(T value, int timeout, CancellationToken token)
            {
                using (var waiter = VarFull.Enter(timeout, token))
                {
                    if (waiter.Wait(s => { Assert.IsTrue(Monitor.IsEntered(s.SharedSyncObj)); return(s.Queue.Count < s.MaxCount); }, this))
                    {
                        Assert.IsTrue(Monitor.IsEntered(SharedSyncObj));
                        Queue.Enqueue(value);
                        VarEmpty.Pulse();
                        return(true);
                    }

                    Assert.IsTrue(Monitor.IsEntered(SharedSyncObj));
                    return(false);
                }
            }
            public bool TryTake(out T value, int timeout, CancellationToken token)
            {
                using (var waiter = VarEmpty.Enter(timeout, token))
                {
                    if (waiter.Wait(s => { Assert.IsTrue(Monitor.IsEntered(s.SharedSyncObj)); return(s.Queue.Count > 0); }, this))
                    {
                        Assert.IsTrue(Monitor.IsEntered(SharedSyncObj));
                        value = Queue.Dequeue();
                        VarFull.Pulse();
                        return(true);
                    }

                    Assert.IsTrue(Monitor.IsEntered(SharedSyncObj));
                    value = default(T);
                    return(false);
                }
            }
        public void TestCancellationWorks()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                int result = 0;
                CancellationTokenSource tokenSrc = new CancellationTokenSource();
                var task = Task.Run(() =>
                {
                    try
                    {
                        using (var waiter = testInst.Enter(60000, tokenSrc.Token))
                        {
                            if (waiter.Wait(_ => false, (object)null))
                            {
                                Interlocked.Exchange(ref result, 1);
                            }
                            else
                            {
                                Interlocked.Exchange(ref result, 2);
                            }
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        Interlocked.Exchange(ref result, 3);
                    }
                });


                TimingAssert.AreEqual(10000, 1, () => testInst.WaiterCount);
                lock (syncObj)
                {
                    testInst.Pulse();
                }
                Assert.AreEqual(0, Volatile.Read(ref result));

                Thread.Sleep(100);
                tokenSrc.Cancel();
                TimingAssert.AreEqual(10000, 3, () => Volatile.Read(ref result));
            }
        }
        public void TestNotificationWithPredicate()
        {
            object syncObj = new object();

            using (var testInst = new ConditionVariable(syncObj))
            {
                int result = 0;
                int state  = 0;
                var task   = Task.Run(() =>
                {
                    using (var waiter = testInst.Enter(60000))
                    {
                        if (waiter.Wait((s) => Volatile.Read(ref state) > 0, new object()))
                        {
                            Interlocked.Exchange(ref result, 1);
                        }
                        else
                        {
                            Interlocked.Exchange(ref result, 2);
                        }
                    }
                });

                Thread.Sleep(100);
                Assert.AreEqual(0, Volatile.Read(ref result));

                lock (syncObj)
                {
                    testInst.Pulse();
                }
                Thread.Sleep(100);
                Assert.AreEqual(0, Volatile.Read(ref result));

                Interlocked.Increment(ref state);
                lock (syncObj)
                {
                    testInst.Pulse();
                }
                TimingAssert.AreEqual(10000, 1, () => Volatile.Read(ref result));
            }
        }