view Implab/Components/RunnableComponent.cs @ 156:97fbbf816844 v2

Promises: SignalXXX methods merged into SignalHandler method. Components: RunnableComponent In progress
author cin
date Mon, 15 Feb 2016 04:22:15 +0300
parents
children 948c015a9011
line wrap: on
line source

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
    }
}