Mercurial > pub > ImplabNet
comparison Implab/Components/RunnableComponent.cs @ 210:5dc21f6a3222 v2
Code review for RunnableComponent
Added StaApartment class based on System.Windows.Forms.Application message loop
author | cin |
---|---|
date | Mon, 20 Mar 2017 17:44:18 +0300 |
parents | 7d07503621fe |
children | eee3e49dd1ff |
comparison
equal
deleted
inserted
replaced
209:a867536c68fc | 210:5dc21f6a3222 |
---|---|
13 Reset, | 13 Reset, |
14 Last = Reset | 14 Last = Reset |
15 } | 15 } |
16 | 16 |
17 class StateMachine { | 17 class StateMachine { |
18 static readonly ExecutionState[,] _transitions; | 18 public static readonly ExecutionState[,] ReusableTransitions; |
19 public static readonly ExecutionState[,] NonreusableTransitions; | |
20 | |
21 class StateBuilder { | |
22 readonly ExecutionState[,] m_states; | |
23 | |
24 public ExecutionState[,] States { | |
25 get { return m_states; } | |
26 } | |
27 public StateBuilder(ExecutionState[,] states) { | |
28 m_states = states; | |
29 } | |
30 | |
31 public StateBuilder() { | |
32 m_states = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; | |
33 } | |
34 | |
35 public StateBuilder Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { | |
36 m_states[(int)s1, (int)cmd] = s2; | |
37 return this; | |
38 } | |
39 | |
40 public StateBuilder Clone() { | |
41 return new StateBuilder((ExecutionState[,])m_states.Clone()); | |
42 } | |
43 } | |
19 | 44 |
20 static StateMachine() { | 45 static StateMachine() { |
21 _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; | 46 ReusableTransitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; |
22 | 47 |
23 Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init); | 48 var common = new StateBuilder() |
24 Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose); | 49 .Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init) |
25 | 50 .Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose) |
26 Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok); | 51 |
27 Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail); | 52 .Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok) |
28 | 53 .Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail) |
29 Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); | 54 |
30 Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); | 55 .Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start) |
31 | 56 .Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose) |
32 Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok); | 57 |
33 Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail); | 58 .Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok) |
34 Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop); | 59 .Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail) |
35 Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose); | 60 .Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop) |
36 | 61 .Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose) |
37 Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail); | 62 |
38 Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop); | 63 .Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail) |
39 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose); | 64 .Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop) |
40 | 65 .Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose) |
41 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); | 66 |
42 Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok); | 67 .Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose) |
43 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose); | 68 .Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset) |
44 | 69 |
45 Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose); | 70 .Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail) |
46 Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset); | 71 .Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose) |
47 } | 72 |
48 | 73 .Edge(ExecutionState.Disposed, ExecutionState.Disposed, Commands.Dispose); |
49 static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { | 74 |
50 _transitions[(int)s1, (int)cmd] = s2; | 75 var reusable = common |
51 } | 76 .Clone() |
77 .Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok); | |
78 | |
79 var nonreusable = common | |
80 .Clone() | |
81 .Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); | |
82 | |
83 NonreusableTransitions = nonreusable.States; | |
84 ReusableTransitions = reusable.States; | |
85 | |
86 } | |
87 | |
88 readonly ExecutionState[,] m_states; | |
52 | 89 |
53 public ExecutionState State { | 90 public ExecutionState State { |
54 get; | 91 get; |
55 private set; | 92 private set; |
56 } | 93 } |
57 | 94 |
58 public StateMachine(ExecutionState initial) { | 95 public StateMachine(ExecutionState[,] states, ExecutionState initial) { |
59 State = initial; | 96 State = initial; |
97 m_states = states; | |
60 } | 98 } |
61 | 99 |
62 public bool Move(Commands cmd) { | 100 public bool Move(Commands cmd) { |
63 var next = _transitions[(int)State, (int)cmd]; | 101 var next = m_states[(int)State, (int)cmd]; |
64 if (next == ExecutionState.Undefined) | 102 if (next == ExecutionState.Undefined) |
65 return false; | 103 return false; |
66 State = next; | 104 State = next; |
67 return true; | 105 return true; |
68 } | 106 } |
79 /// Initializes component state. | 117 /// Initializes component state. |
80 /// </summary> | 118 /// </summary> |
81 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param> | 119 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param> |
82 /// <param name="reusable">If set, the component may start after it has been stopped, otherwise the component is disposed after being stopped.</param> | 120 /// <param name="reusable">If set, the component may start after it has been stopped, otherwise the component is disposed after being stopped.</param> |
83 protected RunnableComponent(bool initialized, bool reusable) { | 121 protected RunnableComponent(bool initialized, bool reusable) { |
84 m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); | 122 m_stateMachine = new StateMachine( |
123 reusable ? StateMachine.ReusableTransitions : StateMachine.NonreusableTransitions, | |
124 initialized ? ExecutionState.Ready : ExecutionState.Created | |
125 ); | |
85 m_reusable = reusable; | 126 m_reusable = reusable; |
86 DisposeTimeout = 10000; | |
87 } | 127 } |
88 | 128 |
89 /// <summary> | 129 /// <summary> |
90 /// Initializes component state. The component created with this constructor is not reusable, i.e. it will be disposed after stop. | 130 /// Initializes component state. The component created with this constructor is not reusable, i.e. it will be disposed after stop. |
91 /// </summary> | 131 /// </summary> |
92 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param> | 132 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param> |
93 protected RunnableComponent(bool initialized) : this(initialized, false) { | 133 protected RunnableComponent(bool initialized) : this(initialized, false) { |
94 } | |
95 | |
96 /// <summary> | |
97 /// Gets or sets the timeout to wait for the pending operation to complete. If the pending operation doesn't finish than the component will be disposed anyway. | |
98 /// </summary> | |
99 protected int DisposeTimeout { | |
100 get; | |
101 set; | |
102 } | 134 } |
103 | 135 |
104 void ThrowInvalidCommand(Commands cmd) { | 136 void ThrowInvalidCommand(Commands cmd) { |
105 if (m_stateMachine.State == ExecutionState.Disposed) | 137 if (m_stateMachine.State == ExecutionState.Disposed) |
106 throw new ObjectDisposedException(ToString()); | 138 throw new ObjectDisposedException(ToString()); |
154 current = m_stateMachine.State; | 186 current = m_stateMachine.State; |
155 | 187 |
156 ret = m_pending; | 188 ret = m_pending; |
157 m_pending = pending; | 189 m_pending = pending; |
158 m_lastError = error; | 190 m_lastError = error; |
159 | 191 |
160 } | 192 } |
161 if(prev != current) | 193 if (prev != current) |
162 OnStateChanged(prev, current, error); | 194 OnStateChanged(prev, current, error); |
163 return ret; | 195 return ret; |
164 } | 196 } |
165 | 197 |
198 /// <summary> | |
199 /// Handles the state of the component change event, raises the <see cref="StateChanged"/> event, handles | |
200 /// the transition to the <see cref="ExecutionState.Disposed"/> state (calls <see cref="Dispose(bool)"/> method). | |
201 /// </summary> | |
202 /// <param name="previous">The previous state</param> | |
203 /// <param name="current">The current state</param> | |
204 /// <param name="error">The last error if any.</param> | |
205 /// <remarks> | |
206 /// <para> | |
207 /// If the previous state and the current state are same this method isn't called, such situiation is treated | |
208 /// as the component hasn't changed it's state. | |
209 /// </para> | |
210 /// <para> | |
211 /// When overriding this method ensure the call is made to the base implementation, otherwise it will lead to | |
212 /// the wrong behavior of the component. | |
213 /// </para> | |
214 /// </remarks> | |
166 protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) { | 215 protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) { |
167 var h = StateChanged; | 216 StateChanged.DispatchEvent( |
168 if (h != null) | 217 this, |
169 h(this, new StateChangeEventArgs { | 218 new StateChangeEventArgs { |
170 State = current, | 219 State = current, |
171 LastError = error | 220 LastError = error |
172 }); | 221 } |
222 ); | |
223 if (current == ExecutionState.Disposed) { | |
224 GC.SuppressFinalize(this); | |
225 Dispose(true); | |
226 } | |
173 } | 227 } |
174 | 228 |
175 /// <summary> | 229 /// <summary> |
176 /// Moves the component from running to failed state. | 230 /// Moves the component from running to failed state. |
177 /// </summary> | 231 /// </summary> |
276 protected virtual IPromise OnStart() { | 330 protected virtual IPromise OnStart() { |
277 return Promise.Success; | 331 return Promise.Success; |
278 } | 332 } |
279 | 333 |
280 public IPromise Stop() { | 334 public IPromise Stop() { |
281 var pending = InvokeAsync(Commands.Stop, OnStop, StopPending); | 335 return InvokeAsync(Commands.Stop, OnStop, StopPending); |
282 return m_reusable ? pending : pending.Then(Dispose); | |
283 } | 336 } |
284 | 337 |
285 protected virtual IPromise OnStop() { | 338 protected virtual IPromise OnStop() { |
286 return Promise.Success; | 339 return Promise.Success; |
287 } | 340 } |
334 /// especially if <see cref="Stop"/> method is failed. Using this method insted of <see cref="Stop()"/> may | 387 /// especially if <see cref="Stop"/> method is failed. Using this method insted of <see cref="Stop()"/> may |
335 /// lead to the data loss by the component. | 388 /// lead to the data loss by the component. |
336 /// </para></remarks> | 389 /// </para></remarks> |
337 [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")] | 390 [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")] |
338 public void Dispose() { | 391 public void Dispose() { |
339 IPromise pending; | 392 Move(Commands.Dispose, null, null); |
340 | |
341 lock (m_stateMachine) { | |
342 if (m_stateMachine.State == ExecutionState.Disposed) | |
343 return; | |
344 Move(Commands.Dispose, null, null); | |
345 } | |
346 | |
347 GC.SuppressFinalize(this); | |
348 Dispose(true); | |
349 } | 393 } |
350 | 394 |
351 ~RunnableComponent() { | 395 ~RunnableComponent() { |
352 Dispose(false); | 396 Dispose(false); |
353 } | 397 } |
358 /// Releases all resources used by the component, called automatically, override this method to implement your cleanup. | 402 /// Releases all resources used by the component, called automatically, override this method to implement your cleanup. |
359 /// </summary> | 403 /// </summary> |
360 /// <param name="disposing">true if this method is called during normal dispose process.</param> | 404 /// <param name="disposing">true if this method is called during normal dispose process.</param> |
361 /// <param name="pending">The operation which is currenty pending</param> | 405 /// <param name="pending">The operation which is currenty pending</param> |
362 protected virtual void Dispose(bool disposing) { | 406 protected virtual void Dispose(bool disposing) { |
363 | |
364 } | 407 } |
365 | 408 |
366 } | 409 } |
367 } | 410 } |
368 | 411 |