Mercurial > pub > ImplabNet
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) { - } }