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
|