using System; using Implab.Parsing; namespace Implab.Components { public class RunnableComponent : Disposable, IRunnable, IInitializable { class Automaton : DFAutomaton { static readonly EDFADefinition _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(EnumAlphabet.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 } }