Mercurial > pub > ImplabNet
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); + } + } +} +