changeset 145:706fccb85524 v2

RC: cancellation support for promises + tests
author cin
date Sun, 08 Mar 2015 02:52:27 +0300
parents 8c0b95069066
children e03ccec4a08d
files Implab.Fx/ControlBoundPromise.cs Implab.Test/AsyncTests.cs Implab.Test/CancelationTests.cs Implab.Test/Implab.Test.mono.csproj Implab/AbstractEvent.cs Implab/AbstractPromise.cs Implab/AbstractPromiseT.cs Implab/ActionChainTask.cs Implab/ActionChainTaskBase.cs Implab/ActionChainTaskT.cs Implab/ActionTask.cs Implab/ActionTaskBase.cs Implab/ActionTaskT.cs Implab/ChainTask.cs Implab/FuncChainTask.cs Implab/FuncChainTaskBase.cs Implab/FuncChainTaskT.cs Implab/ICancelationToken.cs Implab/ICancellationToken.cs Implab/IDeferred.cs Implab/IDeferredT.cs Implab/ITaskController.cs Implab/Implab.csproj Implab/Parallels/ArrayTraits.cs Implab/Parallels/AsyncPool.cs Implab/PromiseExtensions.cs Implab/Safe.cs Implab/SuccessPromise.cs Implab/SuccessPromiseT.cs Implab/SyncContextPromise.cs MonoPlay/Program.cs
diffstat 31 files changed, 1046 insertions(+), 203 deletions(-) [+]
line wrap: on
line diff
--- a/Implab.Fx/ControlBoundPromise.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab.Fx/ControlBoundPromise.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -12,23 +12,23 @@
             m_target = target;
         }
 
-        protected override void SignalSuccess(IDeferred<T> handler) {
+        protected override void SignalSuccess(Promise<T>.HandlerDescriptor handler) {
             if (m_target.InvokeRequired)
-                m_target.BeginInvoke(new Action<IDeferred<T>>(base.SignalSuccess), handler);
+                m_target.BeginInvoke(new Action<Promise<T>.HandlerDescriptor>(base.SignalSuccess), handler);
             else
                 base.SignalSuccess(handler);
         }
 
-        protected override void SignalCancelled(IDeferred<T> handler, Exception reason) {
+        protected override void SignalCancelled(Promise<T>.HandlerDescriptor handler, Exception reason) {
             if (m_target.InvokeRequired)
-                m_target.BeginInvoke(new Action<IDeferred<T>,Exception>(base.SignalCancelled), handler, reason);
+                m_target.BeginInvoke(new Action<Promise<T>.HandlerDescriptor,Exception>(base.SignalCancelled), handler, reason);
             else
                 base.SignalCancelled(handler, reason);
         }
 
-        protected override void SignalError(IDeferred<T> handler, Exception error) {
+        protected override void SignalError(Promise<T>.HandlerDescriptor handler, Exception error) {
             if (m_target.InvokeRequired)
-                m_target.BeginInvoke(new Action<IDeferred<T>,Exception>(base.SignalError), handler, error);
+                m_target.BeginInvoke(new Action<Promise<T>.HandlerDescriptor,Exception>(base.SignalError), handler, error);
             else
                 base.SignalError(handler, error);
         }
--- a/Implab.Test/AsyncTests.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab.Test/AsyncTests.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -7,7 +7,7 @@
 
 using NUnit.Framework;
 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
-using TestMethod = NUnit.Framework.TestAttribute;
+using TestMethodAttribute = NUnit.Framework.TestAttribute;
 
 #else
 
@@ -51,7 +51,7 @@
         [TestMethod]
         public void CancelExceptionTest() {
             var p = new Promise<bool>();
-            p.Cancel();
+            p.CancelOperation(null);
 
             var p2 = p.Then(x => x, null, reason => {
                 throw new ApplicationException("CANCELLED"); 
@@ -69,10 +69,10 @@
         [TestMethod]
         public void ContinueOnCancelTest() {
             var p = new Promise<bool>();
-            p.Cancel();
+            p.CancelOperation(null);
 
             var p2 = p
-                .Then<bool>(x => x, null, reason => {
+                .Then(x => x, null, reason => {
                     throw new ApplicationException("CANCELLED");
                 })
                 .Then(x => x, e => true);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Test/CancelationTests.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,144 @@
+using System;
+using Implab.Parallels;
+
+#if MONO
+
+using NUnit.Framework;
+using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
+using TestMethodAttribute = NUnit.Framework.TestAttribute;
+
+#else
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+#endif
+
+namespace Implab.Test {
+    [TestClass]
+    public class CancelationTests {
+
+        [TestMethod]
+        public void PromiseCancelTest() {
+            var p = new Promise();
+            bool requested = false;
+            var reason = new Exception("Test");
+
+            // request cancelation
+            p.Cancel(reason);
+
+            Assert.IsTrue(p.IsCancellationRequested);
+            Assert.AreSame(reason, p.CancellationReason);
+            Assert.IsFalse(p.IsCancelled);
+
+            p.CancellationRequested(r => {
+                Assert.AreSame(reason, r);
+                requested = true;
+            });
+
+            Assert.IsTrue(requested);
+
+            // cancel the promise
+            Assert.IsTrue(p.CancelOperationIfRequested());
+            Assert.IsTrue(p.IsCancelled);
+            Assert.AreSame(reason, p.Error);
+        }
+
+        [TestMethod]
+        public void CancelActionBeforeStartTask() {
+            bool run = false;
+            var task = new ActionTask(() => {
+                run = true;
+            }, null, null);
+
+            // request cancelation
+            task.Cancel();
+            Assert.IsTrue(task.IsCancelled);
+            task.Resolve();
+            Assert.IsFalse(run);
+        }
+
+        [TestMethod]
+        public void CancelActionAfterTaskStarted() {
+            var finish = new Signal();
+            var started = new Signal();
+
+            var task = new ActionTask(() => {
+                started.Set();
+                finish.Wait();
+            }, null, null);
+
+            AsyncPool.RunThread(() => {
+                task.Resolve();
+            });
+
+            started.Wait(1000);
+
+            task.Cancel();
+            Assert.IsTrue(task.IsCancellationRequested);
+            Assert.IsFalse(task.IsCancelled);
+            Assert.IsFalse(task.IsResolved);
+
+            finish.Set();
+            task.Join(1000);
+
+        }
+
+        [TestMethod]
+        public void CancelTaskChainFromBottom() {
+            var check1 = new Signal();
+            var requested = false;
+            var p1 = AsyncPool.RunThread(token => {
+                token.CancellationRequested(reason => requested = true);
+                check1.Wait();
+                token.CancelOperationIfRequested();
+            });
+
+            var p2 = p1.Then(() => {
+            });
+
+            Assert.IsFalse(p1.IsResolved);
+            Assert.IsFalse(p2.IsResolved);
+
+            p2.Cancel();
+
+            Assert.IsFalse(p2.IsCancelled);
+            Assert.IsFalse(p1.IsCancelled);
+            Assert.IsTrue(requested);
+
+            check1.Set();
+
+            try {
+                p2.Join(1000);
+                Assert.Fail("The chain isn't cancelled");
+            } catch(OperationCanceledException){
+            }
+
+            Assert.IsTrue(p1.IsCancelled);
+            Assert.IsTrue(p2.IsCancelled);
+        }
+
+
+
+        [TestMethod]
+        public void CancellableAsyncTask() {
+            var finish = new Signal();
+            var started = new Signal();
+
+            var p = AsyncPool.RunThread(token => {
+                token.CancellationRequested(r => finish.Set());
+                started.Set();
+                finish.Wait();
+                Assert.IsTrue(token.CancelOperationIfRequested());
+            });
+
+            started.Wait(1000);
+            Assert.IsFalse(p.IsResolved);
+            p.Cancel();
+            try {
+                p.Join(1000);
+            } catch (OperationCanceledException) {
+            }
+        }
+    }
+}
+
--- a/Implab.Test/Implab.Test.mono.csproj	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab.Test/Implab.Test.mono.csproj	Sun Mar 08 02:52:27 2015 +0300
@@ -56,6 +56,7 @@
     <Compile Include="AsyncTests.cs" />
     <Compile Include="PromiseHelper.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="CancelationTests.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Implab\Implab.csproj">
--- a/Implab/AbstractEvent.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/AbstractEvent.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -4,7 +4,7 @@
 using System.Reflection;
 
 namespace Implab {
-    public abstract class AbstractEvent<THandler> : ICancelationToken, ICancellable { 
+    public abstract class AbstractEvent<THandler> : ICancellationToken, ICancellable { 
 
         const int UNRESOLVED_SATE = 0;
         const int TRANSITIONAL_STATE = 1;
@@ -280,31 +280,34 @@
             }
         }
 
-        public bool AcceptIfRequested() {
-            if (IsCancelRequested)
-                CancelOperation(CancelReason);
+        public bool CancelOperationIfRequested() {
+            if (IsCancellationRequested) {
+                CancelOperation(CancellationReason);
+                return true;
+            }
+            return false;
         }
 
         public virtual void CancelOperation(Exception reason) {
             SetCancelled(reason);
         }
 
-        public void CancelationRequested(Action<Exception> handler) {
+        public void CancellationRequested(Action<Exception> handler) {
             Safe.ArgumentNotNull(handler, "handler");
-            if (IsCancelRequested)
-                handler(CancelReason);
+            if (IsCancellationRequested)
+                handler(CancellationReason);
 
             if (m_cancelationHandlers == null)
                 Interlocked.CompareExchange(ref m_cancelationHandlers, new MTQueue<Action<Exception>>(), null);
 
             m_cancelationHandlers.Enqueue(handler);
 
-            if (IsCancelRequested && m_cancelationHandlers.TryDequeue(out handler))
+            if (IsCancellationRequested && m_cancelationHandlers.TryDequeue(out handler))
                 // TryDeque implies MemoryBarrier()
                 handler(m_cancelationReason);
         }
 
-        public bool IsCancelRequested {
+        public bool IsCancellationRequested {
             get {
                 do {
                     if (m_cancelRequest == CANCEL_NOT_REQUESTED)
@@ -316,7 +319,7 @@
             }
         }
 
-        public Exception CancelReason {
+        public Exception CancellationReason {
             get {
                 do {
                     Thread.MemoryBarrier();
@@ -333,7 +336,7 @@
         }
 
         public void Cancel(Exception reason) {
-            if (CANCEL_NOT_REQUESTED == Interlocked.CompareExchange(ref m_cancelRequest, CANCEL_REQUESTING)) {
+            if (CANCEL_NOT_REQUESTED == Interlocked.CompareExchange(ref m_cancelRequest, CANCEL_REQUESTING, CANCEL_NOT_REQUESTED)) {
                 m_cancelationReason = reason;
                 m_cancelRequest = CANCEL_REQUESTED;
                 if (m_cancelationHandlers != null) {
--- a/Implab/AbstractPromise.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/AbstractPromise.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -18,11 +18,13 @@
 
             public HandlerDescriptor(Action handler, PromiseEventType mask) {
                 m_handler = handler;
+                m_error = null;
+                m_cancel = null;
                 m_mask = mask;
             }
 
             public void SignalSuccess() {
-                if (m_mask & PromiseEventType.Success && m_handler != null) {
+                if ((m_mask & PromiseEventType.Success) != 0 && m_handler != null) {
                     try {
                         m_handler();
                     } catch (Exception err) {
@@ -40,7 +42,7 @@
                         // Analysis disable once EmptyGeneralCatchClause
                     } catch {
                     }
-                } else if (m_mask & PromiseEventType.Error && m_handler != null) {
+                } else if ((m_mask & PromiseEventType.Error ) != 0 && m_handler != null) {
                     try {
                         m_handler();
                         // Analysis disable once EmptyGeneralCatchClause
@@ -56,7 +58,7 @@
                     } catch (Exception err) {
                         SignalError(err);
                     }
-                } else if (m_mask & PromiseEventType.Cancelled && m_handler != null) {
+                } else if ( (m_mask & PromiseEventType.Cancelled) != 0 && m_handler != null) {
                     try {
                         m_handler();
                         // Analysis disable once EmptyGeneralCatchClause
@@ -84,11 +86,11 @@
         protected override Signal GetResolveSignal() {
             var signal = new Signal();
             On(signal.Set, PromiseEventType.All);
+            return signal;
         }
 
         #endregion
 
-
         public Type PromiseType {
             get {
                 return typeof(void);
--- a/Implab/AbstractPromiseT.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/AbstractPromiseT.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -14,10 +14,14 @@
                 m_success = success;
                 m_error = error;
                 m_cancel = cancel;
+
+                m_handler = null;
+                m_mask = 0;
             }
 
             public HandlerDescriptor(Action success, Action<Exception> error, Action<Exception> cancel) {
                 m_handler = success;
+                m_success = null;
                 m_error = error;
                 m_cancel = cancel;
                 m_mask = PromiseEventType.Success;
@@ -26,6 +30,9 @@
             public HandlerDescriptor(Action handler, PromiseEventType mask) {
                 m_handler = handler;
                 m_mask = mask;
+                m_success = null;
+                m_error = null;
+                m_cancel = null;
             }
 
             public void SignalSuccess(T result) {
@@ -35,7 +42,7 @@
                     } catch(Exception err) {
                         SignalError(err);
                     }
-                } else if (m_mask & PromiseEventType.Success && m_handler != null) {
+                } else if ((m_mask & PromiseEventType.Success) != 0 && m_handler != null) {
                     try {
                         m_handler();
                     } catch(Exception err) {
@@ -53,7 +60,7 @@
                         // Analysis disable once EmptyGeneralCatchClause
                     } catch {
                     }
-                } else if (m_mask & PromiseEventType.Error && m_handler != null) {
+                } else if ((m_mask & PromiseEventType.Error) != 0 && m_handler != null) {
                     try {
                         m_handler();
                         // Analysis disable once EmptyGeneralCatchClause
@@ -69,7 +76,7 @@
                     } catch (Exception err) {
                         SignalError(err);
                     }
-                } else if (m_mask & PromiseEventType.Cancelled && m_handler != null) {
+                } else if ((m_mask & PromiseEventType.Cancelled) != 0 && m_handler != null) {
                     try {
                         m_handler();
                         // Analysis disable once EmptyGeneralCatchClause
@@ -79,23 +86,28 @@
             }
         }
 
-
-
         public Type PromiseType {
             get {
                 return typeof(T);
             }
         }
 
-        public new T Join() {
+        public T Join() {
             WaitResult(-1);
             return m_result;
         }
-        public new T Join(int timeout) {
+        public T Join(int timeout) {
             WaitResult(timeout);
             return m_result;
         }
 
+        void IPromise.Join() {
+            WaitResult(-1);
+        }
+        void IPromise.Join(int timeout) {
+            WaitResult(timeout);
+        }
+
         public IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel) {
             AddHandler(new HandlerDescriptor(success, error, cancel));
             return this;
@@ -146,6 +158,11 @@
             return this;
         }
 
+        IPromise IPromise.On(Action handler, PromiseEventType events) {
+            AddHandler(new HandlerDescriptor(handler, events));
+            return this;
+        }
+
         public IPromise<T2> Cast<T2>() {
             return (IPromise<T2>)this;
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/ActionChainTask.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,23 @@
+using System;
+
+namespace Implab {
+    public class ActionChainTask : ActionChainTaskBase, IDeferred {
+        readonly Func<IPromise> m_task;
+
+        public ActionChainTask(Func<IPromise> task, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) : base(error,cancel) {
+            m_task = task;
+        }
+
+        public void Resolve() {
+            if (m_task != null && LockCancelation()) {
+                try {
+                    Observe(m_task());
+                } catch(Exception err) {
+                    HandleErrorInternal(err);
+                }
+            }
+        }
+
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/ActionChainTaskBase.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,62 @@
+using System;
+using System.Threading;
+
+namespace Implab {
+    public class ActionChainTaskBase : AbstractPromise {
+        readonly Func<Exception, IPromise> m_error;
+        readonly Func<Exception, IPromise> m_cancel;
+
+        int m_cancelationLock;
+
+        protected ActionChainTaskBase( Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) {
+            m_error = error;
+            m_cancel = cancel;
+        }
+
+        public void Reject(Exception error) {
+            if (LockCancelation())
+                HandleErrorInternal(error);
+        }
+
+
+
+        public override void CancelOperation(Exception reason) {
+            if (m_cancel != null && LockCancelation()) {
+                try {
+                    Observe(m_cancel(reason));
+                } catch(Exception err) {
+                    HandleErrorInternal(err);
+                }
+            }
+
+        }
+
+        protected void HandleErrorInternal(Exception error) {
+            if (m_error != null) {
+                try {
+                    Observe(m_error(error));
+                } catch(Exception err) {
+                    SetError(err);
+                }
+            } else {
+                SetError(error);
+            }
+        }
+
+        protected void Observe(IPromise operation) {
+            if (operation == null)
+                throw new NullReferenceException("The task returned null promise");
+
+            // pass operation results to the current promise
+            operation.On(SetResult, SetError, SetCancelled);
+
+            // pass the cancelation request
+            CancellationRequested(operation.Cancel);
+        }
+
+        protected bool LockCancelation() {
+            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/ActionChainTaskT.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,23 @@
+using System;
+
+namespace Implab {
+    public class ActionChainTask<T> : ActionChainTaskBase, IDeferred<T> {
+        readonly Func<T, IPromise> m_task;
+
+        public ActionChainTask(Func<T, IPromise> task, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) : base(error,cancel) {
+            m_task = task;
+        }
+
+        public void Resolve(T value) {
+            if (m_task != null && LockCancelation()) {
+                try {
+                    Observe(m_task(value));
+                } catch(Exception err) {
+                    HandleErrorInternal(err);
+                }
+            }
+        }
+
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/ActionTask.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,22 @@
+using System;
+
+namespace Implab {
+    public class ActionTask : ActionTaskBase, IDeferred {
+        readonly Action m_task;
+        public ActionTask(Action task, Action<Exception> error, Action<Exception> cancel) : base(error,cancel) {
+            m_task = task;
+        }
+
+        public void Resolve() {
+            if (m_task != null && LockCancelation()) {
+                try {
+                    m_task();
+                    SetResult();
+                } catch(Exception err) {
+                    HandleErrorInternal(err);
+                }
+            }
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/ActionTaskBase.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,55 @@
+using System;
+using System.Threading;
+
+namespace Implab {
+    public class ActionTaskBase : AbstractPromise {
+        readonly Action<Exception> m_cancel;
+        readonly Action<Exception> m_error;
+
+        int m_cancelationLock;
+
+        protected ActionTaskBase( Action<Exception> error, Action<Exception> cancel) {
+            m_error = error;
+            m_cancel = cancel;
+        }
+
+        public void Reject(Exception error) {
+            Safe.ArgumentNotNull(error, "error");
+            if (LockCancelation())
+                HandleErrorInternal(error);
+        }
+
+        protected void HandleErrorInternal(Exception error) {
+            if (m_error != null) {
+                try {
+                    m_error(error);
+                    SetResult();
+                } catch(Exception err) {
+                    SetError(err);
+                }
+            } else {
+                SetError(error);
+            }
+        }
+
+        public override void CancelOperation(Exception reason) {
+            if (LockCancelation()) {
+                if (m_cancel != null) {
+                    try {
+                        m_cancel(reason);
+                        SetResult();
+                    } catch (Exception err) {
+                        HandleErrorInternal(err);
+                    }
+                } else {
+                    SetCancelled(reason);
+                }
+            }
+        }
+
+        protected bool LockCancelation() {
+            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/ActionTaskT.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,22 @@
+using System;
+
+namespace Implab {
+    public class ActionTask<T> : ActionTaskBase, IDeferred<T> {
+        readonly Action<T> m_task;
+        public ActionTask(Action<T> task, Action<Exception> error, Action<Exception> cancel) : base(error,cancel) {
+            m_task = task;
+        }
+
+        public void Resolve(T value) {
+            if (m_task != null && LockCancelation()) {
+                try {
+                    m_task(value);
+                    SetResult();
+                } catch(Exception err) {
+                    HandleErrorInternal(err);
+                }
+            }
+        }
+    }
+}
+
--- a/Implab/ChainTask.cs	Fri Mar 06 15:45:26 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-using System;
-using System.Threading;
-
-namespace Implab {
-    public class ChainTask : AbstractPromise, IDeferred {
-        readonly Func<IPromise> m_task;
-        readonly Action<Exception> m_error;
-        readonly Action<Exception> m_cancel;
-
-        int m_cancelationLock;
-
-        public ChainTask(Func<IPromise> task, Func<Exception> error, Func<Exception> cancel) {
-            m_task = task;
-        }
-
-        public void Resolve() {
-            if (m_task != null && LockCancelation()) {
-                try {
-                    var operation = m_task();
-                    if (operation == null)
-                        throw new NullReferenceException("The task returned null promise");
-                        
-                    operation.On(SetResult, SetError, SetCancelled);
-
-                    CancelationRequested(operation.Cancel);
-                } catch(Exception err) {
-                    HandleErrorInternal(err);
-                }
-            }
-        }
-
-        public void Reject(Exception error) {
-            throw new NotImplementedException();
-        }
-
-        protected void HandleErrorInternal(Exception error) {
-            if (m_error != null) {
-                try {
-                    m_error(error);
-                    SetResult();
-                } catch(Exception err) {
-                    SetError(err);
-                }
-            } else {
-                SetError(error);
-            }
-        }
-
-        protected bool LockCancelation() {
-            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
-        }
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/FuncChainTask.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,21 @@
+using System;
+
+namespace Implab {
+    public class FuncChainTask<TResult> : FuncChainTaskBase<TResult>, IDeferred {
+        readonly Func<IPromise<TResult>> m_task;
+
+        public FuncChainTask(Func<IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel) : base(error, cancel){
+            m_task = task;
+        }
+
+        public void Resolve() {
+            if (m_task != null && LockCancelation()) {
+                try {
+                    Observe(m_task());
+                } catch (Exception err) {
+                    HandleErrorInternal(err);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/FuncChainTaskBase.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,60 @@
+using System;
+using System.Threading;
+
+namespace Implab {
+    public class FuncChainTaskBase<TResult> : AbstractPromise<TResult> {
+        readonly Func<Exception, IPromise<TResult>> m_error;
+        readonly Func<Exception, IPromise<TResult>> m_cancel;
+
+        int m_cancelationLock;
+
+        protected FuncChainTaskBase( Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel) {
+            m_error = error;
+            m_cancel = cancel;
+        }
+
+        public void Reject(Exception error) {
+            if (LockCancelation())
+                HandleErrorInternal(error);
+        }
+
+        public override void CancelOperation(Exception reason) {
+            if (m_cancel != null && LockCancelation()) {
+                try {
+                    Observe(m_cancel(reason));
+                } catch(Exception err) {
+                    HandleErrorInternal(err);
+                }
+            }
+
+        }
+
+        protected void HandleErrorInternal(Exception error) {
+            if (m_error != null) {
+                try {
+                    Observe(m_error(error));
+                } catch(Exception err) {
+                    SetError(err);
+                }
+            } else {
+                SetError(error);
+            }
+        }
+
+        protected void Observe(IPromise<TResult> operation) {
+            if (operation == null)
+                throw new NullReferenceException("The task returned null promise");
+
+            // pass operation results to the current promise
+            operation.On(SetResult, SetError, SetCancelled);
+
+            // pass the cancelation request
+            CancellationRequested(operation.Cancel);
+        }
+
+        protected bool LockCancelation() {
+            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/FuncChainTaskT.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,21 @@
+using System;
+
+namespace Implab {
+    public class FuncChainTask<TArg,TResult> : FuncChainTaskBase<TResult>, IDeferred<TArg> {
+        readonly Func<TArg, IPromise<TResult>> m_task;
+
+        public FuncChainTask(Func<TArg, IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel) : base(error, cancel){
+            m_task = task;
+        }
+
+        public void Resolve(TArg value) {
+            if (m_task != null && LockCancelation()) {
+                try {
+                    Observe(m_task(value));
+                } catch (Exception err) {
+                    HandleErrorInternal(err);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
--- a/Implab/ICancelationToken.cs	Fri Mar 06 15:45:26 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-using System;
-
-namespace Implab {
-    public interface ICancelationToken {
-        /// <summary>
-        /// Indicates wherther the cancellation was requested.
-        /// </summary>
-        bool IsCancelRequested { get ; }
-
-        /// <summary>
-        /// The reason why the operation should be cancelled.
-        /// </summary>
-        Exception CancelReason { get ; }
-
-        /// <summary>
-        /// Accepts if requested.
-        /// </summary>
-        /// <returns><c>true</c>, if if requested was accepted, <c>false</c> otherwise.</returns>
-        bool AcceptIfRequested();
-
-        /// <summary>
-        /// Sets the token to cancelled state.
-        /// </summary>
-        /// <param name="reason">The reason why the operation was cancelled.</param>
-        void CancelOperation(Exception reason);
-
-        /// <summary>
-        /// Adds the listener for the cancellation request, is the cancellation was requested the <paramref name="handler"/>
-        /// is executed immediatelly.
-        /// </summary>
-        /// <param name="handler">The handler which will be executed if the cancel occurs.</param>
-        void CancelationRequested(Action<Exception> handler);
-
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/ICancellationToken.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,36 @@
+using System;
+
+namespace Implab {
+    public interface ICancellationToken {
+        /// <summary>
+        /// Indicates wherther the cancellation was requested.
+        /// </summary>
+        bool IsCancellationRequested { get ; }
+
+        /// <summary>
+        /// The reason why the operation should be cancelled.
+        /// </summary>
+        Exception CancellationReason { get ; }
+
+        /// <summary>
+        /// Accepts if requested.
+        /// </summary>
+        /// <returns><c>true</c>, if if requested was accepted, <c>false</c> otherwise.</returns>
+        bool CancelOperationIfRequested();
+
+        /// <summary>
+        /// Sets the token to cancelled state.
+        /// </summary>
+        /// <param name="reason">The reason why the operation was cancelled.</param>
+        void CancelOperation(Exception reason);
+
+        /// <summary>
+        /// Adds the listener for the cancellation request, is the cancellation was requested the <paramref name="handler"/>
+        /// is executed immediatelly.
+        /// </summary>
+        /// <param name="handler">The handler which will be executed if the cancel occurs.</param>
+        void CancellationRequested(Action<Exception> handler);
+
+    }
+}
+
--- a/Implab/IDeferred.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/IDeferred.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -4,7 +4,7 @@
     /// <summary>
     /// Deferred result, usually used by asynchronous services as the service part of the promise.
     /// </summary>
-    public interface IDeferred : ICancelationToken {
+    public interface IDeferred : ICancellationToken {
 
         void Resolve();
 
--- a/Implab/IDeferredT.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/IDeferredT.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -1,7 +1,7 @@
 using System;
 
 namespace Implab {
-    public interface IDeferred<T> : ICancelationToken {
+    public interface IDeferred<in T> : ICancellationToken {
         void Resolve(T value);
 
         void Reject(Exception error);
--- a/Implab/ITaskController.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/ITaskController.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -1,7 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 
 namespace Implab {
     public interface ITaskController: IProgressHandler, ICancellable {
--- a/Implab/Implab.csproj	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/Implab.csproj	Sun Mar 08 02:52:27 2015 +0300
@@ -157,14 +157,24 @@
     <Compile Include="Diagnostics\ILogWriter.cs" />
     <Compile Include="Diagnostics\ListenerBase.cs" />
     <Compile Include="Parallels\BlockingQueue.cs" />
-    <Compile Include="ICancelationToken.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="ChainTask.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" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup />
--- a/Implab/Parallels/ArrayTraits.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/Parallels/ArrayTraits.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -152,7 +152,7 @@
                 throw new ArgumentOutOfRangeException("threads","Threads number must be greater then zero");
 
             if (source.Length == 0)
-                return Promise<TDst[]>.ResultToPromise(new TDst[0]);
+                return Promise<TDst[]>.FromResult(new TDst[0]);
 
             var promise = new Promise<TDst[]>();
             var res = new TDst[source.Length];
--- a/Implab/Parallels/AsyncPool.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/Parallels/AsyncPool.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -31,6 +31,24 @@
 			return p;
 		}
 
+        public static IPromise<T> Invoke<T>(Func<ICancellationToken, T> func) {
+            var p = new Promise<T>();
+            var caller = TraceContext.Instance.CurrentOperation;
+
+            ThreadPool.QueueUserWorkItem(param => {
+                TraceContext.Instance.EnterLogicalOperation(caller,false);
+                try {
+                    p.Resolve(func(p));
+                } catch(Exception e) {
+                    p.Reject(e);
+                } finally {
+                    TraceContext.Instance.Leave();
+                }
+            });
+
+            return p;
+        }
+
         public static IPromise<T> RunThread<T>(Func<T> func) {
             var p = new Promise<T>();
 
@@ -52,6 +70,27 @@
             return p;
         }
 
+        public static IPromise<T> RunThread<T>(Func<ICancellationToken, T> func) {
+            var p = new Promise<T>();
+
+            var caller = TraceContext.Instance.CurrentOperation;
+
+            var worker = new Thread(() => {
+                TraceContext.Instance.EnterLogicalOperation(caller,false);
+                try {
+                    p.Resolve(func(p));
+                } catch (Exception e) {
+                    p.Reject(e);
+                } finally {
+                    TraceContext.Instance.Leave();
+                }
+            });
+            worker.IsBackground = true;
+            worker.Start();
+
+            return p;
+        }
+
 
         public static IPromise RunThread(Action func) {
             var p = new Promise();
@@ -75,12 +114,42 @@
             return p;
         }
 
+        public static IPromise RunThread(Action<ICancellationToken> func) {
+            var p = new Promise();
+
+            var caller = TraceContext.Instance.CurrentOperation;
+
+            var worker = new Thread(() => {
+                TraceContext.Instance.EnterLogicalOperation(caller,false);
+                try {
+                    func(p);
+                    p.Resolve();
+                } catch (Exception e) {
+                    p.Reject(e);
+                } finally {
+                    TraceContext.Instance.Leave();
+                }
+            });
+            worker.IsBackground = true;
+            worker.Start();
+
+            return p;
+        }
+
         public static IPromise[] RunThread(params Action[] func) {
             return func.Select(f => RunThread(f)).ToArray();
         }
 
+        public static IPromise[] RunThread(params Action<ICancellationToken>[] func) {
+            return func.Select(f => RunThread(f)).ToArray();
+        }
+
         public static IPromise<T>[] RunThread<T>(params Func<T>[] func) {
             return func.Select(f => RunThread(f)).ToArray();
         }
+
+        public static IPromise<T>[] RunThread<T>(params Func<ICancellationToken, T>[] func) {
+            return func.Select(f => RunThread(f)).ToArray();
+        }
 	}
 }
--- a/Implab/PromiseExtensions.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/PromiseExtensions.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -174,6 +174,116 @@
 
             return medium;
         }
+
+        public static IPromise Then(this IPromise that, Action success, Action<Exception> error, Action<Exception> cancel) {
+            Safe.ArgumentNotNull(that, "that");
+
+            var d = new ActionTask(success, error, cancel);
+            that.On(d.Resolve, d.Reject, d.CancelOperation);
+            if (success != null)
+                d.CancellationRequested(that.Cancel);
+            return d;
+        }
+
+        public static IPromise Then(this IPromise that, Action success, Action<Exception> error) {
+            return Then(that, success, error, null);
+        }
+
+        public static IPromise Then(this IPromise that, Action success) {
+            return Then(that, success, null, null);
+        }
+
+        public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error, Func<Exception, T> cancel) {
+            Safe.ArgumentNotNull(that, "that");
+
+            var d = new FuncTask<T>(success, error, cancel);
+            that.On(d.Resolve, d.Reject, d.CancelOperation);
+            if (success != null)
+                d.CancellationRequested(that.Cancel);
+            return d;
+        }
+
+        public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error) {
+            return Then(that, success, error, null);
+        }
+
+        public static IPromise<T> Then<T>(this IPromise that, Func<T> success) {
+            return Then(that, success, null, null);
+        }
+
+        public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error, Func<Exception, T2> cancel) {
+            Safe.ArgumentNotNull(that, "that");
+            var d = new FuncTask<T,T2>(success, error, cancel);
+            that.On(d.Resolve, d.Reject, d.CancelOperation);
+            if (success != null)
+                d.CancellationRequested(that.Cancel);
+            return d;
+        }
+
+        public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error) {
+            return Then(that, success, error, null);
+        }
+
+        public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success) {
+            return Then(that, success, null, null);
+        }
+
+        #region chain traits
+        public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception,IPromise> error, Func<Exception,IPromise> cancel) {
+            Safe.ArgumentNotNull(that, "that");
+
+            var d = new ActionChainTask(success, error, cancel);
+            that.On(d.Resolve, d.Reject, d.CancelOperation);
+            if (success != null)
+                d.CancellationRequested(that.Cancel);
+            return d;
+        }
+
+        public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception,IPromise> error) {
+            return Chain(that, success, error, null);
+        }
+
+        public static IPromise Chain(this IPromise that, Func<IPromise> success) {
+            return Chain(that, success, null, null);
+        }
+
+        public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error, Func<Exception, IPromise<T>> cancel) {
+            Safe.ArgumentNotNull(that, "that");
+
+            var d = new FuncChainTask<T>(success, error, cancel);
+            that.On(d.Resolve, d.Reject, d.CancelOperation);
+            if (success != null)
+                d.CancellationRequested(that.Cancel);
+            return d;
+        }
+
+        public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error) {
+            return Chain(that, success, error, null);
+        }
+
+        public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success) {
+            return Chain(that, success, null, null);
+        }
+
+        public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel) {
+            Safe.ArgumentNotNull(that, "that");
+            var d = new FuncChainTask<T,T2>(success, error, cancel);
+            that.On(d.Resolve, d.Reject, d.CancelOperation);
+            if (success != null)
+                d.CancellationRequested(that.Cancel);
+            return d;
+        }
+
+        public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error) {
+            return Chain(that, success, error, null);
+        }
+
+        public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success) {
+            return Chain(that, success, null, null);
+        }
+
+        #endregion
+
             
         #if NET_4_5
 
--- a/Implab/Safe.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/Safe.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -109,9 +109,9 @@
             ArgumentNotNull(action, "action");
 
             try {
-                return action() ?? Promise<T>.ExceptionToPromise(new Exception("The action returned null"));
+                return action() ?? Promise<T>.FromException(new Exception("The action returned null"));
             } catch (Exception err) {
-                return Promise<T>.ExceptionToPromise(err);
+                return Promise<T>.FromException(err);
             }
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/SuccessPromise.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,111 @@
+using System;
+
+namespace Implab {
+    public class SuccessPromise : IPromise {
+        #region IPromise implementation
+
+        public IPromise On(Action success, Action<Exception> error, Action<Exception> cancel) {
+            if (success != null) {
+                try {
+                    success();
+                } catch(Exception err) {
+                    if (error != null) {
+                        try {
+                            error(err);
+                        // Analysis disable once EmptyGeneralCatchClause
+                        } catch {
+                        }
+                    }
+                }
+            }
+            return this;
+        }
+
+        public IPromise On(Action success, Action<Exception> error) {
+            if (success != null) {
+                try {
+                    success();
+                } catch(Exception err) {
+                    if (error != null) {
+                        try {
+                            error(err);
+                            // Analysis disable once EmptyGeneralCatchClause
+                        } catch {
+                        }
+                    }
+                }
+            }
+            return this;
+        }
+
+        public IPromise On(Action success) {
+            if (success != null) {
+                try {
+                    success();
+                    // Analysis disable once EmptyGeneralCatchClause
+                } catch {
+                }
+            }
+            return this;
+        }
+
+        public IPromise On(Action handler, PromiseEventType events) {
+            if (handler != null && events.HasFlag(PromiseEventType.Success)) {
+                try {
+                    handler();
+                    // Analysis disable once EmptyGeneralCatchClause
+                } catch {
+                }
+            }
+            return this;
+        }
+
+        public IPromise<T> Cast<T>() {
+            throw new InvalidCastException();
+        }
+
+        public void Join() {
+        }
+
+        public void Join(int timeout) {
+        }
+
+        public Type PromiseType {
+            get {
+                return typeof(void);
+            }
+        }
+
+        public bool IsResolved {
+            get {
+                return true;
+            }
+        }
+
+        public bool IsCancelled {
+            get {
+                return false;
+            }
+        }
+
+        public Exception Error {
+            get {
+                return null;
+            }
+        }
+
+        #endregion
+
+        #region ICancellable implementation
+
+        public void Cancel() {
+        }
+
+        public void Cancel(Exception reason) {
+        }
+
+        #endregion
+
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/SuccessPromiseT.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -0,0 +1,177 @@
+using System;
+
+namespace Implab {
+    public class SuccessPromise<T> : IPromise<T> {
+        readonly T m_value;
+
+        public SuccessPromise(T value){
+            m_value = value;
+        }
+
+        public IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel) {
+            if (success != null) {
+                try {
+                    success(m_value);
+                } catch(Exception err) {
+                    if (error != null) {
+                        try {
+                            error(err);
+                            // Analysis disable once EmptyGeneralCatchClause
+                        } catch {
+                        }
+                    }
+                }
+            }
+            return this;
+        }
+
+        public IPromise<T> On(Action<T> success, Action<Exception> error) {
+            if (success != null) {
+                try {
+                    success(m_value);
+                } catch(Exception err) {
+                    if (error != null) {
+                        try {
+                            error(err);
+                            // Analysis disable once EmptyGeneralCatchClause
+                        } catch {
+                        }
+                    }
+                }
+            }
+            return this;
+        }
+
+        public IPromise<T> On(Action<T> success) {
+            if (success != null) {
+                try {
+                    success(m_value);
+                    // Analysis disable once EmptyGeneralCatchClause
+                } catch {
+                }
+            }
+            return this;
+        }
+
+        public T Join() {
+            return m_value;
+        }
+
+        public T Join(int timeout) {
+            return m_value;
+        }
+
+        public IPromise<T> On(Action success, Action<Exception> error, Action<Exception> cancel) {
+            if (success != null) {
+                try {
+                    success();
+                } catch(Exception err) {
+                    if (error != null) {
+                        try {
+                            error(err);
+                            // Analysis disable once EmptyGeneralCatchClause
+                        } catch {
+                        }
+                    }
+                }
+            }
+            return this;
+        }
+
+        public IPromise<T> On(Action success, Action<Exception> error) {
+            if (success != null) {
+                try {
+                    success();
+                } catch(Exception err) {
+                    if (error != null) {
+                        try {
+                            error(err);
+                            // Analysis disable once EmptyGeneralCatchClause
+                        } catch {
+                        }
+                    }
+                }
+            }
+            return this;
+        }
+
+        public IPromise<T> On(Action success) {
+            if (success != null) {
+                try {
+                    success();
+                    // Analysis disable once EmptyGeneralCatchClause
+                } catch {
+                }
+            }
+            return this;
+        }
+            
+        public IPromise<T> On(Action handler, PromiseEventType events) {
+            if (handler != null && events.HasFlag(PromiseEventType.Success)) {
+                try {
+                    handler();
+                // Analysis disable once EmptyGeneralCatchClause
+                } catch {
+                }
+            }
+            return this;
+        }
+
+        IPromise IPromise.On(Action success, Action<Exception> error, Action<Exception> cancel) {
+            return On(success, error, cancel);
+        }
+
+        IPromise IPromise.On(Action success, Action<Exception> error) {
+            return On(success, error);
+        }
+
+        IPromise IPromise.On(Action success) {
+            return On(success);
+        }
+
+        IPromise IPromise.On(Action handler, PromiseEventType events) {
+            return On(handler, events);
+        }
+
+        public IPromise<T2> Cast<T2>() {
+            return new SuccessPromise<T2>((T2)(object)m_value);
+        }
+
+        void IPromise.Join() {
+        }
+
+        void IPromise.Join(int timeout) {
+        }
+
+        public Type PromiseType {
+            get {
+                return typeof(T);
+            }
+        }
+
+        public bool IsResolved {
+            get {
+                return true;
+            }
+        }
+
+        public bool IsCancelled {
+            get {
+                return false;
+            }
+        }
+
+        public Exception Error {
+            get {
+                return null;
+            }
+        }
+
+        public void Cancel() {
+        }
+
+        public void Cancel(Exception reason) {
+        }
+    }
+}
+
--- a/Implab/SyncContextPromise.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/Implab/SyncContextPromise.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -10,15 +10,15 @@
             m_context = context;
         }
 
-        protected override void SignalSuccess(IDeferred<T> handler) {
+        protected override void SignalSuccess(Promise<T>.HandlerDescriptor handler) {
             m_context.Post(x => base.SignalSuccess(handler), null);
         }
 
-        protected override void SignalError(IDeferred<T> handler, Exception error) {
+        protected override void SignalError(Promise<T>.HandlerDescriptor handler, Exception error) {
             m_context.Post(x => base.SignalError(handler, error), null);
         }
 
-        protected override void SignalCancelled(IDeferred<T> handler, Exception reason) {
+        protected override void SignalCancelled(Promise<T>.HandlerDescriptor handler, Exception reason) {
             m_context.Post(x => base.SignalCancelled(handler, reason), null);
         }
     }
--- a/MonoPlay/Program.cs	Fri Mar 06 15:45:26 2015 +0300
+++ b/MonoPlay/Program.cs	Sun Mar 08 02:52:27 2015 +0300
@@ -8,86 +8,33 @@
 
 namespace MonoPlay {
     class MainClass {
+
+
         public static void Main(string[] args) {
             if (args == null)
                 throw new ArgumentNullException("args");
 
             var t1 = Environment.TickCount;
 
-            const int reads = 100000;
-            const int writes = 1000;
-            const int readThreads = 8;
-            const int writeThreads = 0;
-
-            var l = new SharedLock();
-            var st = new HashSet<int>();
-
-            Action reader1 = () => {
-                for (int i =0; i < reads; i++) {
-                    try {
-                        l.LockShared();
-                        st.Contains(i % 1000);
-                        Thread.Sleep(0);
-                    } finally {
-                        l.Release();
-                    }
-                }
-            };
-
-            Action reader2 = () => {
-                for(var i = 0; i < reads; i++)
-                    lock(st) {
-                        st.Contains(i % 1000);
-                        Thread.Sleep(0);
-                    }
-            };
-
-            Action writer1 = () => {
-                var rnd = new Random(Environment.TickCount);
-                for (int i = 0; i < writes; i++) {
-                    try {
-                        l.LockExclusive();
-                        st.Add(rnd.Next(1000));
-                        //Thread.Sleep(1);
-                    } finally {
-                        l.Release();
-                    }
-                }
-            };
-
-            Action writer2 = () => {
-                var rnd = new Random(Environment.TickCount);
-                for (int i = 0; i < writes; i++) {
-                    lock (st) {
-                        st.Add(rnd.Next(1000));
-                        //Thread.Sleep(1);
-                    }
-                }
-            };
-
-
-
-            var readers = new IPromise[readThreads];
-            for (int i = 0; i < readThreads; i++)
-                readers[i] = AsyncPool.RunThread(reader2);
-
-            var writers = new IPromise[writeThreads];
-            for (int i = 0; i < writeThreads; i++)
-                writers[i] = AsyncPool.RunThread(writer1);
-
-
-            new [] {
-                readers.Bundle().On(() => Console.WriteLine("readers complete in {0} ms", Environment.TickCount - t1)),
-                writers.Bundle().On(() => Console.WriteLine("writers complete in {0} ms", Environment.TickCount - t1))
-            }.Bundle().Join();
-
-
+            for (int i = 0; i < 10000000; i++) {
+            
+                var p = new Promise<int>();
+                p.On(HandleResult);
+                p.Resolve(i);
+            }
 
             var t2 = Environment.TickCount;
             Console.WriteLine("done: {0} ms, {1:.00} Mb, {2} GC", t2 - t1, GC.GetTotalMemory(false) / (1024*1024), GC.CollectionCount(0) );
 
         }
 
+        static void HandleAction ()
+        {
+            
+        }
 
+        static void HandleResult(int x) {
+
+        }
     }
 }