Mercurial > pub > ImplabNet
comparison Implab/Components/RunnableComponent.cs @ 184:d6a8cba73acc ref20160224
working on runnable component
| author | cin |
|---|---|
| date | Sat, 16 Apr 2016 03:23:26 +0300 |
| parents | c32688129f14 |
| children | 822aab37b107 |
comparison
equal
deleted
inserted
replaced
| 183:4f82e0f161c3 | 184:d6a8cba73acc |
|---|---|
| 1 using System; | 1 using System; |
| 2 using Implab.Formats; | 2 using Implab.Formats; |
| 3 | 3 |
| 4 namespace Implab.Components { | 4 namespace Implab.Components { |
| 5 public class RunnableComponent : Disposable, IRunnable, IInitializable { | 5 public class RunnableComponent : Disposable, IRunnable, IInitializable { |
| 6 | 6 enum Commands { |
| 7 Ok = 0, | |
| 8 Fail, | |
| 9 Init, | |
| 10 Start, | |
| 11 Stop, | |
| 12 Dispose, | |
| 13 Last = Dispose | |
| 14 } | |
| 15 | |
| 16 class StateMachine { | |
| 17 static readonly ExecutionState[,] _transitions; | |
| 18 | |
| 19 static StateMachine() { | |
| 20 _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; | |
| 21 | |
| 22 Edge(ExecutionState.Created, ExecutionState.Ready, Commands.Ok); | |
| 23 Edge(ExecutionState.Created, ExecutionState.Failed, Commands.Fail); | |
| 24 | |
| 25 Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); | |
| 26 Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); | |
| 27 | |
| 28 Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok); | |
| 29 Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail); | |
| 30 Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop); | |
| 31 Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose); | |
| 32 | |
| 33 Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail); | |
| 34 Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop); | |
| 35 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose); | |
| 36 | |
| 37 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); | |
| 38 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); | |
| 39 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose); | |
| 40 } | |
| 41 | |
| 42 static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { | |
| 43 _transitions[(int)s1, (int)cmd] = s2; | |
| 44 } | |
| 45 | |
| 46 public ExecutionState State { | |
| 47 get; | |
| 48 private set; | |
| 49 } | |
| 50 | |
| 51 public StateMachine(ExecutionState initial) { | |
| 52 State = initial; | |
| 53 } | |
| 54 | |
| 55 public bool Move(Commands cmd) { | |
| 56 var next = _transitions[(int)State, (int)cmd]; | |
| 57 if (next == ExecutionState.Undefined) | |
| 58 return false; | |
| 59 State = next; | |
| 60 return true; | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 IPromise m_pending; | |
| 65 Exception m_lastError; | |
| 66 | |
| 67 readonly StateMachine m_stateMachine; | |
| 68 | |
| 69 protected RunnableComponent(bool initialized) { | |
| 70 m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); | |
| 71 } | |
| 72 | |
| 73 void ThrowInvalidCommand(Commands cmd) { | |
| 74 throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); | |
| 75 } | |
| 76 | |
| 77 protected void Move(Commands cmd) { | |
| 78 lock (m_stateMachine) | |
| 79 if (!m_stateMachine.Move(cmd)) | |
| 80 ThrowInvalidCommand(cmd); | |
| 81 } | |
| 82 | |
| 83 protected void Fail(Exception err) { | |
| 84 lock (m_stateMachine) { | |
| 85 if (!m_stateMachine.Move(Commands.Fail)) | |
| 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 { | |
| 99 action(); | |
| 100 Move(Commands.Ok); | |
| 101 } catch (Exception err) { | |
| 102 Fail(err); | |
| 103 throw; | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 protected IPromise InvokeAsync(Commands cmd, Func<IPromise> action) { | |
| 108 Move(cmd); | |
| 109 var medium = new Promise(); | |
| 110 | |
| 111 IPromise promise = null; | |
| 112 | |
| 113 promise = medium.Then( | |
| 114 () => { | |
| 115 lock(m_stateMachine) { | |
| 116 if (m_pending == promise) { | |
| 117 m_pending = null; | |
| 118 Move(Commands.Ok); | |
| 119 } | |
| 120 } | |
| 121 }, e => { | |
| 122 if (m_pending == promise) { | |
| 123 m_pending = null; | |
| 124 Fail( | |
| 125 } | |
| 126 } | |
| 127 ); | |
| 7 | 128 |
| 8 | 129 |
| 9 | 130 |
| 10 | 131 return Safe.InvokePromise(action).Then( |
| 11 IPromise m_pending; | 132 Success, |
| 12 Exception m_lastError; | 133 Fail |
| 134 ); | |
| 135 } | |
| 13 | 136 |
| 14 protected RunnableComponent(bool initialized) { | 137 void AddPending(IPromise result) { |
| 15 | 138 |
| 16 } | 139 } |
| 140 | |
| 17 | 141 |
| 18 #region IInitializable implementation | 142 #region IInitializable implementation |
| 19 | 143 |
| 20 public void Init() { | 144 public void Init() { |
| 21 | 145 Invoke(Commands.Init, OnInitialize); |
| 146 } | |
| 147 | |
| 148 protected virtual void OnInitialize() { | |
| 22 } | 149 } |
| 23 | 150 |
| 24 #endregion | 151 #endregion |
| 25 | 152 |
| 26 #region IRunnable implementation | 153 #region IRunnable implementation |
| 27 | 154 |
| 28 public IPromise Start() { | 155 public IPromise Start() { |
| 29 throw new NotImplementedException(); | 156 Move(Commands.Start); |
| 157 | |
| 158 return Safe.InvokePromise(OnStart).Then( | |
| 159 () => { | |
| 160 Move(Commands.Ok); | |
| 161 Run(); | |
| 162 }, | |
| 163 () => { | |
| 164 Move(Commands.Fail); | |
| 165 } | |
| 166 ); | |
| 30 } | 167 } |
| 31 | 168 |
| 32 protected virtual IPromise OnStart() { | 169 protected virtual IPromise OnStart() { |
| 33 return Promise.SUCCESS; | 170 return Promise.SUCCESS; |
| 34 } | 171 } |
