Browse Source

Support for periodic scheduling with a period of 0ms.

Bart De Smet 10 years ago
parent
commit
0b0d1c2510

+ 1 - 1
Rx.NET/Source/System.Reactive.Core/Reactive/Concurrency/ConcurrencyAbstractionLayer.Default.cs

@@ -371,7 +371,7 @@ namespace System.Reactive.Concurrency
         class FastPeriodicTimer : IDisposable
         {
             private readonly Action _action;
-            private bool disposed;
+            private volatile bool disposed;
 
             public FastPeriodicTimer(Action action)
             {

+ 1 - 1
Rx.NET/Source/System.Reactive.PlatformServices/Reactive/Concurrency/ConcurrencyAbstractionLayerImpl.cs

@@ -375,7 +375,7 @@ namespace System.Reactive.Concurrency
         class FastPeriodicTimer : IDisposable
         {
             private readonly Action _action;
-            private bool disposed;
+            private volatile bool disposed;
 
             public FastPeriodicTimer(Action action)
             {

+ 40 - 9
Rx.NET/Source/System.Reactive.PlatformServices/Reactive/Concurrency/ThreadPoolScheduler.cs

@@ -117,21 +117,52 @@ namespace System.Reactive.Concurrency
         /// <param name="action">Action to be executed, potentially updating the state.</param>
         /// <returns>The disposable object used to cancel the scheduled recurring action (best effort).</returns>
         /// <exception cref="ArgumentNullException"><paramref name="action"/> is null.</exception>
-        /// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than or equal to zero.</exception>
+        /// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than zero.</exception>
         public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
         {
-            //
-            // MSDN documentation states the following:
-            //
-            //    "If period is zero (0) or negative one (-1) milliseconds and dueTime is positive, callback is invoked once;
-            //     the periodic behavior of the timer is disabled, but can be re-enabled using the Change method."
-            //
-            if (period <= TimeSpan.Zero)
+            if (period < TimeSpan.Zero)
                 throw new ArgumentOutOfRangeException("period");
             if (action == null)
                 throw new ArgumentNullException("action");
 
-            return new PeriodicTimer<TState>(state, period, action);
+            if (period == TimeSpan.Zero)
+            {
+                return new FastPeriodicTimer<TState>(state, action);
+            }
+            else
+            {
+                return new PeriodicTimer<TState>(state, period, action);
+            }
+        }
+
+        sealed class FastPeriodicTimer<TState> : IDisposable
+        {
+            private TState _state;
+            private Func<TState, TState> _action;
+            private volatile bool _disposed;
+
+            public FastPeriodicTimer(TState state, Func<TState, TState> action)
+            {
+                _state = state;
+                _action = action;
+
+                ThreadPool.QueueUserWorkItem(Tick, null);
+            }
+
+            private void Tick(object state)
+            {
+                if (!_disposed)
+                {
+                    _state = _action(_state);
+                    ThreadPool.QueueUserWorkItem(Tick, null);
+                }
+            }
+
+            public void Dispose()
+            {
+                _disposed = true;
+                _action = Stubs<TState>.I;
+            }
         }
 
 #if USE_TIMER_SELF_ROOT

+ 12 - 2
Rx.NET/Source/Tests.System.Reactive/Tests/Concurrency/ThreadPoolSchedulerTest.cs

@@ -209,12 +209,22 @@ namespace ReactiveTests.Tests
         public void Periodic_ArgumentChecking()
         {
             ReactiveAssert.Throws<ArgumentNullException>(() => ThreadPoolScheduler.Instance.SchedulePeriodic(0, TimeSpan.FromSeconds(1), null));
-            ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => ThreadPoolScheduler.Instance.SchedulePeriodic(0, TimeSpan.Zero, _ => _));
             ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => ThreadPoolScheduler.Instance.SchedulePeriodic(0, TimeSpan.FromSeconds(-1), _ => _));
         }
 
         [TestMethod]
         public void Periodic_Regular()
+        {
+            Periodic_Impl(TimeSpan.FromMilliseconds(25));
+        }
+
+        [TestMethod]
+        public void Periodic_Zero()
+        {
+            Periodic_Impl(TimeSpan.Zero);
+        }
+
+        private void Periodic_Impl(TimeSpan period)
         {
             var gate = new object();
             var n = 0;
@@ -222,7 +232,7 @@ namespace ReactiveTests.Tests
 
             var lst = new List<int>();
 
-            var d = ThreadPoolScheduler.Instance.SchedulePeriodic(0, TimeSpan.FromMilliseconds(25), x =>
+            var d = ThreadPoolScheduler.Instance.SchedulePeriodic(0, period, x =>
             {
                 lock (gate)
                 {