RunnableComponent.cs
195 lines
| 5.5 KiB
| text/x-csharp
|
CSharpLexer
|
|
r156 | using System; | ||
|
|
r180 | using Implab.Formats; | ||
|
|
r156 | |||
| namespace Implab.Components { | ||||
| public class RunnableComponent : Disposable, IRunnable, IInitializable { | ||||
|
|
r184 | enum Commands { | ||
| Ok = 0, | ||||
| Fail, | ||||
| Init, | ||||
| Start, | ||||
| Stop, | ||||
| Dispose, | ||||
| Last = Dispose | ||||
| } | ||||
| class StateMachine { | ||||
| static readonly ExecutionState[,] _transitions; | ||||
| static StateMachine() { | ||||
| _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; | ||||
| Edge(ExecutionState.Created, ExecutionState.Ready, Commands.Ok); | ||||
| Edge(ExecutionState.Created, ExecutionState.Failed, Commands.Fail); | ||||
| Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); | ||||
| Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); | ||||
| Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok); | ||||
| Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail); | ||||
| Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop); | ||||
| Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose); | ||||
| Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail); | ||||
| Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop); | ||||
| Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose); | ||||
| Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); | ||||
| Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); | ||||
| Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose); | ||||
| } | ||||
| static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { | ||||
| _transitions[(int)s1, (int)cmd] = s2; | ||||
| } | ||||
| public ExecutionState State { | ||||
| get; | ||||
| private set; | ||||
| } | ||||
| public StateMachine(ExecutionState initial) { | ||||
| State = initial; | ||||
| } | ||||
| public bool Move(Commands cmd) { | ||||
| var next = _transitions[(int)State, (int)cmd]; | ||||
| if (next == ExecutionState.Undefined) | ||||
| return false; | ||||
| State = next; | ||||
| return true; | ||||
| } | ||||
| } | ||||
| IPromise m_pending; | ||||
| Exception m_lastError; | ||||
| readonly StateMachine m_stateMachine; | ||||
| protected RunnableComponent(bool initialized) { | ||||
| m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); | ||||
| } | ||||
| void ThrowInvalidCommand(Commands cmd) { | ||||
| throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); | ||||
| } | ||||
| protected void Move(Commands cmd) { | ||||
| lock (m_stateMachine) | ||||
| if (!m_stateMachine.Move(cmd)) | ||||
| ThrowInvalidCommand(cmd); | ||||
| } | ||||
| protected void Fail(Exception err) { | ||||
| lock (m_stateMachine) { | ||||
| if (!m_stateMachine.Move(Commands.Fail)) | ||||
| ThrowInvalidCommand(Commands.Fail); | ||||
| m_lastError = err; | ||||
| } | ||||
| } | ||||
| protected void Success() { | ||||
| Move(Commands.Ok); | ||||
| } | ||||
| protected void Invoke(Commands cmd, Action action) { | ||||
| Move(cmd); | ||||
| try { | ||||
| action(); | ||||
| Move(Commands.Ok); | ||||
| } catch (Exception err) { | ||||
| Fail(err); | ||||
| throw; | ||||
| } | ||||
| } | ||||
| protected IPromise InvokeAsync(Commands cmd, Func<IPromise> action) { | ||||
| Move(cmd); | ||||
| var medium = new Promise(); | ||||
| IPromise promise = null; | ||||
| promise = medium.Then( | ||||
| () => { | ||||
| lock(m_stateMachine) { | ||||
| if (m_pending == promise) { | ||||
| m_pending = null; | ||||
| Move(Commands.Ok); | ||||
| } | ||||
| } | ||||
| }, e => { | ||||
| if (m_pending == promise) { | ||||
| m_pending = null; | ||||
| Fail( | ||||
| } | ||||
| } | ||||
| ); | ||||
|
|
r157 | |||
|
|
r156 | |||
|
|
r184 | return Safe.InvokePromise(action).Then( | ||
| Success, | ||||
| Fail | ||||
| ); | ||||
| } | ||||
|
|
r156 | |||
|
|
r184 | void AddPending(IPromise result) { | ||
|
|
r156 | } | ||
|
|
r184 | |||
|
|
r156 | #region IInitializable implementation | ||
| public void Init() { | ||||
|
|
r184 | Invoke(Commands.Init, OnInitialize); | ||
| } | ||||
| protected virtual void OnInitialize() { | ||||
|
|
r156 | } | ||
| #endregion | ||||
| #region IRunnable implementation | ||||
| public IPromise Start() { | ||||
|
|
r184 | Move(Commands.Start); | ||
| return Safe.InvokePromise(OnStart).Then( | ||||
| () => { | ||||
| Move(Commands.Ok); | ||||
| Run(); | ||||
| }, | ||||
| () => { | ||||
| Move(Commands.Fail); | ||||
| } | ||||
| ); | ||||
|
|
r156 | } | ||
| protected virtual IPromise OnStart() { | ||||
| return Promise.SUCCESS; | ||||
| } | ||||
| protected virtual void Run() { | ||||
| } | ||||
| public IPromise Stop() { | ||||
| throw new NotImplementedException(); | ||||
| } | ||||
| public ExecutionState State { | ||||
| get { | ||||
| throw new NotImplementedException(); | ||||
| } | ||||
| } | ||||
| public Exception LastError { | ||||
| get { | ||||
| throw new NotImplementedException(); | ||||
| } | ||||
| } | ||||
| #endregion | ||||
| } | ||||
| } | ||||
