| 202 | 1 using System; | 
|  | 2 using System.Threading; | 
|  | 3 using Implab.Diagnostics; | 
|  | 4 | 
|  | 5 namespace Implab.Components { | 
|  | 6     public class PollingRunnableComponent : RunnableComponent { | 
|  | 7         readonly Timer m_timer; | 
|  | 8         readonly Func<Func<IPromise>, IPromise> m_dispatcher; | 
|  | 9         readonly TimeSpan m_interval; | 
|  | 10 | 
|  | 11         int m_processing; | 
|  | 12         Promise m_pending; | 
|  | 13 | 
|  | 14         protected PollingRunnableComponent(TimeSpan interval, Func<Func<IPromise>, IPromise> dispatcher, bool initialized) : base(initialized) { | 
|  | 15             m_timer = new Timer(OnInternalTick); | 
|  | 16 | 
|  | 17             m_interval = interval; | 
|  | 18             m_dispatcher = dispatcher; | 
|  | 19         } | 
|  | 20 | 
|  | 21         protected override IPromise OnStart() { | 
|  | 22             m_timer.Change(TimeSpan.Zero, m_interval); | 
|  | 23 | 
|  | 24             return base.OnStart(); | 
|  | 25         } | 
|  | 26 | 
|  | 27         void OnInternalTick(object state) { | 
|  | 28             if (StartTick()) { | 
|  | 29                 try { | 
|  | 30                     AwaitTick(m_dispatcher != null ? m_dispatcher(OnTick) : OnTick()); | 
|  | 31                 } catch (Exception error) { | 
|  | 32                     HandleTickError(error); | 
|  | 33                 } | 
|  | 34             } | 
|  | 35         } | 
|  | 36 | 
|  | 37         /// <summary> | 
|  | 38         /// Starts the tick handler. | 
|  | 39         /// </summary> | 
|  | 40         /// <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> | 
|  | 41         /// <remarks> | 
|  | 42         /// 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. | 
|  | 43         /// </remarks> | 
|  | 44         protected virtual bool StartTick() { | 
|  | 45             if (State == ExecutionState.Running && Interlocked.CompareExchange(ref m_processing, 1, 0) == 0) { | 
|  | 46                 m_pending = new Promise(); | 
|  | 47                 m_pending | 
|  | 48                     .On(() => m_processing = 0, PromiseEventType.All) | 
|  | 49                     .On(null, LogTickError); | 
|  | 50                 return true; | 
|  | 51             } | 
|  | 52             return false; | 
|  | 53         } | 
|  | 54 | 
|  | 55         protected virtual void AwaitTick(IPromise tick) { | 
|  | 56             if (tick == null) { | 
|  | 57                 m_pending.Resolve(); | 
|  | 58             } else { | 
|  | 59                 tick.On( | 
|  | 60                     m_pending.Resolve, | 
|  | 61                     m_pending.Reject, | 
|  | 62                     m_pending.CancelOperation | 
|  | 63                 ); | 
|  | 64                 m_pending.CancellationRequested(tick.Cancel); | 
|  | 65             } | 
|  | 66         } | 
|  | 67 | 
|  | 68         protected virtual void HandleTickError(Exception error) { | 
|  | 69             m_pending.Reject(error); | 
|  | 70         } | 
|  | 71 | 
|  | 72         protected virtual void LogTickError(Exception error) { | 
|  | 73         } | 
|  | 74 | 
|  | 75         protected virtual IPromise OnTick() { | 
|  | 76             return Promise.SUCCESS; | 
|  | 77         } | 
|  | 78 | 
|  | 79         protected override IPromise OnStop() { | 
|  | 80             m_timer.Change(-1, -1); | 
|  | 81 | 
|  | 82             if (m_pending != null) { | 
|  | 83                 m_pending.Cancel(); | 
|  | 84                 return m_pending.Then(base.OnStop); | 
|  | 85             } | 
|  | 86 | 
|  | 87             return base.OnStop(); | 
|  | 88         } | 
|  | 89 | 
|  | 90         protected override void Dispose(bool disposing, Exception lastError) { | 
|  | 91             if (disposing) | 
|  | 92                 Safe.Dispose(m_timer); | 
|  | 93 | 
|  | 94             base.Dispose(disposing, lastError); | 
|  | 95         } | 
|  | 96     } | 
|  | 97 } | 
|  | 98 |