changeset 244:eee3e49dd1ff v3

working on promises
author cin
date Thu, 25 Jan 2018 19:09:16 +0300
parents b1e0ffdf3451
children b904e0a3ba72
files Implab/AbstractEvent.cs Implab/AbstractPromise.cs Implab/ActionChainTask.cs Implab/ActionTask.cs Implab/Components/RunnableComponent.cs Implab/Deferred.cs Implab/FuncChainTask.cs Implab/FuncTask.cs Implab/IDeferred.cs Implab/IDispatcher.cs Implab/IPromise.cs Implab/IResolvable.cs Implab/Implab.old.csproj Implab/Promise.cs Implab/PromiseState.cs
diffstat 15 files changed, 255 insertions(+), 449 deletions(-) [+]
line wrap: on
line diff
--- a/Implab/AbstractEvent.cs	Wed Jan 24 19:24:10 2018 +0300
+++ b/Implab/AbstractEvent.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -35,31 +35,31 @@
     /// </para>
     /// </remarks>
     public abstract class AbstractEvent<THandler> where THandler : class {
-        const int PENDING_SATE = 0;
+        const int PendingState = 0;
 
-        const int TRANSITIONAL_STATE = 1;
+        const int TransitionalState = 1;
 
-        const int FULFILLED_STATE = 2;
+        const int ResolvedState = 2;
 
         volatile int m_state;
 
         THandler m_handler;
         SimpleAsyncQueue<THandler> m_extraHandlers;
 
-        public bool IsFulfilled {
+        public bool IsResolved {
             get {
-                return m_state > TRANSITIONAL_STATE;
+                return m_state > TransitionalState;
             }
         }
 
         #region state managment
         protected bool BeginTransit() {
-            return PENDING_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, PENDING_SATE);
+            return PendingState == Interlocked.CompareExchange(ref m_state, TransitionalState, PendingState);
         }
 
         protected void CompleteTransit() {
 #if DEBUG
-            if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, FULFILLED_STATE, TRANSITIONAL_STATE))
+            if (TransitionalState != Interlocked.CompareExchange(ref m_state, ResolvedState, TransitionalState))
                 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
 #else
             m_state = state;
@@ -68,11 +68,11 @@
         }
 
         protected void WaitTransition() {
-            if (m_state == TRANSITIONAL_STATE) {
+            if (m_state == TransitionalState) {
                 SpinWait spin;
                 do {
                     spin.SpinOnce();
-                } while (m_state == TRANSITIONAL_STATE);
+                } while (m_state == TransitionalState);
             }
         }
 
@@ -91,7 +91,7 @@
 
         #region synchronization traits
         protected void WaitResult(int timeout) {
-            if (!(IsFulfilled || GetFulfillSignal().Wait(timeout)))
+            if (!(IsResolved || GetFulfillSignal().Wait(timeout)))
                 throw new TimeoutException();
         }
 
@@ -102,13 +102,13 @@
 
         protected void AddHandler(THandler handler) {
 
-            if (IsFulfilled) {
+            if (IsResolved) {
                 // the promise is in the resolved state, just invoke the handler
                 SignalHandler(handler);
             } else {
                 EnqueueHandler(handler);
 
-                if (IsFulfilled && TryDequeueHandler(out handler))
+                if (IsResolved && TryDequeueHandler(out handler))
                     // if the promise have been resolved while we was adding the handler to the queue
                     // we can't guarantee that someone is still processing it
                     // therefore we need to fetch a handler from the queue and execute it
--- a/Implab/AbstractPromise.cs	Wed Jan 24 19:24:10 2018 +0300
+++ b/Implab/AbstractPromise.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -4,36 +4,21 @@
 using Implab.Parallels;
 
 namespace Implab {
-    public abstract class AbstractPromise : AbstractEvent<AbstractPromise.HandlerDescriptor>, IPromise {
-        public class HandlerDescriptor {
-            readonly Action m_resolve;
-            readonly Action<Exception> m_reject;
+    public class AbstractPromise : AbstractEvent<IResolvable>, IPromise {
 
-            readonly IDeferred m_deferred;
-            public HandlerDescriptor(Action success, Action<Exception> error) {
-                m_resolve = success;
-                m_reject = error;
+        class ResolvableSignal : IResolvable {
+            public Signal Signal { get; private set; }
+            public ResolvableSignal() {
+                Signal = new Signal();
             }
 
-            public void SignalSuccess() {
-                try {
-                    if (m_resolve != null)
-                        m_resolve();
-                    m_deferred.Resolve();
-                } catch (Exception ex) {
-                    m_deferred.Reject(ex);
-                }
+
+            public void Reject(Exception error) {
+                Signal.Set();
             }
 
-            public void SignalError(Exception err) {
-                if (m_reject != null) {
-                    try {
-                        m_reject(err);
-                        m_deferred.Resolve();
-                    } catch (Exception ex) {
-                        m_deferred.Reject(ex);
-                    }
-                }
+            public void Resolve() {
+                Signal.Set();
             }
         }
 
@@ -47,9 +32,9 @@
             }
         }
 
-        public bool IsResolved {
+        public bool IsFulfilled {
             get {
-                return m_state == PromiseState.Resolved;
+                return m_state == PromiseState.Fulfilled;
             }
         }
 
@@ -60,15 +45,29 @@
         }
 
 
+        internal void Resolve() {
+            if (BeginTransit())
+                CompleteResolve();
+        }
+
+        internal void Reject(Exception reason) {
+            if (BeginTransit()) {
+                m_error = reason;
+                m_state = PromiseState.Rejected;
+                CompleteTransit();
+            }
+        }
+
+
         #region implemented abstract members of AbstractPromise
 
-        protected override void SignalHandler(HandlerDescriptor handler) {
+        protected override void SignalHandler(IResolvable handler) {
             switch (m_state) {
-                case PromiseState.Resolved:
-                    handler.SignalSuccess();
+                case PromiseState.Fulfilled:
+                    handler.Resolve();
                     break;
                 case PromiseState.Rejected:
-                    handler.SignalError(RejectReason);
+                    handler.Reject(RejectReason);
                     break;
                 default:
                     throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", m_state));
@@ -76,15 +75,15 @@
         }
 
         protected override Signal GetFulfillSignal() {
-            var signal = new Signal();
-            On(signal.Set, e => signal.Set());
-            return signal;
+            var next = new ResolvableSignal();
+            Then(next);
+            return next.Signal;
         }
 
         #endregion
 
         protected void CompleteResolve() {
-            m_state = PromiseState.Resolved;
+            m_state = PromiseState.Fulfilled;
             CompleteTransit();
         }
 
@@ -94,27 +93,6 @@
             }
         }
 
-        /// <summary>
-        /// Выполняет обещание, сообщая об ошибке
-        /// </summary>
-        /// <remarks>
-        /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
-        /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
-        /// будут проигнорированы.
-        /// </remarks>
-        /// <param name="error">Исключение возникшее при выполнении операции</param>
-        /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
-        protected void SetError(Exception error) {
-            if (BeginTransit()) {
-                m_error = error;
-                m_state = PromiseState.Rejected;
-                CompleteTransit();
-            } else {
-                WaitTransition();
-                if (m_state == PromiseState.Resolved)
-                    throw new InvalidOperationException("The promise is already resolved");
-            }
-        }
 
         protected void Rethrow() {
             Debug.Assert(m_error != null);
@@ -124,8 +102,8 @@
                 throw new TargetInvocationException(m_error);
         }
 
-        public void On(Action success, Action<Exception> error) {
-            AddHandler(new HandlerDescriptor(success, error));
+        public void Then(IResolvable next) {
+            AddHandler(next);
         }
 
         public IPromise<T> Cast<T>() {
--- a/Implab/ActionChainTask.cs	Wed Jan 24 19:24:10 2018 +0300
+++ b/Implab/ActionChainTask.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -1,7 +1,7 @@
 using System;
 
 namespace Implab {
-    public class ActionChainTask : ActionChainTaskBase, IDeferred {
+    public class ActionChainTask : ActionChainTaskBase, IResolvable {
         readonly Func<IPromise> m_task;
 
         /// <summary>
--- a/Implab/ActionTask.cs	Wed Jan 24 19:24:10 2018 +0300
+++ b/Implab/ActionTask.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -1,7 +1,7 @@
 using System;
 
 namespace Implab {
-    public class ActionTask : ActionTaskBase, IDeferred {
+    public class ActionTask : ActionTaskBase, IResolvable {
         readonly Action m_task;
         public ActionTask(Action task, Action<Exception> error, Action<Exception> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
             m_task = task;
--- a/Implab/Components/RunnableComponent.cs	Wed Jan 24 19:24:10 2018 +0300
+++ b/Implab/Components/RunnableComponent.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -1,6 +1,6 @@
 using System;
-using System.Diagnostics.CodeAnalysis;
-
+using System.Diagnostics.CodeAnalysis;
+
 namespace Implab.Components {
     public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable {
         enum Commands {
@@ -18,28 +18,28 @@
             public static readonly ExecutionState[,] ReusableTransitions;
             public static readonly ExecutionState[,] NonreusableTransitions;
 
-            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());
-                }
+            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());
+                }
             }
 
             static StateMachine() {
@@ -65,8 +65,8 @@
                     .Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose)
 
                     .Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose)
-                    .Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset)
-
+                    .Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset)
+
                     .Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail)
                     .Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose)
 
@@ -74,11 +74,11 @@
 
                 var reusable = common
                     .Clone()
-                    .Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok);
-
-                var nonreusable = common
-                    .Clone()
-                    .Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
+                    .Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok);
+
+                var nonreusable = common
+                    .Clone()
+                    .Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
                 
                 NonreusableTransitions = nonreusable.States;
                 ReusableTransitions = reusable.States;
@@ -109,45 +109,45 @@
         IPromise m_pending;
         Exception m_lastError;
 
-        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) {
+        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) {
             m_stateMachine = new StateMachine(
                 reusable ? StateMachine.ReusableTransitions : StateMachine.NonreusableTransitions,
                 initialized ? ExecutionState.Ready : ExecutionState.Created
             );
             m_reusable = reusable;
-        }
-
-        /// <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>
+        }
+
+        /// <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>
         protected RunnableComponent(bool initialized) : this(initialized, false) {
         }
 
         void ThrowInvalidCommand(Commands cmd) {
             if (m_stateMachine.State == ExecutionState.Disposed)
-                throw new ObjectDisposedException(ToString());
-
+                throw new ObjectDisposedException(ToString());
+
             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;
-            lock (m_stateMachine) {
-                if (m_stateMachine.State != state)
-                    return false;
-
-                prev = m_stateMachine.State;
-                if (!m_stateMachine.Move(cmd))
+            lock (m_stateMachine) {
+                if (m_stateMachine.State != state)
+                    return false;
+
+                prev = m_stateMachine.State;
+                if (!m_stateMachine.Move(cmd))
                     ThrowInvalidCommand(cmd);
                 current = m_stateMachine.State;
 
@@ -161,46 +161,46 @@
 
         bool MoveIfPending(Commands cmd, IPromise pending, Exception error, IPromise expected) {
             ExecutionState prev, current;
-            lock (m_stateMachine) {
-                if (m_pending != expected)
-                    return false;
-                prev = m_stateMachine.State;
-                if (!m_stateMachine.Move(cmd))
+            lock (m_stateMachine) {
+                if (m_pending != expected)
+                    return false;
+                prev = m_stateMachine.State;
+                if (!m_stateMachine.Move(cmd))
                     ThrowInvalidCommand(cmd);
                 current = m_stateMachine.State;
                 m_pending = pending;
                 m_lastError = error;
-            }
+            }
             if (prev != current)
-                OnStateChanged(prev, current, error);
+                OnStateChanged(prev, current, error);
             return true;
         }
 
         IPromise Move(Commands cmd, IPromise pending, Exception error) {
             ExecutionState prev, current;
             IPromise ret;
-            lock (m_stateMachine) {
-                prev = m_stateMachine.State;
-                if (!m_stateMachine.Move(cmd))
+            lock (m_stateMachine) {
+                prev = m_stateMachine.State;
+                if (!m_stateMachine.Move(cmd))
                     ThrowInvalidCommand(cmd);
                 current = m_stateMachine.State;
 
                 ret = m_pending;
                 m_pending = pending;
-                m_lastError = error;
-
+                m_lastError = error;
+
             }
             if (prev != current)
                 OnStateChanged(prev, current, error);
             return ret;
         }
 
-        /// <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>
+        /// <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>
         /// <param name="error">The last error if any.</param>
         /// <remarks>
         /// <para>
@@ -212,31 +212,31 @@
         /// the wrong behavior of the component.
         /// </para>
         /// </remarks>
-        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);
-            }
+        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);
+            }
         }
 
         /// <summary>
         /// Moves the component from running to failed state.
         /// </summary>
         /// <param name="error">The exception which is describing the error.</param>
-        protected bool Fail(Exception error) {
-            return MoveIfInState(Commands.Fail, null, error, ExecutionState.Running);
+        protected bool Fail(Exception error) {
+            return MoveIfInState(Commands.Fail, null, error, ExecutionState.Running);
         }
 
-        /// <summary>
-        /// Tries to reset <see cref="ExecutionState.Failed"/> state to <see cref="ExecutionState.Ready"/>.
-        /// </summary>
+        /// <summary>
+        /// Tries to reset <see cref="ExecutionState.Failed"/> state to <see cref="ExecutionState.Ready"/>.
+        /// </summary>
         /// <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>
@@ -246,10 +246,10 @@
         /// 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>
-        protected bool ResetState() {
-            if (!MoveIfInState(Commands.Reset, null, null, ExecutionState.Failed))
-                return false;
-
+        protected bool ResetState() {
+            if (!MoveIfInState(Commands.Reset, null, null, ExecutionState.Failed))
+                return false;
+
             try {
                 OnResetState();
                 Move(Commands.Ok, null, null);
@@ -257,38 +257,38 @@
             } catch (Exception err) {
                 Move(Commands.Fail, null, err);
                 throw;
-            }
+            }
         }
 
-        /// <summary>
-        /// This method is called by <see cref="ResetState"/> to reinitialize component in the failed state.
+        /// <summary>
+        /// This method is called by <see cref="ResetState"/> to reinitialize component in the failed state.
         /// </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>
-        protected virtual void OnResetState() {
-            throw new NotImplementedException();
+        protected virtual void OnResetState() {
+            throw new NotImplementedException();
         }
 
-        IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) {
+        IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IResolvable> chain) {
             IPromise promise = null;
             IPromise prev;
 
             var task = new ActionChainTask(action, null, null, true);
 
-            Action<Exception> errorOrCancel = e => {
-                if (e == null)
-                    e = new OperationCanceledException();
-                MoveIfPending(Commands.Fail, null, e, promise);
-                throw new PromiseTransientException(e);
+            Action<Exception> errorOrCancel = e => {
+                if (e == null)
+                    e = new OperationCanceledException();
+                MoveIfPending(Commands.Fail, null, e, promise);
+                throw new PromiseTransientException(e);
             };
 
             promise = task.Then(
                 () => MoveIfPending(Commands.Ok, null, null, promise),
                 errorOrCancel,
-                errorOrCancel
+                errorOrCancel
             );
 
             prev = Move(cmd, promise, null);
@@ -305,8 +305,8 @@
         #region IInitializable implementation
 
         public void Initialize() {
-            Move(Commands.Init, null, null);
-
+            Move(Commands.Init, null, null);
+
             try {
                 OnInitialize();
                 Move(Commands.Ok, null, null);
@@ -344,7 +344,7 @@
         /// </summary>
         /// <param name="current">Current.</param>
         /// <param name="stop">Stop.</param>
-        protected virtual void StopPending(IPromise current, IDeferred stop) {
+        protected virtual void StopPending(IPromise current, IResolvable stop) {
             if (current == null) {
                 stop.Resolve();
             } else {
@@ -398,11 +398,11 @@
 
         #endregion
 
-        /// <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>
+        /// <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>
         protected virtual void Dispose(bool disposing) {
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Deferred.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -0,0 +1,52 @@
+using System;
+using System.Diagnostics;
+
+namespace Implab {
+    /// <summary>
+    /// This class is responsible for the promise resolution, dispatching and chaining
+    /// </summary>
+    public class Deferred : IResolvable {
+
+        readonly AbstractPromise m_promise;
+        readonly IDispatcher m_dispatcher;
+
+        internal Deferred(AbstractPromise promise, IDispatcher dispatcher) {
+            Debug.Assert(promise != null);
+            m_promise = promise;
+            m_dispatcher = dispatcher;
+        }
+
+        public IPromise Promise {
+            get { return m_promise; }
+        }
+
+        public void Reject(Exception error) {
+            m_promise.Reject(error);
+        }
+
+        public void Resolve() {
+            m_promise.Resolve();
+        }
+
+        public void Resolve(IPromise thenable) {
+            if (thenable == null)
+                Reject(new Exception("The promise or task are expected"));
+            if (thenable == m_promise)
+                Reject(new Exception("The promise cannot be resolved with oneself"));
+
+            else if (m_dispatcher != null)
+                // dispatch (see ecma-262/6.0: 25.4.1.3.2 Promise Resolve Functions)
+                m_dispatcher.Enqueue(() => thenable.Then(this));
+            else
+                thenable.Then(this);
+        }
+
+        void Chain(IPromise thenable) {
+            try {
+                thenable.Then(this);
+            } catch (Exception err) {
+                Reject(err);
+            }
+        }
+    }
+}
\ No newline at end of file
--- a/Implab/FuncChainTask.cs	Wed Jan 24 19:24:10 2018 +0300
+++ b/Implab/FuncChainTask.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -1,7 +1,7 @@
 using System;
 
 namespace Implab {
-    public class FuncChainTask<TResult> : FuncChainTaskBase<TResult>, IDeferred {
+    public class FuncChainTask<TResult> : FuncChainTaskBase<TResult>, IResolvable {
         readonly Func<IPromise<TResult>> m_task;
 
         public FuncChainTask(Func<IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel, bool autoCancellable)
--- a/Implab/FuncTask.cs	Wed Jan 24 19:24:10 2018 +0300
+++ b/Implab/FuncTask.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -2,7 +2,7 @@
 using System.Threading;
 
 namespace Implab {
-    public class FuncTask<T> : FuncTaskBase<T>, IDeferred {
+    public class FuncTask<T> : FuncTaskBase<T>, IResolvable {
         readonly Func<T> m_task;
 
         public FuncTask(Func<T> task, Func<Exception, T> error, Func<Exception, T> cancel, bool autoCancellable) : base(error, cancel, autoCancellable) {
--- a/Implab/IDeferred.cs	Wed Jan 24 19:24:10 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-using System;
-
-namespace Implab {
-    /// <summary>
-    /// Deferred result, usually used by asynchronous services as the service part of the promise.
-    /// </summary>
-    public interface IDeferred {
-
-        void Resolve();
-
-        /// <summary>
-        /// Reject the promise with the specified error.
-        /// </summary>
-        /// <param name="error">The reason why the promise is rejected.</param>
-        /// <remarks>
-        /// Some exceptions are treated in a special case: 
-        /// <see cref="OperationCanceledException"/> is interpreted as call to <see cref="Cancel()"/> method,
-        /// and <see cref="PromiseTransientException"/> is always unwrapped and its
-        /// <see cref="PromiseTransientException.InnerException"> is used as the reason to reject promise.
-        /// </remarks>
-        void Reject(Exception error);
-
-        /// <summary>
-        /// Marks current instance as cencelled with the specified reason.
-        /// </summary>
-        /// <param name="reason">The reason for the operation cancellation,
-        /// if not specified the new <see cref="OperationCanceledException"> will be created</param>
-        void SetCancelled(Exception reason);
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/IDispatcher.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -0,0 +1,7 @@
+using System;
+
+namespace Implab {
+    public interface IDispatcher {
+        void Enqueue(Action job);
+    }
+}
\ No newline at end of file
--- a/Implab/IPromise.cs	Wed Jan 24 19:24:10 2018 +0300
+++ b/Implab/IPromise.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -14,11 +14,11 @@
         /// <summary>
         /// Обещание является выполненым, либо успешно, либо с ошибкой, либо отменено.
         /// </summary>
-        bool IsFulfilled { get; }
+        bool IsResolved { get; }
 
         bool IsRejected { get; }
 
-        bool IsResolved { get; }
+        bool IsFulfilled { get; }
 
         /// <summary>
         /// Исключение возникшее в результате выполнения обещания, либо причина отмены.
@@ -31,23 +31,11 @@
         /// <param name="success">The handler called on the successful promise completion.</param>
         /// <param name="error">The handler is called if an error while completing the promise occurred.</param>
         /// <returns>The current promise.</returns>
-        void On(Action success, Action<Exception> error);
+        void Then(IResolvable next);
 
         /// <summary>
         /// Преобразует результат обещания к заданному типу и возвращает новое обещание.
         /// </summary>
         IPromise<T> Cast<T>();
-
-        /// <summary>
-        /// Синхронизирует текущий поток с обещанием.
-        /// </summary>
-        void Join();
-        /// <summary>
-        /// Синхронизирует текущий поток с обещанием.
-        /// </summary>
-        /// <param name="timeout">Время ожидания, по его истечению возникнет исключение.</param>
-        /// <exception cref="TimeoutException">Превышено время ожидания.</exception>
-        void Join(int timeout);
-
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/IResolvable.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -0,0 +1,26 @@
+using System;
+
+namespace Implab {
+    /// <summary>
+    /// Deferred result, usually used by asynchronous services as the service part of the promise.
+    /// </summary>
+    public interface IResolvable {
+
+        void Resolve();
+
+        void Resolve(IPromise thenable);
+
+        /// <summary>
+        /// Reject the promise with the specified error.
+        /// </summary>
+        /// <param name="error">The reason why the promise is rejected.</param>
+        /// <remarks>
+        /// Some exceptions are treated in a special case: 
+        /// <see cref="OperationCanceledException"/> is interpreted as call to <see cref="Cancel()"/> method,
+        /// and <see cref="PromiseTransientException"/> is always unwrapped and its
+        /// <see cref="PromiseTransientException.InnerException"> is used as the reason to reject promise.
+        /// </remarks>
+        void Reject(Exception error);
+    }
+}
+
--- a/Implab/Implab.old.csproj	Wed Jan 24 19:24:10 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,189 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
-    <OutputType>Library</OutputType>
-    <RootNamespace>Implab</RootNamespace>
-    <AssemblyName>Implab</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
-    <TargetFrameworkProfile />
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-    <RunCodeAnalysis>true</RunCodeAnalysis>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>full</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <DefineConstants>NET_4_5</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <PropertyGroup>
-    <SignAssembly>false</SignAssembly>
-  </PropertyGroup>
-  <PropertyGroup>
-    <AssemblyOriginatorKeyFile>implab.snk</AssemblyOriginatorKeyFile>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Xml" />
-    <Reference Include="mscorlib" />
-    <Reference Include="System.Xml.Linq" />
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="Components\StateChangeEventArgs.cs" />
-    <Compile Include="CustomEqualityComparer.cs" />
-    <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
-    <Compile Include="Diagnostics\LogChannel.cs" />
-    <Compile Include="Diagnostics\LogicalOperation.cs" />
-    <Compile Include="Diagnostics\TextFileListener.cs" />
-    <Compile Include="Diagnostics\Trace.cs" />
-    <Compile Include="Diagnostics\TraceLog.cs" />
-    <Compile Include="Diagnostics\TraceEvent.cs" />
-    <Compile Include="Diagnostics\TraceEventType.cs" />
-    <Compile Include="Diagnostics\TraceSourceAttribute.cs" />
-    <Compile Include="Formats\CharMap.cs" />
-    <Compile Include="Formats\FastInpurScanner.cs" />
-    <Compile Include="Formats\InputScanner.cs" />
-    <Compile Include="Formats\Json\JsonStringScanner.cs" />
-    <Compile Include="Formats\Json\JsonTextScanner.cs" />
-    <Compile Include="ICancellable.cs" />
-    <Compile Include="IProgressHandler.cs" />
-    <Compile Include="IProgressNotifier.cs" />
-    <Compile Include="IPromiseT.cs" />
-    <Compile Include="IPromise.cs" />
-    <Compile Include="IServiceLocator.cs" />
-    <Compile Include="ITaskController.cs" />
-    <Compile Include="Parallels\DispatchPool.cs" />
-    <Compile Include="Parallels\ArrayTraits.cs" />
-    <Compile Include="Parallels\SimpleAsyncQueue.cs" />
-    <Compile Include="Parallels\WorkerPool.cs" />
-    <Compile Include="ProgressInitEventArgs.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Parallels\AsyncPool.cs" />
-    <Compile Include="Safe.cs" />
-    <Compile Include="SyncContextPromise.cs" />
-    <Compile Include="ValueEventArgs.cs" />
-    <Compile Include="PromiseExtensions.cs" />
-    <Compile Include="SyncContextPromiseT.cs" />
-    <Compile Include="Diagnostics\OperationContext.cs" />
-    <Compile Include="Diagnostics\TraceContext.cs" />
-    <Compile Include="Diagnostics\LogEventArgs.cs" />
-    <Compile Include="Diagnostics\LogEventArgsT.cs" />
-    <Compile Include="Diagnostics\Extensions.cs" />
-    <Compile Include="PromiseEventType.cs" />
-    <Compile Include="Parallels\AsyncQueue.cs" />
-    <Compile Include="PromiseT.cs" />
-    <Compile Include="IDeferred.cs" />
-    <Compile Include="IDeferredT.cs" />
-    <Compile Include="Promise.cs" />
-    <Compile Include="PromiseTransientException.cs" />
-    <Compile Include="Parallels\Signal.cs" />
-    <Compile Include="Parallels\SharedLock.cs" />
-    <Compile Include="Diagnostics\ILogWriter.cs" />
-    <Compile Include="Diagnostics\ListenerBase.cs" />
-    <Compile Include="Parallels\BlockingQueue.cs" />
-    <Compile Include="AbstractEvent.cs" />
-    <Compile Include="AbstractPromise.cs" />
-    <Compile Include="AbstractPromiseT.cs" />
-    <Compile Include="FuncTask.cs" />
-    <Compile Include="FuncTaskBase.cs" />
-    <Compile Include="FuncTaskT.cs" />
-    <Compile Include="ActionChainTaskBase.cs" />
-    <Compile Include="ActionChainTask.cs" />
-    <Compile Include="ActionChainTaskT.cs" />
-    <Compile Include="FuncChainTaskBase.cs" />
-    <Compile Include="FuncChainTask.cs" />
-    <Compile Include="FuncChainTaskT.cs" />
-    <Compile Include="ActionTaskBase.cs" />
-    <Compile Include="ActionTask.cs" />
-    <Compile Include="ActionTaskT.cs" />
-    <Compile Include="ICancellationToken.cs" />
-    <Compile Include="SuccessPromise.cs" />
-    <Compile Include="SuccessPromiseT.cs" />
-    <Compile Include="PromiseAwaiterT.cs" />
-    <Compile Include="PromiseAwaiter.cs" />
-    <Compile Include="Components\ComponentContainer.cs" />
-    <Compile Include="Components\Disposable.cs" />
-    <Compile Include="Components\DisposablePool.cs" />
-    <Compile Include="Components\ObjectPool.cs" />
-    <Compile Include="Components\ServiceLocator.cs" />
-    <Compile Include="Components\IInitializable.cs" />
-    <Compile Include="TaskController.cs" />
-    <Compile Include="Components\App.cs" />
-    <Compile Include="Components\IRunnable.cs" />
-    <Compile Include="Components\ExecutionState.cs" />
-    <Compile Include="Components\RunnableComponent.cs" />
-    <Compile Include="Components\IFactory.cs" />
-    <Compile Include="Automaton\IAlphabet.cs" />
-    <Compile Include="Automaton\ParserException.cs" />
-    <Compile Include="Automaton\IndexedAlphabetBase.cs" />
-    <Compile Include="Automaton\IAlphabetBuilder.cs" />
-    <Compile Include="Automaton\RegularExpressions\AltToken.cs" />
-    <Compile Include="Automaton\RegularExpressions\BinaryToken.cs" />
-    <Compile Include="Automaton\RegularExpressions\CatToken.cs" />
-    <Compile Include="Automaton\RegularExpressions\StarToken.cs" />
-    <Compile Include="Automaton\RegularExpressions\SymbolToken.cs" />
-    <Compile Include="Automaton\RegularExpressions\EmptyToken.cs" />
-    <Compile Include="Automaton\RegularExpressions\Token.cs" />
-    <Compile Include="Automaton\RegularExpressions\IVisitor.cs" />
-    <Compile Include="Automaton\AutomatonTransition.cs" />
-    <Compile Include="Formats\Json\JsonElementContext.cs" />
-    <Compile Include="Formats\Json\JsonElementType.cs" />
-    <Compile Include="Formats\Json\JsonGrammar.cs" />
-    <Compile Include="Formats\Json\JsonReader.cs" />
-    <Compile Include="Formats\Json\JsonScanner.cs" />
-    <Compile Include="Formats\Json\JsonTokenType.cs" />
-    <Compile Include="Formats\Json\JsonWriter.cs" />
-    <Compile Include="Formats\Json\StringTranslator.cs" />
-    <Compile Include="Automaton\MapAlphabet.cs" />
-    <Compile Include="Formats\CharAlphabet.cs" />
-    <Compile Include="Formats\ByteAlphabet.cs" />
-    <Compile Include="Automaton\IDFATable.cs" />
-    <Compile Include="Automaton\IDFATableBuilder.cs" />
-    <Compile Include="Automaton\DFATable.cs" />
-    <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitor.cs" />
-    <Compile Include="Automaton\RegularExpressions\ITaggedDFABuilder.cs" />
-    <Compile Include="Formats\Grammar.cs" />
-    <Compile Include="Automaton\RegularExpressions\EndTokenT.cs" />
-    <Compile Include="Automaton\RegularExpressions\EndToken.cs" />
-    <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitorT.cs" />
-    <Compile Include="Automaton\AutomatonConst.cs" />
-    <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" />
-    <Compile Include="Components\LazyAndWeak.cs" />
-    <Compile Include="AbstractTask.cs" />
-    <Compile Include="AbstractTaskT.cs" />
-    <Compile Include="FailedPromise.cs" />
-    <Compile Include="FailedPromiseT.cs" />
-    <Compile Include="Components\PollingComponent.cs" />
-    <Compile Include="Xml\JsonXmlReader.cs" />
-    <Compile Include="Xml\JsonXmlReaderOptions.cs" />
-    <Compile Include="Xml\JsonXmlReaderPosition.cs" />
-    <Compile Include="Xml\SerializationHelpers.cs" />
-    <Compile Include="Xml\SerializersPool.cs" />
-    <Compile Include="Xml\XmlSimpleAttribute.cs" />
-    <Compile Include="Xml\XmlNameContext.cs" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup>
-    <None Include="Implab.nuspec">
-      <SubType>Designer</SubType>
-    </None>
-    <None Include="implab.snk" />
-  </ItemGroup>
-  <ItemGroup>
-    <Content Include="license.txt" />
-  </ItemGroup>
-</Project>
\ No newline at end of file
--- a/Implab/Promise.cs	Wed Jan 24 19:24:10 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-using System;
-using Implab.Parallels;
-
-namespace Implab {
-    public class Promise : AbstractPromise {
-        public static readonly IPromise Success;
-
-        static Promise() {
-            Success = new SuccessPromise();
-        }
-
-        internal void ResolvePromise() {
-            SetResult();
-        }
-
-        internal void RejectPromise(Exception error) {
-            SetError(error);
-        }
-
-        public static IPromise Reject(Exception exception) {
-            return new FailedPromise(exception);
-        }
-    }
-}
-
--- a/Implab/PromiseState.cs	Wed Jan 24 19:24:10 2018 +0300
+++ b/Implab/PromiseState.cs	Thu Jan 25 19:09:16 2018 +0300
@@ -2,7 +2,7 @@
     public enum PromiseState {
         Pending,
 
-        Resolved,
+        Fulfilled,
 
         Rejected
     }