comparison Implab/Components/RunnableComponent.cs @ 185:822aab37b107 ref20160224

runnable component, work in progress
author cin
date Mon, 18 Apr 2016 16:41:17 +0300
parents d6a8cba73acc
children 75103928da09
comparison
equal deleted inserted replaced
184:d6a8cba73acc 185:822aab37b107
1 using System; 1 using System;
2 using Implab.Formats;
3 2
4 namespace Implab.Components { 3 namespace Implab.Components {
5 public class RunnableComponent : Disposable, IRunnable, IInitializable { 4 public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable {
6 enum Commands { 5 enum Commands {
7 Ok = 0, 6 Ok = 0,
8 Fail, 7 Fail,
9 Init, 8 Init,
10 Start, 9 Start,
17 static readonly ExecutionState[,] _transitions; 16 static readonly ExecutionState[,] _transitions;
18 17
19 static StateMachine() { 18 static StateMachine() {
20 _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; 19 _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
21 20
22 Edge(ExecutionState.Created, ExecutionState.Ready, Commands.Ok); 21 Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init);
23 Edge(ExecutionState.Created, ExecutionState.Failed, Commands.Fail); 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);
24 26
25 Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); 27 Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start);
26 Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); 28 Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose);
27 29
28 Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok); 30 Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok);
34 Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop); 36 Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop);
35 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose); 37 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose);
36 38
37 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); 39 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail);
38 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); 40 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
39 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose); 41
42 Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose);
40 } 43 }
41 44
42 static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { 45 static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
43 _transitions[(int)s1, (int)cmd] = s2; 46 _transitions[(int)s1, (int)cmd] = s2;
44 } 47 }
68 71
69 protected RunnableComponent(bool initialized) { 72 protected RunnableComponent(bool initialized) {
70 m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); 73 m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created);
71 } 74 }
72 75
76 protected virtual int DisposeTimeout {
77 get {
78 return 10000;
79 }
80 }
81
73 void ThrowInvalidCommand(Commands cmd) { 82 void ThrowInvalidCommand(Commands cmd) {
83 if (m_stateMachine.State == ExecutionState.Disposed)
84 throw new ObjectDisposedException(ToString());
85
74 throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); 86 throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
75 } 87 }
76 88
77 protected void Move(Commands cmd) { 89 void Move(Commands cmd) {
78 lock (m_stateMachine) 90 if (!m_stateMachine.Move(cmd))
79 if (!m_stateMachine.Move(cmd)) 91 ThrowInvalidCommand(cmd);
80 ThrowInvalidCommand(cmd); 92 }
81 } 93
82 94 void Invoke(Commands cmd, Action action) {
83 protected void Fail(Exception err) { 95 lock (m_stateMachine)
84 lock (m_stateMachine) { 96 Move(cmd);
85 if (!m_stateMachine.Move(Commands.Fail)) 97
86 ThrowInvalidCommand(Commands.Fail);
87
88 m_lastError = err;
89 }
90 }
91
92 protected void Success() {
93 Move(Commands.Ok);
94 }
95
96 protected void Invoke(Commands cmd, Action action) {
97 Move(cmd);
98 try { 98 try {
99 action(); 99 action();
100 Move(Commands.Ok); 100 lock(m_stateMachine)
101 Move(Commands.Ok);
102
101 } catch (Exception err) { 103 } catch (Exception err) {
102 Fail(err); 104 lock (m_stateMachine) {
105 Move(Commands.Fail);
106 m_lastError = err;
107 }
103 throw; 108 throw;
104 } 109 }
105 } 110 }
106 111
107 protected IPromise InvokeAsync(Commands cmd, Func<IPromise> action) { 112 IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) {
108 Move(cmd);
109 var medium = new Promise();
110
111 IPromise promise = null; 113 IPromise promise = null;
112 114 IPromise prev;
113 promise = medium.Then( 115
114 () => { 116 var task = new ActionChainTask(action, null, null, true);
115 lock(m_stateMachine) { 117
116 if (m_pending == promise) { 118 lock (m_stateMachine) {
117 m_pending = null; 119 Move(cmd);
118 Move(Commands.Ok); 120
121 prev = m_pending;
122
123 promise = task.Then(
124 () => {
125 lock(m_stateMachine) {
126 if (m_pending == promise) {
127 Move(Commands.Ok);
128 m_pending = null;
129 }
119 } 130 }
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 => {
142 lock(m_stateMachine) {
143 if (m_pending == promise) {
144 Move(Commands.Fail);
145 m_pending = null;
146 m_lastError = new OperationCanceledException("The operation has been cancelled", r);
147 }
148
149 }
150 throw new OperationCanceledException("The operation has been cancelled", r);
120 } 151 }
121 }, e => { 152 );
122 if (m_pending == promise) { 153
123 m_pending = null; 154 m_pending = promise;
124 Fail( 155 }
125 } 156
126 } 157 if (prev == null)
127 ); 158 task.Resolve();
128 159 else
129 160 chain(prev, task);
130 161
131 return Safe.InvokePromise(action).Then( 162 return promise;
132 Success,
133 Fail
134 );
135 }
136
137 void AddPending(IPromise result) {
138
139 } 163 }
140 164
141 165
142 #region IInitializable implementation 166 #region IInitializable implementation
143 167
151 #endregion 175 #endregion
152 176
153 #region IRunnable implementation 177 #region IRunnable implementation
154 178
155 public IPromise Start() { 179 public IPromise Start() {
156 Move(Commands.Start); 180 return InvokeAsync(Commands.Start, OnStart, null);
157
158 return Safe.InvokePromise(OnStart).Then(
159 () => {
160 Move(Commands.Ok);
161 Run();
162 },
163 () => {
164 Move(Commands.Fail);
165 }
166 );
167 } 181 }
168 182
169 protected virtual IPromise OnStart() { 183 protected virtual IPromise OnStart() {
170 return Promise.SUCCESS; 184 return Promise.SUCCESS;
171 } 185 }
172 186
173 protected virtual void Run() {
174 }
175
176 public IPromise Stop() { 187 public IPromise Stop() {
177 throw new NotImplementedException(); 188 return InvokeAsync(Commands.Stop, OnStop, StopPending).Then(Dispose);
189 }
190
191 protected virtual IPromise OnStop() {
192 return Promise.SUCCESS;
193 }
194
195 /// <summary>
196 /// Stops the current operation if one exists.
197 /// </summary>
198 /// <param name="current">Current.</param>
199 /// <param name="stop">Stop.</param>
200 protected virtual void StopPending(IPromise current, IDeferred stop) {
201 if (current == null) {
202 stop.Resolve();
203 } else {
204 current.On(stop.Resolve, stop.Reject, stop.CancelOperation);
205 current.Cancel();
206 }
178 } 207 }
179 208
180 public ExecutionState State { 209 public ExecutionState State {
181 get { 210 get {
182 throw new NotImplementedException(); 211 return m_stateMachine.State;
183 } 212 }
184 } 213 }
185 214
186 public Exception LastError { 215 public Exception LastError {
187 get { 216 get {
188 throw new NotImplementedException(); 217 return m_lastError;
189 } 218 }
190 } 219 }
191 220
192 #endregion 221 #endregion
222
223 #region IDisposable implementation
224
225 public void Dispose() {
226 IPromise pending;
227 lock (m_stateMachine) {
228 if (m_stateMachine.State == ExecutionState.Disposed)
229 return;
230
231 Move(Commands.Dispose);
232
233 GC.SuppressFinalize(this);
234
235 pending = m_pending;
236 m_pending = null;
237 }
238 if (pending != null) {
239 pending.Cancel();
240 pending.Timeout(DisposeTimeout).On(
241 () => Dispose(true, null),
242 err => Dispose(true, err),
243 reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason))
244 );
245 } else {
246 Dispose(true, m_lastError);
247 }
248 }
249
250 ~RunnableComponent() {
251 Dispose(false, null);
252 }
253
254 #endregion
255
256 protected virtual void Dispose(bool disposing, Exception lastError) {
257
258 }
259
193 } 260 }
194 } 261 }
195 262