| 258 | 1 using System; | 
|  | 2 using System.Threading; | 
|  | 3 using System.Threading.Tasks; | 
|  | 4 | 
|  | 5 namespace Implab.Components { | 
|  | 6     public abstract class PollingComponent : RunnableComponent { | 
|  | 7 | 
|  | 8         readonly Timer m_timer; | 
|  | 9 | 
|  | 10         readonly CancellationTokenSource m_cancellation = new CancellationTokenSource(); | 
|  | 11 | 
| 259 | 12         Task m_pending; | 
|  | 13         Task m_poll; | 
|  | 14 | 
| 258 | 15         /// <summary> | 
|  | 16         /// Poll interval in milliseconds. | 
|  | 17         /// </summary> | 
|  | 18         /// <returns></returns> | 
|  | 19         public int Interval { get; set; } | 
|  | 20 | 
|  | 21         /// <summary> | 
|  | 22         /// Delay to the first poll after start in milliseconds | 
|  | 23         /// </summary> | 
|  | 24         /// <returns></returns> | 
|  | 25         public int Delay { get; set; } | 
|  | 26 | 
|  | 27         /// <summary> | 
|  | 28         /// Indicates how to handle unhandled exceptions in <see cref="Poll()"/> method. | 
|  | 29         /// </summary> | 
|  | 30         /// <returns></returns> | 
|  | 31         public bool FailOnError { get; set; } | 
|  | 32 | 
|  | 33         /// <summary> | 
|  | 34         /// Event for the unhandled exceptions in <see cref="Poll()"/> method. | 
|  | 35         /// </summary> | 
|  | 36         public event EventHandler<UnhandledExceptionEventArgs> UnhandledException; | 
|  | 37 | 
|  | 38         protected PollingComponent(bool initialized) : base(initialized) { | 
|  | 39             m_timer = new Timer(OnTimer); | 
|  | 40         } | 
|  | 41 | 
|  | 42         protected override void RunInternal() { | 
|  | 43             ScheduleNextPoll(Delay); | 
|  | 44         } | 
|  | 45 | 
|  | 46 | 
| 259 | 47         protected override async Task StopInternalAsync(CancellationToken ct) { | 
|  | 48             // component in Stopping state, no new polls will be scheduled | 
|  | 49             m_cancellation.Cancel(); | 
|  | 50             try { | 
|  | 51                 // await for pending poll | 
|  | 52                 await m_poll; | 
|  | 53             } catch (OperationCanceledException e) { | 
|  | 54                 // OK | 
|  | 55             } | 
|  | 56         } | 
| 258 | 57 | 
|  | 58         protected abstract Task Poll(CancellationToken ct); | 
|  | 59 | 
|  | 60         void ScheduleNextPoll(int timeout) { | 
|  | 61             lock (SynchronizationObject) { | 
| 259 | 62                 if (State == ExecutionState.Running) { | 
|  | 63                     m_pending = Safe.CreateTask(m_cancellation.Token); | 
|  | 64                     m_poll = m_pending.Then(() => Poll(m_cancellation.Token)); | 
| 258 | 65                     m_timer.Change(timeout, Timeout.Infinite); | 
| 259 | 66                 } | 
| 258 | 67             } | 
|  | 68         } | 
|  | 69 | 
| 259 | 70         async void OnTimer(object state) { | 
| 258 | 71             try { | 
| 259 | 72                 m_pending.Start(); | 
|  | 73                 await m_poll; | 
| 258 | 74             } catch (Exception e) { | 
|  | 75                 UnhandledException.DispatchEvent(this, new UnhandledExceptionEventArgs(e, false)); | 
|  | 76                 if (FailOnError) | 
|  | 77                     Fail(e); | 
|  | 78             } | 
|  | 79             ScheduleNextPoll(Interval); | 
|  | 80         } | 
|  | 81 | 
|  | 82         protected override void Dispose(bool disposing) { | 
|  | 83             if (disposing) | 
|  | 84                 Safe.Dispose(m_timer, m_cancellation); | 
|  | 85             base.Dispose(disposing); | 
|  | 86         } | 
|  | 87 | 
|  | 88     } | 
|  | 89 } |