Mercurial > pub > ImplabNet
diff 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 |
line wrap: on
line diff
--- a/Implab/Components/RunnableComponent.cs Fri Mar 25 02:49:02 2016 +0300 +++ b/Implab/Components/RunnableComponent.cs Sat Apr 16 03:23:26 2016 +0300 @@ -3,22 +3,149 @@ namespace Implab.Components { public class RunnableComponent : Disposable, IRunnable, IInitializable { - + 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( + } + } + ); - - IPromise m_pending; - Exception m_lastError; + return Safe.InvokePromise(action).Then( + Success, + Fail + ); + } - protected RunnableComponent(bool initialized) { - + void AddPending(IPromise result) { + } + #region IInitializable implementation public void Init() { - + Invoke(Commands.Init, OnInitialize); + } + + protected virtual void OnInitialize() { } #endregion @@ -26,7 +153,17 @@ #region IRunnable implementation public IPromise Start() { - throw new NotImplementedException(); + Move(Commands.Start); + + return Safe.InvokePromise(OnStart).Then( + () => { + Move(Commands.Ok); + Run(); + }, + () => { + Move(Commands.Fail); + } + ); } protected virtual IPromise OnStart() {