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