diff Implab/Components/PollingRunnableComponent.cs @ 202:2651cb9a4250 v2

Implemented PollingRunnableComponent
author cin
date Tue, 18 Oct 2016 01:03:49 +0300
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Components/PollingRunnableComponent.cs	Tue Oct 18 01:03:49 2016 +0300
@@ -0,0 +1,98 @@
+using System;
+using System.Threading;
+using Implab.Diagnostics;
+
+namespace Implab.Components {
+    public class PollingRunnableComponent : RunnableComponent {
+        readonly Timer m_timer;
+        readonly Func<Func<IPromise>, IPromise> m_dispatcher;
+        readonly TimeSpan m_interval;
+
+        int m_processing;
+        Promise m_pending;
+
+        protected PollingRunnableComponent(TimeSpan interval, Func<Func<IPromise>, IPromise> dispatcher, bool initialized) : base(initialized) {
+            m_timer = new Timer(OnInternalTick);
+
+            m_interval = interval;
+            m_dispatcher = dispatcher;
+        }
+
+        protected override IPromise OnStart() {
+            m_timer.Change(TimeSpan.Zero, m_interval);
+
+            return base.OnStart();
+        }
+
+        void OnInternalTick(object state) {
+            if (StartTick()) {
+                try {
+                    AwaitTick(m_dispatcher != null ? m_dispatcher(OnTick) : OnTick());
+                } catch (Exception error) {
+                    HandleTickError(error);
+                }
+            }   
+        }
+
+        /// <summary>
+        /// Starts the tick handler.
+        /// </summary>
+        /// <returns>boolean value, true - the new tick handler may be invoked, false - a tick handler is already running or a component isn't running.</returns>
+        /// <remarks>
+        /// If the component is stopping no new handlers can be run. Every successful call to this method must be completed with either AwaitTick or HandleTickError handlers.
+        /// </remarks>
+        protected virtual bool StartTick() {
+            if (State == ExecutionState.Running && Interlocked.CompareExchange(ref m_processing, 1, 0) == 0) {
+                m_pending = new Promise();
+                m_pending
+                    .On(() => m_processing = 0, PromiseEventType.All)
+                    .On(null, LogTickError);
+                return true;
+            }
+            return false;
+        }
+
+        protected virtual void AwaitTick(IPromise tick) {
+            if (tick == null) {
+                m_pending.Resolve();
+            } else {
+                tick.On(
+                    m_pending.Resolve,
+                    m_pending.Reject,
+                    m_pending.CancelOperation
+                );
+                m_pending.CancellationRequested(tick.Cancel);
+            }
+        }
+
+        protected virtual void HandleTickError(Exception error) {
+            m_pending.Reject(error);
+        }
+
+        protected virtual void LogTickError(Exception error) {
+        }
+
+        protected virtual IPromise OnTick() {
+            return Promise.SUCCESS;
+        }
+
+        protected override IPromise OnStop() {
+            m_timer.Change(-1, -1);
+
+            if (m_pending != null) {
+                m_pending.Cancel();
+                return m_pending.Then(base.OnStop);
+            }
+
+            return base.OnStop();
+        }
+
+        protected override void Dispose(bool disposing, Exception lastError) {
+            if (disposing)
+                Safe.Dispose(m_timer);
+            
+            base.Dispose(disposing, lastError);
+        }
+    }
+}
+