| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 | // Licensed to the .NET Foundation under one or more agreements.// The .NET Foundation licenses this file to you under the MIT License.// See the LICENSE file in the project root for more information. using System;using System.Collections.Generic;using System.Diagnostics;using System.Reactive.Concurrency;using System.Reactive.Disposables;using System.Reactive.Linq;using System.Threading;using Microsoft.Reactive.Testing;using Xunit;#if STRESSusing ReactiveTests.Stress.Schedulers;#endifnamespace ReactiveTests.Tests{    public class EventLoopSchedulerTest    {        private static readonly TimeSpan MaxWaitTime = TimeSpan.FromSeconds(10);        [Fact]        public void EventLoop_ArgumentChecking()        {            using var el = new EventLoopScheduler();#if !NO_THREAD            ReactiveAssert.Throws<ArgumentNullException>(() => new EventLoopScheduler(null));#endif            ReactiveAssert.Throws<ArgumentNullException>(() => el.Schedule(42, default));            ReactiveAssert.Throws<ArgumentNullException>(() => el.Schedule(42, DateTimeOffset.Now, default));            ReactiveAssert.Throws<ArgumentNullException>(() => el.Schedule(42, TimeSpan.Zero, default));            ReactiveAssert.Throws<ArgumentNullException>(() => el.SchedulePeriodic(42, TimeSpan.FromSeconds(1), default));            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => el.SchedulePeriodic(42, TimeSpan.FromSeconds(-1), _ => _));        }        [Fact]        public void EventLoop_Now()        {            using var el = new EventLoopScheduler();            var res = el.Now - DateTime.Now;            Assert.True(res.Seconds < 1);        }        [Fact]        public void EventLoop_DisposeWithInFlightActions()        {            using (var scheduler = new EventLoopScheduler())            using (var subscription = Observable                .Range(1, 10)                .ObserveOn(scheduler)                .Subscribe(_ => Thread.Sleep(50)))            {                Thread.Sleep(50);            }        }        [Fact]        public void EventLoop_ScheduleAction()        {            var ran = false;            var gate = new Semaphore(0, 1);            using var el = new EventLoopScheduler();            el.Schedule(() =>            {                ran = true;                gate.Release();            });            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            Assert.True(ran);        }#if !NO_THREAD        [Fact]        public void EventLoop_DifferentThread()        {            var id = default(int);            var gate = new Semaphore(0, 1);            using var el = new EventLoopScheduler();            el.Schedule(() =>            {                id = Thread.CurrentThread.ManagedThreadId;                gate.Release();            });            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            Assert.NotEqual(Thread.CurrentThread.ManagedThreadId, id);        }#endif        [Fact]        public void EventLoop_ScheduleOrderedActions()        {            var results = new List<int>();            var gate = new Semaphore(0, 1);            using var el = new EventLoopScheduler();            el.Schedule(() => results.Add(0));            el.Schedule(() =>            {                results.Add(1);                gate.Release();            });            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            results.AssertEqual(0, 1);        }        [Fact]        public void EventLoop_SchedulerDisposed()        {            var d = 0;            var e = new ManualResetEvent(false);            var f = new ManualResetEvent(false);            var results = new List<int>();            using var el = new EventLoopScheduler();            el.Schedule(() => results.Add(0));            el.Schedule(() =>            {                el.Dispose();                e.Set();                results.Add(1);                try                {                    el.Schedule(() => { throw new Exception("Should be disposed!"); });                    f.Set();                }                catch (ObjectDisposedException)                {                    // BREAKING CHANGE v2 > v1.x - New exception behavior.                    Interlocked.Increment(ref d);                    f.Set();                }            });            e.WaitOne();            try            {                el.Schedule(() => results.Add(2));            }            catch (ObjectDisposedException)            {                // BREAKING CHANGE v2 > v1.x - New exception behavior.                Interlocked.Increment(ref d);            }            f.WaitOne();            results.AssertEqual(0, 1);            Assert.Equal(2, d);        }        [Fact]        public void EventLoop_ScheduleTimeOrderedActions()        {            var results = new List<int>();            var gate = new Semaphore(0, 1);            using var el = new EventLoopScheduler();            el.Schedule(TimeSpan.FromMilliseconds(50), () => results.Add(1));            el.Schedule(TimeSpan.FromMilliseconds(100), () =>                        {                            results.Add(0);                            gate.Release();                        });            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            results.AssertEqual(1, 0);        }        [Fact]        public void EventLoop_ScheduleOrderedAndTimedActions()        {            var results = new List<int>();            var gate = new Semaphore(0, 1);            using var el = new EventLoopScheduler();            el.Schedule(() => results.Add(1));            el.Schedule(TimeSpan.FromMilliseconds(100), () =>            {                results.Add(0);                gate.Release();            });            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            results.AssertEqual(1, 0);        }        [Fact]        public void EventLoop_ScheduleTimeOrderedInFlightActions()        {            var results = new List<int>();            var gate = new Semaphore(0, 1);            using var el = new EventLoopScheduler();            el.Schedule(TimeSpan.FromMilliseconds(100), () =>                        {                            results.Add(0);                            el.Schedule(TimeSpan.FromMilliseconds(50), () => results.Add(1));                            el.Schedule(TimeSpan.FromMilliseconds(100), () =>                            {                                results.Add(2);                                gate.Release();                            });                        });            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            results.AssertEqual(0, 1, 2);        }        [Fact]        public void EventLoop_ScheduleTimeAndOrderedInFlightActions()        {            var results = new List<int>();            var gate = new Semaphore(0, 1);            using var el = new EventLoopScheduler();            el.Schedule(TimeSpan.FromMilliseconds(100), () =>            {                results.Add(0);                el.Schedule(() => results.Add(4));                el.Schedule(TimeSpan.FromMilliseconds(50), () => results.Add(1));                el.Schedule(TimeSpan.FromMilliseconds(100), () =>                {                    results.Add(2);                    gate.Release();                });            });            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            results.AssertEqual(0, 4, 1, 2);        }        [Fact]        public void EventLoop_ScheduleActionNested()        {            var ran = false;            using var el = new EventLoopScheduler();            var gate = new Semaphore(0, 1);            el.Schedule(() => el.Schedule(() =>            {                ran = true;                gate.Release();            }));            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            Assert.True(ran);        }        [Fact(Skip = "")]        public void EventLoop_ScheduleActionDue()        {            var ran = false;            using var el = new EventLoopScheduler();            var sw = new Stopwatch();            var gate = new Semaphore(0, 1);            sw.Start();            el.Schedule(TimeSpan.FromSeconds(0.2), () =>            {                ran = true;                sw.Stop();                gate.Release();            });            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            Assert.True(ran, "ran");            Assert.True(sw.ElapsedMilliseconds > 180, "due " + sw.ElapsedMilliseconds);        }        [Fact(Skip = "")]        public void EventLoop_ScheduleActionDueNested()        {            var ran = false;            using var el = new EventLoopScheduler();            var gate = new Semaphore(0, 1);            var sw = new Stopwatch();            sw.Start();            el.Schedule(TimeSpan.FromSeconds(0.2), () =>            {                sw.Stop();                sw.Start();                el.Schedule(TimeSpan.FromSeconds(0.2), () =>                {                    sw.Stop();                    ran = true;                    gate.Release();                });            });            Assert.True(gate.WaitOne(MaxWaitTime), "Timeout!");            Assert.True(ran, "ran");            Assert.True(sw.ElapsedMilliseconds > 380, "due " + sw.ElapsedMilliseconds);        }#if !NO_PERF        [Fact]        public void Stopwatch()        {            using var el = new EventLoopScheduler();            StopwatchTest.Run(el);        }#endif        [Fact]        public void EventLoop_Immediate()        {            var M = 1000;            var N = 4;            for (var i = 0; i < N; i++)            {                for (var j = 1; j <= M; j *= 10)                {                    using (var e = new EventLoopScheduler())                    {                        using (var d = new CompositeDisposable())                        {                            var cd = new CountdownEvent(j);                            for (var k = 0; k < j; k++)                            {                                d.Add(e.Schedule(() => cd.Signal()));                            }                            if (!cd.Wait(10000))                            {                                Assert.True(false, "j = " + j);                            }                        }                    }                }            }        }        [Fact]        public void EventLoop_TimeCollisions()        {            var M = 1000;            var N = 4;            for (var i = 0; i < N; i++)            {                for (var j = 1; j <= M; j *= 10)                {                    using (var e = new EventLoopScheduler())                    {                        using (var d = new CompositeDisposable())                        {                            var cd = new CountdownEvent(j);                            for (var k = 0; k < j; k++)                            {                                d.Add(e.Schedule(TimeSpan.FromMilliseconds(100), () => cd.Signal()));                            }                            if (!cd.Wait(10000))                            {                                Assert.True(false, "j = " + j);                            }                        }                    }                }            }        }        [Fact]        public void EventLoop_Spread()        {            var M = 1000;            var N = 4;            for (var i = 0; i < N; i++)            {                for (var j = 1; j <= M; j *= 10)                {                    using (var e = new EventLoopScheduler())                    {                        using (var d = new CompositeDisposable())                        {                            var cd = new CountdownEvent(j);                            for (var k = 0; k < j; k++)                            {                                d.Add(e.Schedule(TimeSpan.FromMilliseconds(k), () => cd.Signal()));                            }                            if (!cd.Wait(10000))                            {                                Assert.True(false, "j = " + j);                            }                        }                    }                }            }        }        [Fact]        public void EventLoop_Periodic()        {            var n = 0;            using (var s = new EventLoopScheduler())            {                var e = new ManualResetEvent(false);                var d = s.SchedulePeriodic(TimeSpan.FromMilliseconds(25), () =>                {                    if (Interlocked.Increment(ref n) == 10)                    {                        e.Set();                    }                });                if (!e.WaitOne(10000))                {                    Assert.True(false);                }                d.Dispose();            }        }#if STRESS        [Fact]        public void EventLoop_Stress()        {            EventLoop.NoSemaphoreFullException();        }#endif#if DESKTOPCLR        [Fact]        public void EventLoop_CorrectWorkStealing()        {            const int workItemCount = 100;            var failureCount = 0;            var countdown = new CountdownEvent(workItemCount);            var dueTime = DateTimeOffset.Now + TimeSpan.FromSeconds(1);            using (var d = new CompositeDisposable())            {                for (var i = 0; i < workItemCount; i++)                {                    var scheduler = new EventLoopScheduler();                    scheduler.Schedule(() =>                    {                        var schedulerThread = Thread.CurrentThread;                        scheduler.Schedule(dueTime, () =>                        {                            if (Thread.CurrentThread != schedulerThread)                            {                                Interlocked.Increment(ref failureCount);                            }                            countdown.Signal();                        });                    });                    d.Add(scheduler);                }                countdown.Wait();            }            Assert.Equal(0, failureCount);        }#endif    }}
 |