diff Implab/Components/RunnableComponent.cs @ 210:5dc21f6a3222 v2

Code review for RunnableComponent Added StaApartment class based on System.Windows.Forms.Application message loop
author cin
date Mon, 20 Mar 2017 17:44:18 +0300
parents 7d07503621fe
children eee3e49dd1ff
line wrap: on
line diff
--- a/Implab/Components/RunnableComponent.cs	Wed Nov 16 03:06:08 2016 +0300
+++ b/Implab/Components/RunnableComponent.cs	Mon Mar 20 17:44:18 2017 +0300
@@ -15,52 +15,90 @@
         }
 
         class StateMachine {
-            static readonly ExecutionState[,] _transitions;
+            public static readonly ExecutionState[,] ReusableTransitions;
+            public static readonly ExecutionState[,] NonreusableTransitions;
+
+            class StateBuilder {
+                readonly ExecutionState[,] m_states;
+
+                public ExecutionState[,] States {
+                    get { return m_states; }
+                }
+                public StateBuilder(ExecutionState[,] states) {
+                    m_states = states;
+                }
+
+                public StateBuilder() {
+                    m_states = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
+                }
+
+                public StateBuilder Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
+                    m_states[(int)s1, (int)cmd] = s2;
+                    return this;
+                }
+
+                public StateBuilder Clone() {
+                    return new StateBuilder((ExecutionState[,])m_states.Clone());
+                }
+            }
 
             static StateMachine() {
-                _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
+                ReusableTransitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
 
-                Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init);
-                Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose);
+                var common = new StateBuilder()
+                    .Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init)
+                    .Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose)
 
-                Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok);
-                Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail);
+                    .Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok)
+                    .Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail)
 
-                Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start);
-                Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose);
+                    .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.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.Failed, ExecutionState.Disposed, Commands.Dispose)
+                    .Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset)
+
+                    .Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail)
+                    .Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose)
+
+                    .Edge(ExecutionState.Disposed, 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);
+                var reusable = common
+                    .Clone()
+                    .Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok);
+
+                var nonreusable = common
+                    .Clone()
+                    .Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
+                
+                NonreusableTransitions = nonreusable.States;
+                ReusableTransitions = reusable.States;
 
-                Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail);
-                Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok);
-                Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose);
-
-                Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose);
-                Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset);
             }
 
-            static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
-                _transitions[(int)s1, (int)cmd] = s2;
-            }
+            readonly ExecutionState[,] m_states;
 
             public ExecutionState State {
                 get;
                 private set;
             }
 
-            public StateMachine(ExecutionState initial) {
+            public StateMachine(ExecutionState[,] states, ExecutionState initial) {
                 State = initial;
+                m_states = states;
             }
 
             public bool Move(Commands cmd) {
-                var next = _transitions[(int)State, (int)cmd];
+                var next = m_states[(int)State, (int)cmd];
                 if (next == ExecutionState.Undefined)
                     return false;
                 State = next;
@@ -81,9 +119,11 @@
         /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param>
         /// <param name="reusable">If set, the component may start after it has been stopped, otherwise the component is disposed after being stopped.</param>
         protected RunnableComponent(bool initialized, bool reusable) {
-            m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created);
+            m_stateMachine = new StateMachine(
+                reusable ? StateMachine.ReusableTransitions : StateMachine.NonreusableTransitions,
+                initialized ? ExecutionState.Ready : ExecutionState.Created
+            );
             m_reusable = reusable;
-            DisposeTimeout = 10000;
         }
 
         /// <summary>
@@ -93,14 +133,6 @@
         protected RunnableComponent(bool initialized) : this(initialized, false) {
         }
 
-        /// <summary>
-        /// Gets or sets the timeout to wait for the pending operation to complete. If the pending operation doesn't finish than the component will be disposed anyway.
-        /// </summary>
-        protected int DisposeTimeout {
-            get;
-            set;
-        }
-
         void ThrowInvalidCommand(Commands cmd) {
             if (m_stateMachine.State == ExecutionState.Disposed)
                 throw new ObjectDisposedException(ToString());
@@ -155,21 +187,43 @@
 
                 ret = m_pending;
                 m_pending = pending;
-                m_lastError = error;
-                
+                m_lastError = error;
+
             }
-            if(prev != current)
+            if (prev != current)
                 OnStateChanged(prev, current, error);
             return ret;
         }
 
+        /// <summary>
+        /// Handles the state of the component change event, raises the <see cref="StateChanged"/>  event, handles
+        /// the transition to the <see cref="ExecutionState.Disposed"/> state (calls <see cref="Dispose(bool)"/> method).
+        /// </summary>
+        /// <param name="previous">The previous state</param>
+        /// <param name="current">The current state</param>
+        /// <param name="error">The last error if any.</param>
+        /// <remarks>
+        /// <para>
+        /// If the previous state and the current state are same this method isn't called, such situiation is treated
+        /// as the component hasn't changed it's state.
+        /// </para>
+        /// <para>
+        /// When overriding this method ensure the call is made to the base implementation, otherwise it will lead to 
+        /// the wrong behavior of the component.
+        /// </para>
+        /// </remarks>
         protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) {
-            var h = StateChanged;
-            if (h != null)
-                h(this, new StateChangeEventArgs {
+            StateChanged.DispatchEvent(
+                this,
+                new StateChangeEventArgs {
                     State = current,
                     LastError = error
-                });
+                }
+            );
+            if (current == ExecutionState.Disposed) {
+                GC.SuppressFinalize(this);
+                Dispose(true);
+            }
         }
 
         /// <summary>
@@ -278,8 +332,7 @@
         }
 
         public IPromise Stop() {
-            var pending = InvokeAsync(Commands.Stop, OnStop, StopPending);
-            return m_reusable ? pending : pending.Then(Dispose);
+            return InvokeAsync(Commands.Stop, OnStop, StopPending);
         }
 
         protected virtual IPromise OnStop() {
@@ -336,16 +389,7 @@
         /// </para></remarks>
         [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")]
         public void Dispose() {
-            IPromise pending;
-
-            lock (m_stateMachine) {
-                if (m_stateMachine.State == ExecutionState.Disposed)
-                    return;
-                Move(Commands.Dispose, null, null);
-            }
-
-            GC.SuppressFinalize(this);
-            Dispose(true);
+            Move(Commands.Dispose, null, null);
         }
 
         ~RunnableComponent() {
@@ -360,7 +404,6 @@
         /// <param name="disposing">true if this method is called during normal dispose process.</param>
         /// <param name="pending">The operation which is currenty pending</param>
         protected virtual void Dispose(bool disposing) {
-
         }
 
     }