Mercurial > pub > ImplabNet
diff Implab/Components/RunnableComponent.cs @ 185:822aab37b107 ref20160224
runnable component, work in progress
author | cin |
---|---|
date | Mon, 18 Apr 2016 16:41:17 +0300 |
parents | d6a8cba73acc |
children | 75103928da09 |
line wrap: on
line diff
--- a/Implab/Components/RunnableComponent.cs Sat Apr 16 03:23:26 2016 +0300 +++ b/Implab/Components/RunnableComponent.cs Mon Apr 18 16:41:17 2016 +0300 @@ -1,8 +1,7 @@ using System; -using Implab.Formats; namespace Implab.Components { - public class RunnableComponent : Disposable, IRunnable, IInitializable { + public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable { enum Commands { Ok = 0, Fail, @@ -19,8 +18,11 @@ 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.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.Ready, ExecutionState.Starting, Commands.Start); Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); @@ -36,7 +38,8 @@ Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); - Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose); + + Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose); } static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { @@ -70,72 +73,93 @@ m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); } + protected virtual int DisposeTimeout { + get { + return 10000; + } + } + void ThrowInvalidCommand(Commands cmd) { + if (m_stateMachine.State == ExecutionState.Disposed) + throw new ObjectDisposedException(ToString()); + 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); + void Move(Commands cmd) { + 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); + void Invoke(Commands cmd, Action action) { + lock (m_stateMachine) + Move(cmd); + try { action(); - Move(Commands.Ok); + lock(m_stateMachine) + Move(Commands.Ok); + } catch (Exception err) { - Fail(err); + lock (m_stateMachine) { + Move(Commands.Fail); + m_lastError = err; + } throw; } } - protected IPromise InvokeAsync(Commands cmd, Func<IPromise> action) { - Move(cmd); - var medium = new Promise(); + IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) { + IPromise promise = null; + IPromise prev; - IPromise promise = null; + var task = new ActionChainTask(action, null, null, true); + + lock (m_stateMachine) { + Move(cmd); + + prev = m_pending; - promise = medium.Then( - () => { - lock(m_stateMachine) { - if (m_pending == promise) { - m_pending = null; - Move(Commands.Ok); + promise = task.Then( + () => { + lock(m_stateMachine) { + if (m_pending == promise) { + Move(Commands.Ok); + m_pending = null; + } } + }, e => { + lock(m_stateMachine) { + if (m_pending == promise) { + Move(Commands.Fail); + m_pending = null; + m_lastError = e; + } + } + throw new PromiseTransientException(e); + }, + r => { + lock(m_stateMachine) { + if (m_pending == promise) { + Move(Commands.Fail); + m_pending = null; + m_lastError = new OperationCanceledException("The operation has been cancelled", r); + } + + } + throw new OperationCanceledException("The operation has been cancelled", r); } - }, e => { - if (m_pending == promise) { - m_pending = null; - Fail( - } - } - ); + ); + m_pending = promise; + } + if (prev == null) + task.Resolve(); + else + chain(prev, task); - return Safe.InvokePromise(action).Then( - Success, - Fail - ); - } - - void AddPending(IPromise result) { - + return promise; } @@ -153,43 +177,86 @@ #region IRunnable implementation public IPromise Start() { - Move(Commands.Start); - - return Safe.InvokePromise(OnStart).Then( - () => { - Move(Commands.Ok); - Run(); - }, - () => { - Move(Commands.Fail); - } - ); + return InvokeAsync(Commands.Start, OnStart, null); } protected virtual IPromise OnStart() { return Promise.SUCCESS; } - protected virtual void Run() { + public IPromise Stop() { + return InvokeAsync(Commands.Stop, OnStop, StopPending).Then(Dispose); + } + + protected virtual IPromise OnStop() { + return Promise.SUCCESS; } - public IPromise Stop() { - throw new NotImplementedException(); + /// <summary> + /// Stops the current operation if one exists. + /// </summary> + /// <param name="current">Current.</param> + /// <param name="stop">Stop.</param> + protected virtual void StopPending(IPromise current, IDeferred stop) { + if (current == null) { + stop.Resolve(); + } else { + current.On(stop.Resolve, stop.Reject, stop.CancelOperation); + current.Cancel(); + } } public ExecutionState State { get { - throw new NotImplementedException(); + return m_stateMachine.State; } } public Exception LastError { get { - throw new NotImplementedException(); + return m_lastError; } } #endregion + + #region IDisposable implementation + + public void Dispose() { + IPromise pending; + lock (m_stateMachine) { + if (m_stateMachine.State == ExecutionState.Disposed) + return; + + Move(Commands.Dispose); + + GC.SuppressFinalize(this); + + pending = m_pending; + m_pending = null; + } + if (pending != null) { + pending.Cancel(); + pending.Timeout(DisposeTimeout).On( + () => Dispose(true, null), + err => Dispose(true, err), + reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason)) + ); + } else { + Dispose(true, m_lastError); + } + } + + ~RunnableComponent() { + Dispose(false, null); + } + + #endregion + + protected virtual void Dispose(bool disposing, Exception lastError) { + + } + } }