Mercurial > pub > ImplabNet
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 |