|
|
using System;
|
|
|
using Implab.Parsing;
|
|
|
|
|
|
namespace Implab.Components {
|
|
|
public class RunnableComponent : Disposable, IRunnable, IInitializable {
|
|
|
class Automaton : DFAutomaton<ExecutionState> {
|
|
|
static readonly EDFADefinition<ExecutionState> _dfa;
|
|
|
|
|
|
static Automaton() {
|
|
|
|
|
|
var token = Token
|
|
|
.New(ExecutionState.Uninitialized).Optional() // we can skip uninitialized state
|
|
|
.Cat(
|
|
|
Token.New(ExecutionState.Ready) // uninitialized -> initial
|
|
|
.Cat(
|
|
|
Token.New(ExecutionState.Starting) // initial -> starting
|
|
|
.Cat(
|
|
|
Token.New(ExecutionState.Running) // running -> {stopping -> stopped | failed }
|
|
|
.Cat(
|
|
|
Token.New(ExecutionState.Stopping) // running -> stopping
|
|
|
.Cat(
|
|
|
Token.New(ExecutionState.Stopped) // stopping -> stopped
|
|
|
.Or(Token.New(ExecutionState.Failed)) // stopping -> failed
|
|
|
)
|
|
|
.Or(Token.New(ExecutionState.Failed)) // running -> failed
|
|
|
)
|
|
|
.Or(Token.New(ExecutionState.Failed)) // starting -> failed
|
|
|
).EClosure()
|
|
|
)
|
|
|
.Or(Token.New(ExecutionState.Failed)) // uninitialized->failed
|
|
|
.Cat(Token.New(ExecutionState.Disposed).Tag(0)) // ... -> disposed
|
|
|
);
|
|
|
|
|
|
var builder = new DFABuilder();
|
|
|
token.Accept(builder);
|
|
|
|
|
|
var _dfa = new EDFADefinition<ExecutionState>(EnumAlphabet<ExecutionState>.FullAlphabet);
|
|
|
builder.BuildDFA(_dfa); // don't optimize dfa to avoid remapping of the alphabet
|
|
|
|
|
|
}
|
|
|
|
|
|
public Automaton() : base(_dfa.States, INITIAL_STATE, ExecutionState.Reserved) {
|
|
|
}
|
|
|
|
|
|
public void MoveTo(ExecutionState state) {
|
|
|
|
|
|
if (!CanMove((int)state))
|
|
|
throw new InvalidOperationException(String.Format("Illegal state transition from {0} to {1}", Current, state));
|
|
|
Move((int)state);
|
|
|
m_context.info = state;
|
|
|
}
|
|
|
|
|
|
public ExecutionState Current {
|
|
|
get {
|
|
|
return (ExecutionState)m_context.info;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
readonly Automaton m_automaton = new Automaton();
|
|
|
IPromise m_pending;
|
|
|
Exception m_lastError;
|
|
|
|
|
|
protected RunnableComponent(bool initialized) {
|
|
|
if (initialized)
|
|
|
m_automaton.MoveTo(ExecutionState.Ready);
|
|
|
else
|
|
|
m_automaton.MoveTo(ExecutionState.Uninitialized);
|
|
|
}
|
|
|
|
|
|
#region IInitializable implementation
|
|
|
|
|
|
public void Init() {
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region IRunnable implementation
|
|
|
|
|
|
public IPromise Start() {
|
|
|
return Safe.InvokePromise(() => {
|
|
|
Promise promise;
|
|
|
lock (m_automaton) {
|
|
|
if (m_automaton.Current == ExecutionState.Starting)
|
|
|
return m_pending;
|
|
|
m_automaton.MoveTo(ExecutionState.Starting);
|
|
|
m_pending = promise = new Promise();
|
|
|
}
|
|
|
|
|
|
var start = Safe.InvokePromise(OnStart);
|
|
|
promise.On(null, null, start.Cancel);
|
|
|
start.On(promise.Resolve, promise.Reject, promise.CancelOperation);
|
|
|
|
|
|
return promise.Then(() => {
|
|
|
lock(m_automaton) {
|
|
|
m_automaton.MoveTo(ExecutionState.Running);
|
|
|
m_pending = null;
|
|
|
}
|
|
|
|
|
|
Run();
|
|
|
}, err => {
|
|
|
if (BeginTransition(RUNNING_REQUIRE)) {
|
|
|
m_lastError = err;
|
|
|
CompleteTransition(FAILED_STATE);
|
|
|
throw new PromiseTransientException(err);
|
|
|
}
|
|
|
throw new OperationCanceledException();
|
|
|
}, reason => {
|
|
|
throw new OperationCanceledException("The operation was cancelled", reason);
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
|
|
|
protected virtual IPromise OnStart() {
|
|
|
return Promise.SUCCESS;
|
|
|
}
|
|
|
|
|
|
protected virtual void Run() {
|
|
|
}
|
|
|
|
|
|
public IPromise Stop() {
|
|
|
throw new NotImplementedException();
|
|
|
}
|
|
|
|
|
|
public ExecutionState State {
|
|
|
get {
|
|
|
throw new NotImplementedException();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public Exception LastError {
|
|
|
get {
|
|
|
throw new NotImplementedException();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|