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 |
