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() {