Mercurial > pub > ImplabNet
annotate Implab/Components/RunnableComponent.cs @ 186:75103928da09 ref20160224
working on cancelation and error handling
author | cin |
---|---|
date | Tue, 19 Apr 2016 00:50:14 +0300 |
parents | 822aab37b107 |
children | dd4a3590f9c6 |
rev | line source |
---|---|
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
1 using System; |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
2 |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
3 namespace Implab.Components { |
185 | 4 public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable { |
184 | 5 enum Commands { |
6 Ok = 0, | |
7 Fail, | |
8 Init, | |
9 Start, | |
10 Stop, | |
11 Dispose, | |
12 Last = Dispose | |
13 } | |
14 | |
15 class StateMachine { | |
16 static readonly ExecutionState[,] _transitions; | |
17 | |
18 static StateMachine() { | |
19 _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; | |
20 | |
185 | 21 Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init); |
22 Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose); | |
23 | |
24 Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok); | |
25 Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail); | |
184 | 26 |
27 Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); | |
28 Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); | |
29 | |
30 Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok); | |
31 Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail); | |
32 Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop); | |
33 Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose); | |
34 | |
35 Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail); | |
36 Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop); | |
37 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose); | |
38 | |
39 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); | |
40 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); | |
185 | 41 |
42 Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose); | |
184 | 43 } |
44 | |
45 static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { | |
46 _transitions[(int)s1, (int)cmd] = s2; | |
47 } | |
48 | |
49 public ExecutionState State { | |
50 get; | |
51 private set; | |
52 } | |
53 | |
54 public StateMachine(ExecutionState initial) { | |
55 State = initial; | |
56 } | |
57 | |
58 public bool Move(Commands cmd) { | |
59 var next = _transitions[(int)State, (int)cmd]; | |
60 if (next == ExecutionState.Undefined) | |
61 return false; | |
62 State = next; | |
63 return true; | |
64 } | |
65 } | |
66 | |
67 IPromise m_pending; | |
68 Exception m_lastError; | |
69 | |
70 readonly StateMachine m_stateMachine; | |
71 | |
72 protected RunnableComponent(bool initialized) { | |
73 m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); | |
74 } | |
75 | |
185 | 76 protected virtual int DisposeTimeout { |
77 get { | |
78 return 10000; | |
79 } | |
80 } | |
81 | |
184 | 82 void ThrowInvalidCommand(Commands cmd) { |
185 | 83 if (m_stateMachine.State == ExecutionState.Disposed) |
84 throw new ObjectDisposedException(ToString()); | |
85 | |
184 | 86 throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); |
87 } | |
88 | |
185 | 89 void Move(Commands cmd) { |
90 if (!m_stateMachine.Move(cmd)) | |
91 ThrowInvalidCommand(cmd); | |
184 | 92 } |
93 | |
185 | 94 void Invoke(Commands cmd, Action action) { |
95 lock (m_stateMachine) | |
96 Move(cmd); | |
97 | |
184 | 98 try { |
99 action(); | |
185 | 100 lock(m_stateMachine) |
101 Move(Commands.Ok); | |
102 | |
184 | 103 } catch (Exception err) { |
185 | 104 lock (m_stateMachine) { |
105 Move(Commands.Fail); | |
106 m_lastError = err; | |
107 } | |
184 | 108 throw; |
109 } | |
110 } | |
111 | |
185 | 112 IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) { |
113 IPromise promise = null; | |
114 IPromise prev; | |
184 | 115 |
185 | 116 var task = new ActionChainTask(action, null, null, true); |
117 | |
118 lock (m_stateMachine) { | |
119 Move(cmd); | |
120 | |
121 prev = m_pending; | |
184 | 122 |
185 | 123 promise = task.Then( |
124 () => { | |
125 lock(m_stateMachine) { | |
126 if (m_pending == promise) { | |
127 Move(Commands.Ok); | |
128 m_pending = null; | |
129 } | |
184 | 130 } |
185 | 131 }, e => { |
132 lock(m_stateMachine) { | |
133 if (m_pending == promise) { | |
134 Move(Commands.Fail); | |
135 m_pending = null; | |
136 m_lastError = e; | |
137 } | |
138 } | |
139 throw new PromiseTransientException(e); | |
140 }, | |
141 r => { | |
186 | 142 // handle cancellation as exception |
185 | 143 throw new OperationCanceledException("The operation has been cancelled", r); |
184 | 144 } |
185 | 145 ); |
157 | 146 |
185 | 147 m_pending = promise; |
148 } | |
157 | 149 |
185 | 150 if (prev == null) |
151 task.Resolve(); | |
152 else | |
153 chain(prev, task); | |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
154 |
185 | 155 return promise; |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
156 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
157 |
184 | 158 |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
159 #region IInitializable implementation |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
160 |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
161 public void Init() { |
184 | 162 Invoke(Commands.Init, OnInitialize); |
163 } | |
164 | |
165 protected virtual void OnInitialize() { | |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
166 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
167 |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
168 #endregion |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
169 |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
170 #region IRunnable implementation |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
171 |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
172 public IPromise Start() { |
185 | 173 return InvokeAsync(Commands.Start, OnStart, null); |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
174 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
175 |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
176 protected virtual IPromise OnStart() { |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
177 return Promise.SUCCESS; |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
178 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
179 |
185 | 180 public IPromise Stop() { |
181 return InvokeAsync(Commands.Stop, OnStop, StopPending).Then(Dispose); | |
182 } | |
183 | |
184 protected virtual IPromise OnStop() { | |
185 return Promise.SUCCESS; | |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
186 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
187 |
185 | 188 /// <summary> |
189 /// Stops the current operation if one exists. | |
190 /// </summary> | |
191 /// <param name="current">Current.</param> | |
192 /// <param name="stop">Stop.</param> | |
193 protected virtual void StopPending(IPromise current, IDeferred stop) { | |
194 if (current == null) { | |
195 stop.Resolve(); | |
196 } else { | |
186 | 197 current.On(stop.Resolve, stop.Reject, e => stop.Resolve()); |
185 | 198 current.Cancel(); |
199 } | |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
200 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
201 |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
202 public ExecutionState State { |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
203 get { |
185 | 204 return m_stateMachine.State; |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
205 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
206 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
207 |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
208 public Exception LastError { |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
209 get { |
185 | 210 return m_lastError; |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
211 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
212 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
213 |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
214 #endregion |
185 | 215 |
216 #region IDisposable implementation | |
217 | |
218 public void Dispose() { | |
219 IPromise pending; | |
220 lock (m_stateMachine) { | |
221 if (m_stateMachine.State == ExecutionState.Disposed) | |
222 return; | |
223 | |
224 Move(Commands.Dispose); | |
225 | |
226 GC.SuppressFinalize(this); | |
227 | |
228 pending = m_pending; | |
229 m_pending = null; | |
230 } | |
231 if (pending != null) { | |
232 pending.Cancel(); | |
233 pending.Timeout(DisposeTimeout).On( | |
234 () => Dispose(true, null), | |
235 err => Dispose(true, err), | |
236 reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason)) | |
237 ); | |
238 } else { | |
239 Dispose(true, m_lastError); | |
240 } | |
241 } | |
242 | |
243 ~RunnableComponent() { | |
244 Dispose(false, null); | |
245 } | |
246 | |
247 #endregion | |
248 | |
249 protected virtual void Dispose(bool disposing, Exception lastError) { | |
250 | |
251 } | |
252 | |
156
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
253 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
254 } |
97fbbf816844
Promises: SignalXXX methods merged into SignalHandler method.
cin
parents:
diff
changeset
|
255 |