changeset 184:d6a8cba73acc ref20160224

working on runnable component
author cin
date Sat, 16 Apr 2016 03:23:26 +0300
parents 4f82e0f161c3
children 822aab37b107
files Implab.Test/Implab.Format.Test/JsonTests.cs Implab/Components/ExecutionState.cs Implab/Components/IInitializable.cs Implab/Components/RunnableComponent.cs
diffstat 4 files changed, 161 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/Implab.Test/Implab.Format.Test/JsonTests.cs	Fri Mar 25 02:49:02 2016 +0300
+++ b/Implab.Test/Implab.Format.Test/JsonTests.cs	Sat Apr 16 03:23:26 2016 +0300
@@ -10,7 +10,7 @@
         public void TestScannerValidTokens() {
             using (var scanner = new JSONScanner(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) {
 
-                Tuple<JsonTokenType,object>[] expexted = new [] {
+                Tuple<JsonTokenType,object>[] expexted = {
                     new Tuple<JsonTokenType,object>(JsonTokenType.Number, 9123d),
                     new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "),
                     new Tuple<JsonTokenType,object>(JsonTokenType.Number, -123d),
--- a/Implab/Components/ExecutionState.cs	Fri Mar 25 02:49:02 2016 +0300
+++ b/Implab/Components/ExecutionState.cs	Sat Apr 16 03:23:26 2016 +0300
@@ -1,14 +1,24 @@
 namespace Implab.Components {
     
     public enum ExecutionState {
-        Reserved = 0,
-        Uninitialized,
+        Undefined = 0,
+
+        Created,
+
+        Initializing,
+
         Ready,
+
         Starting,
+
         Running,
+
         Stopping,
-        Stopped,
+
+        Failed,
+
         Disposed,
-        Failed
+
+        Last = Disposed
     }
 }
\ No newline at end of file
--- a/Implab/Components/IInitializable.cs	Fri Mar 25 02:49:02 2016 +0300
+++ b/Implab/Components/IInitializable.cs	Sat Apr 16 03:23:26 2016 +0300
@@ -11,7 +11,7 @@
         /// Completes initialization.
         /// </summary>
         /// <remarks>
-        /// Normally virtual shouldn't be called from the constructor, due to the incomplete object state, but
+        /// Normally virtual methods shouldn't be called from the constructor, due to the incomplete object state, but
         /// they can be called from this method. This method is also usefull when we constructing a complex grpah
         /// of components where cyclic references may take place.
         /// </remarks>
--- 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() {