##// END OF EJS Templates
working on promises
working on promises

File last commit:

r244:eee3e49dd1ff v3
r247:fb70574741a1 v3
Show More
RunnableComponent.cs
411 lines | 16.2 KiB | text/x-csharp | CSharpLexer
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 using System;
cin
working on promises
r244 using System.Diagnostics.CodeAnalysis;
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 namespace Implab.Components {
cin
runnable component, work in progress
r185 public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable {
cin
working on runnable component
r184 enum Commands {
Ok = 0,
Fail,
Init,
Start,
Stop,
Dispose,
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 Reset,
Last = Reset
cin
working on runnable component
r184 }
class StateMachine {
cin
Code review for RunnableComponent...
r210 public static readonly ExecutionState[,] ReusableTransitions;
public static readonly ExecutionState[,] NonreusableTransitions;
cin
working on promises
r244 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());
}
cin
Code review for RunnableComponent...
r210 }
cin
working on runnable component
r184
static StateMachine() {
cin
Code review for RunnableComponent...
r210 ReusableTransitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
cin
working on runnable component
r184
cin
Code review for RunnableComponent...
r210 var common = new StateBuilder()
.Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init)
.Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose)
cin
runnable component, work in progress
r185
cin
Code review for RunnableComponent...
r210 .Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok)
.Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail)
cin
working on runnable component
r184
cin
Code review for RunnableComponent...
r210 .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)
cin
working on runnable component
r184
cin
Code review for RunnableComponent...
r210 .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)
cin
working on promises
r244 .Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset)
cin
Code review for RunnableComponent...
r210 .Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail)
.Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose)
.Edge(ExecutionState.Disposed, ExecutionState.Disposed, Commands.Dispose);
cin
working on runnable component
r184
cin
Code review for RunnableComponent...
r210 var reusable = common
.Clone()
cin
working on promises
r244 .Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok);
var nonreusable = common
.Clone()
.Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
cin
Code review for RunnableComponent...
r210
NonreusableTransitions = nonreusable.States;
ReusableTransitions = reusable.States;
cin
working on runnable component
r184
}
cin
Code review for RunnableComponent...
r210 readonly ExecutionState[,] m_states;
cin
working on runnable component
r184
public ExecutionState State {
get;
private set;
}
cin
Code review for RunnableComponent...
r210 public StateMachine(ExecutionState[,] states, ExecutionState initial) {
cin
working on runnable component
r184 State = initial;
cin
Code review for RunnableComponent...
r210 m_states = states;
cin
working on runnable component
r184 }
public bool Move(Commands cmd) {
cin
Code review for RunnableComponent...
r210 var next = m_states[(int)State, (int)cmd];
cin
working on runnable component
r184 if (next == ExecutionState.Undefined)
return false;
State = next;
return true;
}
}
IPromise m_pending;
Exception m_lastError;
cin
working on promises
r244 readonly StateMachine m_stateMachine;
readonly bool m_reusable;
public event EventHandler<StateChangeEventArgs> StateChanged;
/// <summary>
/// Initializes component state.
/// </summary>
/// <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) {
cin
Code review for RunnableComponent...
r210 m_stateMachine = new StateMachine(
reusable ? StateMachine.ReusableTransitions : StateMachine.NonreusableTransitions,
initialized ? ExecutionState.Ready : ExecutionState.Created
);
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 m_reusable = reusable;
cin
working on promises
r244 }
/// <summary>
/// Initializes component state. The component created with this constructor is not reusable, i.e. it will be disposed after stop.
/// </summary>
/// <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>
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 protected RunnableComponent(bool initialized) : this(initialized, false) {
cin
working on runnable component
r184 }
void ThrowInvalidCommand(Commands cmd) {
cin
runnable component, work in progress
r185 if (m_stateMachine.State == ExecutionState.Disposed)
cin
working on promises
r244 throw new ObjectDisposedException(ToString());
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 throw new InvalidOperationException(String.Format("Command {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
}
bool MoveIfInState(Commands cmd, IPromise pending, Exception error, ExecutionState state) {
ExecutionState prev, current;
cin
working on promises
r244 lock (m_stateMachine) {
if (m_stateMachine.State != state)
return false;
prev = m_stateMachine.State;
if (!m_stateMachine.Move(cmd))
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 ThrowInvalidCommand(cmd);
current = m_stateMachine.State;
m_pending = pending;
m_lastError = error;
}
if (prev != current)
OnStateChanged(prev, current, error);
return true;
cin
working on runnable component
r184 }
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 bool MoveIfPending(Commands cmd, IPromise pending, Exception error, IPromise expected) {
ExecutionState prev, current;
cin
working on promises
r244 lock (m_stateMachine) {
if (m_pending != expected)
return false;
prev = m_stateMachine.State;
if (!m_stateMachine.Move(cmd))
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 ThrowInvalidCommand(cmd);
current = m_stateMachine.State;
m_pending = pending;
m_lastError = error;
cin
working on promises
r244 }
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 if (prev != current)
cin
working on promises
r244 OnStateChanged(prev, current, error);
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 return true;
}
IPromise Move(Commands cmd, IPromise pending, Exception error) {
ExecutionState prev, current;
IPromise ret;
cin
working on promises
r244 lock (m_stateMachine) {
prev = m_stateMachine.State;
if (!m_stateMachine.Move(cmd))
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 ThrowInvalidCommand(cmd);
current = m_stateMachine.State;
ret = m_pending;
m_pending = pending;
cin
working on promises
r244 m_lastError = error;
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 }
cin
Code review for RunnableComponent...
r210 if (prev != current)
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 OnStateChanged(prev, current, error);
return ret;
}
cin
working on promises
r244 /// <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>
cin
Code review for RunnableComponent...
r210 /// <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>
cin
working on promises
r244 protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) {
StateChanged.DispatchEvent(
this,
new StateChangeEventArgs {
State = current,
LastError = error
}
);
if (current == ExecutionState.Disposed) {
GC.SuppressFinalize(this);
Dispose(true);
}
cin
working on runnable component
r184 }
cin
Added 'Fail' method to RunnableComponent which allows component to move from...
r203 /// <summary>
/// Moves the component from running to failed state.
/// </summary>
/// <param name="error">The exception which is describing the error.</param>
cin
working on promises
r244 protected bool Fail(Exception error) {
return MoveIfInState(Commands.Fail, null, error, ExecutionState.Running);
cin
Added 'Fail' method to RunnableComponent which allows component to move from...
r203 }
cin
working on promises
r244 /// <summary>
/// Tries to reset <see cref="ExecutionState.Failed"/> state to <see cref="ExecutionState.Ready"/>.
/// </summary>
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 /// <returns>True if component is reset to <see cref="ExecutionState.Ready"/>, false if the componet wasn't
/// in <see cref="ExecutionState.Failed"/> state.</returns>
/// <remarks>
/// This method checks the current state of the component and if it's in <see cref="ExecutionState.Failed"/>
/// moves component to <see cref="ExecutionState.Initializing"/>.
/// The <see cref="OnResetState()"/> is called and if this method completes succesfully the component moved
/// to <see cref="ExecutionState.Ready"/> state, otherwise the component is moved to <see cref="ExecutionState.Failed"/>
/// state. If <see cref="OnResetState()"/> throws an exception it will be propagated by this method to the caller.
/// </remarks>
cin
working on promises
r244 protected bool ResetState() {
if (!MoveIfInState(Commands.Reset, null, null, ExecutionState.Failed))
return false;
cin
working on runnable component
r184 try {
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 OnResetState();
Move(Commands.Ok, null, null);
return true;
cin
working on runnable component
r184 } catch (Exception err) {
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 Move(Commands.Fail, null, err);
cin
working on runnable component
r184 throw;
cin
working on promises
r244 }
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 }
cin
working on promises
r244 /// <summary>
/// This method is called by <see cref="ResetState"/> to reinitialize component in the failed state.
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 /// </summary>
/// <remarks>
/// Default implementation throws <see cref="NotImplementedException"/> which will cause the component
/// fail to reset it's state and it left in <see cref="ExecutionState.Failed"/> state.
/// If this method doesn't throw exceptions the component is moved to <see cref="ExecutionState.Ready"/> state.
/// </remarks>
cin
working on promises
r244 protected virtual void OnResetState() {
throw new NotImplementedException();
cin
working on runnable component
r184 }
cin
working on promises
r244 IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IResolvable> chain) {
cin
runnable component, work in progress
r185 IPromise promise = null;
IPromise prev;
cin
working on runnable component
r184
cin
runnable component, work in progress
r185 var task = new ActionChainTask(action, null, null, true);
cin
working on promises
r244 Action<Exception> errorOrCancel = e => {
if (e == null)
e = new OperationCanceledException();
MoveIfPending(Commands.Fail, null, e, promise);
throw new PromiseTransientException(e);
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 };
cin
working on runnable component
r184
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 promise = task.Then(
() => MoveIfPending(Commands.Ok, null, null, promise),
errorOrCancel,
cin
working on promises
r244 errorOrCancel
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 );
cin
fixed promise chaining behavior, the error handler doesn't handle result or cancellation handlers exceptions these exceptions are propagated to the next handlers.
r196
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 prev = Move(cmd, promise, null);
cin
sync
r157
cin
runnable component, work in progress
r185 if (prev == null)
task.Resolve();
else
chain(prev, task);
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156
cin
runnable component, work in progress
r185 return promise;
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 }
cin
working on runnable component
r184
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 #region IInitializable implementation
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 public void Initialize() {
cin
working on promises
r244 Move(Commands.Init, null, null);
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 try {
OnInitialize();
Move(Commands.Ok, null, null);
} catch (Exception err) {
Move(Commands.Fail, null, err);
throw;
}
cin
working on runnable component
r184 }
protected virtual void OnInitialize() {
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 }
#endregion
#region IRunnable implementation
public IPromise Start() {
cin
runnable component, work in progress
r185 return InvokeAsync(Commands.Start, OnStart, null);
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 }
protected virtual IPromise OnStart() {
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 return Promise.Success;
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 }
cin
runnable component, work in progress
r185 public IPromise Stop() {
cin
Code review for RunnableComponent...
r210 return InvokeAsync(Commands.Stop, OnStop, StopPending);
cin
runnable component, work in progress
r185 }
protected virtual IPromise OnStop() {
cin
Added ResetState to RunnableComponent to reset in case of failure...
r205 return Promise.Success;
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 }
cin
runnable component, work in progress
r185 /// <summary>
/// Stops the current operation if one exists.
/// </summary>
/// <param name="current">Current.</param>
/// <param name="stop">Stop.</param>
cin
working on promises
r244 protected virtual void StopPending(IPromise current, IResolvable stop) {
cin
runnable component, work in progress
r185 if (current == null) {
stop.Resolve();
} else {
cin
Reworked cancelation handling, if the cancel handler isn't specified the OperationCanceledException will be handled by the error handler...
r187 // связваем текущую операцию с операцией остановки
current.On(
stop.Resolve, // если текущая операция заверщилась, то можно начинать остановку
stop.Reject, // если текущая операция дала ошибку - то все плохо, нельзя продолжать
e => stop.Resolve() // если текущая отменилась, то можно начинать остановку
);
// посылаем текущей операции сигнал остановки
cin
runnable component, work in progress
r185 current.Cancel();
}
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 }
public ExecutionState State {
get {
cin
Added 'Fail' method to RunnableComponent which allows component to move from...
r203 return m_stateMachine.State;
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 }
}
public Exception LastError {
get {
cin
runnable component, work in progress
r185 return m_lastError;
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 }
}
#endregion
cin
runnable component, work in progress
r185
#region IDisposable implementation
cin
Added 'Fail' method to RunnableComponent which allows component to move from...
r203 /// <summary>
/// Releases all resource used by the <see cref="Implab.Components.RunnableComponent"/> object.
/// </summary>
/// <remarks>
/// <para>Will not try to stop the component, it will just release all resources.
/// To cleanup the component gracefully use <see cref="Stop()"/> method.</para>
/// <para>
/// In normal cases the <see cref="Dispose()"/> method shouldn't be called, the call to the <see cref="Stop()"/>
/// method is sufficient to cleanup the component. Call <see cref="Dispose()"/> only to cleanup after errors,
/// especially if <see cref="Stop"/> method is failed. Using this method insted of <see cref="Stop()"/> may
/// lead to the data loss by the component.
/// </para></remarks>
cin
RunnableComponent.Dispose(bool,Exception) changed to standart Dispose(bool)...
r208 [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")]
cin
runnable component, work in progress
r185 public void Dispose() {
cin
Code review for RunnableComponent...
r210 Move(Commands.Dispose, null, null);
cin
runnable component, work in progress
r185 }
~RunnableComponent() {
cin
RunnableComponent.Dispose(bool,Exception) changed to standart Dispose(bool)...
r208 Dispose(false);
cin
runnable component, work in progress
r185 }
#endregion
cin
working on promises
r244 /// <summary>
/// Releases all resources used by the component, called automatically, override this method to implement your cleanup.
/// </summary>
/// <param name="disposing">true if this method is called during normal dispose process.</param>
/// <param name="pending">The operation which is currenty pending</param>
cin
RunnableComponent.Dispose(bool,Exception) changed to standart Dispose(bool)...
r208 protected virtual void Dispose(bool disposing) {
cin
runnable component, work in progress
r185 }
cin
Promises: SignalXXX methods merged into SignalHandler method....
r156 }
}