changeset 138:f75cfa58e3d4 v2

added ICancellable.Cancel(Exception) to allow specify the reason of cancellation
author cin
date Tue, 17 Feb 2015 18:16:26 +0300
parents 238e15580926
children 041b77711262
files Implab.Fx/ControlBoundPromise.cs Implab.Test/AsyncTests.cs Implab/AbstractPromise.cs Implab/Diagnostics/Extensions.cs Implab/ICancellable.cs Implab/IDeferred.cs Implab/IPromise.cs Implab/IPromiseT.cs Implab/Promise.cs Implab/PromiseExtensions.cs Implab/PromiseT.cs Implab/PromiseTransientException.cs Implab/SyncContextPromise.cs Implab/TaskController.cs MonoPlay/Program.cs
diffstat 15 files changed, 169 insertions(+), 257 deletions(-) [+]
line wrap: on
line diff
--- a/Implab.Fx/ControlBoundPromise.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab.Fx/ControlBoundPromise.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -19,11 +19,11 @@
                 base.SignalSuccess(handler);
         }
 
-        protected override void SignalCancelled(IDeferred<T> handler) {
+        protected override void SignalCancelled(IDeferred<T> handler, Exception reason) {
             if (m_target.InvokeRequired)
-                m_target.BeginInvoke(new Action<IDeferred<T>>(base.SignalCancelled), handler);
+                m_target.BeginInvoke(new Action<IDeferred<T>,Exception>(base.SignalCancelled), handler, reason);
             else
-                base.SignalCancelled(handler);
+                base.SignalCancelled(handler, reason);
         }
 
         protected override void SignalError(IDeferred<T> handler, Exception error) {
--- a/Implab.Test/AsyncTests.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab.Test/AsyncTests.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -53,7 +53,7 @@
             var p = new Promise<bool>();
             p.Cancel();
 
-            var p2 = p.Cancelled(() => {
+            var p2 = p.Then(x => x, null, reason => {
                 throw new ApplicationException("CANCELLED"); 
             });
 
@@ -72,10 +72,10 @@
             p.Cancel();
 
             var p2 = p
-                .Cancelled<bool>(() => {
+                .Then<bool>(x => x, null, reason => {
                     throw new ApplicationException("CANCELLED");
                 })
-                .Error(e => true);
+                .Then(x => x, e => true);
 
             Assert.AreEqual(true, p2.Join());
         }
@@ -116,7 +116,7 @@
         public void FixErrorTest() {
             var p = new Promise<int>();
 
-            var p2 = p.Error(e => 101);
+            var p2 = p.Then(x => x, e => 101);
 
             p.Reject(new Exception());
 
@@ -760,7 +760,7 @@
                         });
 
                     result
-                        .Cancelled(() => pSurvive.Resolve(true));
+                        .On(() => pSurvive.Resolve(true), PromiseEventType.Cancelled);
                     
                     return result;
                 });
--- a/Implab/AbstractPromise.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/AbstractPromise.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -68,9 +68,15 @@
         /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
         protected void SetError(Exception error) {
             if (BeginTransit()) {
-                m_error = error is PromiseTransientException ? error.InnerException : error;
-                CompleteTransit(REJECTED_STATE);
-                OnError();
+                if (error is OperationCanceledException) {
+                    CompleteTransit(CANCELLED_STATE);
+                    m_error = error.InnerException;
+                    OnCancelled();
+                } else {
+                    m_error = error is PromiseTransientException ? error.InnerException : error;
+                    CompleteTransit(REJECTED_STATE);
+                    OnError();
+                }
             } else {
                 WaitTransition();
                 if (m_state == SUCCEEDED_STATE)
@@ -82,8 +88,9 @@
         /// Отменяет операцию, если это возможно.
         /// </summary>
         /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
-        protected void SetCancelled() {
+        protected void SetCancelled(Exception reason) {
             if (BeginTransit()) {
+                m_error = reason;
                 CompleteTransit(CANCELLED_STATE);
                 OnCancelled();
             }
@@ -93,7 +100,7 @@
 
         protected abstract void SignalError(THandler handler, Exception error);
 
-        protected abstract void SignalCancelled(THandler handler);
+        protected abstract void SignalCancelled(THandler handler, Exception reason);
 
         void OnSuccess() {
             var hp = m_handlerPointer;
@@ -137,7 +144,7 @@
             var slot = hp +1 ;
             while (slot < m_handlersCommited) {
                 if (Interlocked.CompareExchange(ref m_handlerPointer, slot, hp) == hp) {
-                    SignalCancelled(m_handlers[slot]);
+                    SignalCancelled(m_handlers[slot], m_error);
                 }
                 hp = m_handlerPointer;
                 slot = hp +1 ;
@@ -146,7 +153,7 @@
             if (m_extraHandlers != null) {
                 THandler handler;
                 while (m_extraHandlers.TryDequeue(out handler))
-                    SignalCancelled(handler);
+                    SignalCancelled(handler, m_error);
             }
         }
 
@@ -241,7 +248,7 @@
                     SignalSuccess(handler);
                     break;
                 case CANCELLED_STATE:
-                    SignalCancelled(handler);
+                    SignalCancelled(handler, m_error);
                     break;
                 case REJECTED_STATE:
                     SignalError(handler, m_error);
@@ -282,10 +289,20 @@
         #region ICancellable implementation
 
         public void Cancel() {
-            SetCancelled();
+            SetCancelled(null);
+        }
+
+        public void Cancel(Exception reason) {
+            SetCancelled(reason);
         }
 
         #endregion
+
+        public Exception Error {
+            get {
+                return m_error;
+            }
+        }
     }
 }
 
--- a/Implab/Diagnostics/Extensions.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/Diagnostics/Extensions.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -17,9 +17,9 @@
                     TraceLog.EndLogicalOperation();
                     TraceContext.Instance.Leave();
                 },
-                () => {
+                reason => {
                     TraceContext.Instance.EnterLogicalOperation(op,true);
-                    TraceLog.TraceInformation("promise cancelled");
+                    TraceLog.TraceInformation("promise cancelled {0}", reason.Message);
                     TraceLog.EndLogicalOperation();
                     TraceContext.Instance.Leave();
                 }
--- a/Implab/ICancellable.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/ICancellable.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -6,5 +6,6 @@
 namespace Implab {
     public interface ICancellable {
         void Cancel();
+        void Cancel(Exception reason);
     }
 }
--- a/Implab/IDeferred.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/IDeferred.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -8,6 +8,16 @@
 
         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);
     }
 }
--- a/Implab/IPromise.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/IPromise.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -22,6 +22,11 @@
         bool IsCancelled { get; }
 
         /// <summary>
+        /// Исключение возникшее в результате выполнения обещания, либо причина отмены.
+        /// </summary>
+        Exception Error { get; }
+
+        /// <summary>
         /// Creates a new promise dependend on the current one and resolved on
         /// executing the specified handlers.
         /// </summary>
@@ -43,19 +48,21 @@
         /// exception then the dependant promise will be resolved successfully, otherwise the exception
         /// raised by the handler will be transmitted to the dependent promise. If the handler wants
         /// to passthrough the original exception it needs to wrap the exception with
-        /// the <see cref="PromiseTransientException"/>.
+        /// the <see cref="PromiseTransientException"/>. The handler may raise <see cref="OperationCanceledException"/>
+        /// to cancel the dependant promise, the innner exception specifies the reason why the promise
+        /// is canceled.
         /// </para>
         /// <para>
         /// If the cancelation handler is specified and the current promise is cancelled then the dependent
-        /// promise will be resolved after the handler is executed. If the cancelation hendler raises the
+        /// promise will be resolved after the handler is executed. If the cancelation handler raises the
         /// exception it will be passed to the dependent promise.
         /// </para>
         /// </remarks>
-        IPromise Then(Action success, Action<Exception> error, Action cancel);
+        IPromise Then(Action success, Action<Exception> error, Action<Exception> cancel);
         IPromise Then(Action success, Action<Exception> error);
         IPromise Then(Action success);
 
-        IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error, Func<IPromise> cancel);
+        IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel);
         IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error);
         IPromise Chain(Func<IPromise> chained);
 
@@ -66,7 +73,7 @@
         /// <param name="error">The handler is called if an error while completing the promise occurred.</param>
         /// <param name="cancel">The handler is called in case of promise cancellation.</param>
         /// <returns>The current promise.</returns>
-        IPromise On(Action success, Action<Exception> error, Action cancel);
+        IPromise On(Action success, Action<Exception> error, Action<Exception> cancel);
         IPromise On(Action success, Action<Exception> error);
         IPromise On(Action success);
 
@@ -80,42 +87,6 @@
         IPromise On(Action handler, PromiseEventType events);
 
         /// <summary>
-        /// Adds the specified error handler to the current promise
-        /// and creates the new dependant promise.
-        /// </summary>
-        /// <param name="error">
-        /// The error handler. If the error handler returns without
-        /// an error the dependant promise will be successfully resolved.
-        /// </param>
-        /// <returns>
-        /// The new dependant promise which will be resolved after the error
-        /// handler is executed.
-        /// </returns>
-        /// <remarks>
-        /// The successfull result of the current promise will be ignored.
-        /// </remarks>
-        IPromise Error(Action<Exception> error);
-
-        /// <summary>
-        /// Adds the specified cncellation handler to the current promise
-        /// and creates the new dependant promise.
-        /// </summary>
-        /// <returns>
-        /// The new dependant promise which will be resolved after the cancellation
-        /// handler is executed.
-        /// </returns>
-        /// <param name="handler">
-        /// The cancellation handler.
-        /// </param>
-        /// <remarks>
-        /// If the cancellation handler is executed without an error the dependent
-        /// promise will be successfully resolved, otherwise the raised exception
-        /// will be passed to the dependant promise. The successful result of the
-        /// current promise will be ignored.
-        /// </remarks>
-        IPromise Cancelled(Action handler);
-
-        /// <summary>
         /// Преобразует результат обещания к заданному типу и возвращает новое обещание.
         /// </summary>
         IPromise<T> Cast<T>();
--- a/Implab/IPromiseT.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/IPromiseT.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -7,7 +7,7 @@
 
         new T Join(int timeout);
 
-        IPromise<T> On(Action<T> success, Action<Exception> error, Action cancel);
+        IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel);
         
         IPromise<T> On(Action<T> success, Action<Exception> error);
 
@@ -15,20 +15,16 @@
 
         new IPromise<T> On(Action handler, PromiseEventType events);
 
-        IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error, Func<T2> cancel);
+        IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception, T2> error, Func<Exception, T2> cancel);
 
-        IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error);
+        IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception, T2> error);
 
         IPromise<T2> Then<T2>(Func<T, T2> mapper);
 
-        IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error, Func<IPromise<T2>> cancel);
+        IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel);
 
-        IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error);
+        IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception, IPromise<T2>> error);
 
         IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained);
-
-        IPromise<T2> Error<T2>(Func<Exception,T2> error);
-
-        IPromise<T2> Cancelled<T2>(Func<T2> handler);
     }
 }
--- a/Implab/Promise.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/Promise.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -7,10 +7,10 @@
         public struct HandlerDescriptor {
             readonly Action m_success;
             readonly Action<Exception> m_error;
-            readonly Action m_cancel;
+            readonly Action<Exception> m_cancel;
             readonly IDeferred m_deferred;
 
-            public HandlerDescriptor(Action success, Action<Exception> error, Action cancel, IDeferred deferred) {
+            public HandlerDescriptor(Action success, Action<Exception> error, Action<Exception> cancel, IDeferred deferred) {
                 m_success = success;
                 m_error = error;
                 m_cancel = cancel;
@@ -45,10 +45,18 @@
                 }
             }
 
-            public void SignalCancel() {
+            public void SignalCancel(Exception reason) {
                 if (m_cancel != null) {
                     try {
-                        m_cancel();
+                        m_cancel(reason);
+                        if (m_deferred != null)
+                            m_deferred.Resolve();
+                    } catch (Exception err) {
+                        SignalError(err);
+                    }
+                } else if (reason != null && m_error != null) {
+                    try {
+                        m_error(new OperationCanceledException("The operation was canceled.", reason));
                         if (m_deferred != null)
                             m_deferred.Resolve();
                     } catch (Exception err) {
@@ -56,7 +64,7 @@
                     }
                 } else {
                     if (m_deferred != null)
-                        m_deferred.Cancel();
+                        m_deferred.Cancel(reason);
                 }
             }
         }
@@ -80,15 +88,15 @@
             handler.SignalError(error);
         }
 
-        protected override void SignalCancelled(HandlerDescriptor handler) {
-            handler.SignalCancel();
+        protected override void SignalCancelled(HandlerDescriptor handler, Exception reason) {
+            handler.SignalCancel(reason);
         }
 
         protected override void Listen(PromiseEventType events, Action handler) {
             AddHandler(new HandlerDescriptor(
                 events.HasFlag(PromiseEventType.Success) ? handler : null,
                 events.HasFlag(PromiseEventType.Error) ? new Action<Exception>(err => handler()) : null,
-                events.HasFlag(PromiseEventType.Cancelled) ? handler : null,
+                events.HasFlag(PromiseEventType.Cancelled) ? new Action<Exception>(reason => handler()) : null,
                 null
             ));
         }
@@ -102,7 +110,7 @@
             }
         }
 
-        public IPromise Then(Action success, Action<Exception> error, Action cancel) {
+        public IPromise Then(Action success, Action<Exception> error, Action<Exception> cancel) {
             var promise = new Promise();
             if (success != null)
                 promise.On(Cancel, PromiseEventType.Cancelled);
@@ -120,7 +128,7 @@
             return Then(success, null, null);
         }
 
-        public IPromise On(Action success, Action<Exception> error, Action cancel) {
+        public IPromise On(Action success, Action<Exception> error, Action<Exception> cancel) {
             AddHandler(new HandlerDescriptor(success, error, cancel, null));
             return this;
         }
@@ -137,7 +145,7 @@
             return On(
                 events.HasFlag(PromiseEventType.Success) ? handler : null,
                 events.HasFlag(PromiseEventType.Error) ? new Action<Exception>(err => handler()) : null,
-                events.HasFlag(PromiseEventType.Cancelled) ? handler : null
+                events.HasFlag(PromiseEventType.Cancelled) ? new Action<Exception>(reason => handler()) : null
             );
         }
 
@@ -145,7 +153,7 @@
             throw new InvalidCastException();
         }
 
-        public IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error, Func<IPromise> cancel) {
+        public IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error, Func<Exception,IPromise> cancel) {
             var medium = new Promise();
 
             On(
@@ -168,13 +176,13 @@
                         medium.Reject(ex);
                     }
                 },
-                () => {
+                reason => {
                     if (medium.IsCancelled)
                         return;
                     if (cancel != null)
-                        ConnectPromise(cancel(), medium);
+                        ConnectPromise(cancel(reason), medium);
                     else
-                        medium.Cancel();
+                        medium.Cancel(reason);
                 }
             );
 
@@ -189,9 +197,9 @@
                 result.On(
                     medium.Resolve,
                     medium.Reject,
-                    () => medium.Reject(new OperationCanceledException())
+                    medium.Cancel
                 );
-                medium.On(result.Cancel, PromiseEventType.Cancelled);
+                medium.On(null,null,result.Cancel);
             } else {
                 medium.Reject(
                     new NullReferenceException(
@@ -209,50 +217,6 @@
         public IPromise Chain(Func<IPromise> chained) {
             return Chain(chained, null, null);
         }
-
-        public IPromise Error(Action<Exception> error) {
-            var promise = new Promise();
-            On(
-                null,
-                err => {
-                    if (error != null)
-                        try {
-                            error(err);
-                            promise.Resolve();
-                        } catch (Exception err2) {
-                            promise.Reject(err2);
-                        }
-                    else
-                        promise.Reject(err);
-                }
-            );
-
-            return promise;
-        }
-
-        public IPromise Cancelled(Action handler) {
-            var promise = new Promise();
-            On(
-                null,
-                null,
-                () => {
-                    if (handler != null) {
-                        try {
-                            handler();
-                            promise.Resolve();
-                        } catch (Exception err) {
-                            promise.Reject(err);
-                        }
-                    } else {
-                        promise.Cancel();
-                    }
-                }
-            );
-
-            return promise;
-        }
-
-
     }
 }
 
--- a/Implab/PromiseExtensions.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/PromiseExtensions.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -56,7 +56,7 @@
             Safe.ArgumentNotNull(that, "that");
             Safe.ArgumentNotNull(head, "head");
 
-            that.On(null,null,() => head.On(cleanup));
+            that.On(() => head.On(cleanup), PromiseEventType.Cancelled);
 
             return that;
         }
@@ -123,9 +123,9 @@
                                 new Exception("The dependency promise is failed", error)
                             );
                     },
-                    () => {
+                    reason => {
                         if (Interlocked.Increment(ref errors) == 1)
-                            medium.Reject(
+                            medium.Cancel(
                                 new Exception("The dependency promise is cancelled")
                             );
                     }
@@ -162,10 +162,10 @@
                                 new Exception("The dependency promise is failed", error)
                             );
                     },
-                    () => {
+                    reason => {
                         if (Interlocked.Increment(ref errors) == 1)
-                            medium.Reject(
-                                new Exception("The dependency promise is cancelled")
+                            medium.Cancel(
+                                new Exception("The dependency promise is cancelled", reason)
                             );
                     }
                 );
@@ -181,7 +181,7 @@
             Safe.ArgumentNotNull(that, "that");
             var tcs = new TaskCompletionSource<T>();
 
-            that.On(tcs.SetResult, tcs.SetException, tcs.SetCanceled);
+            that.On(tcs.SetResult, tcs.SetException, r => tcs.SetCanceled());
 
             return tcs.Task;
         }
--- a/Implab/PromiseT.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/PromiseT.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -39,7 +39,7 @@
     /// </remarks>
     public class Promise<T> : AbstractPromise<IDeferred<T>>, IPromise<T>, IDeferred<T> {
 
-        class StubDeferred : IDeferred<T> {
+        class StubDeferred : IDeferred, IDeferred<T> {
             public static readonly StubDeferred instance = new StubDeferred();
 
             StubDeferred() {
@@ -50,6 +50,9 @@
             public void Resolve(T value) {
             }
 
+            public void Resolve() {
+            }
+
             public void Reject(Exception error) {
             }
 
@@ -60,6 +63,9 @@
             public void Cancel() {
             }
 
+            public void Cancel(Exception reason) {
+            }
+
             #endregion
 
 
@@ -67,11 +73,11 @@
 
         class RemapDescriptor<T2> : IDeferred<T> {
             readonly Func<T,T2> m_remap;
-            readonly Func<Exception,T2> m_failed;
-            readonly Func<T2> m_cancel;
+            readonly Func<Exception, T2> m_failed;
+            readonly Func<Exception, T2> m_cancel;
             readonly IDeferred<T2> m_deferred;
 
-            public RemapDescriptor(Func<T,T2> remap, Func<Exception,T2> failed, Func<T2> cancel, IDeferred<T2> deferred ) {
+            public RemapDescriptor(Func<T,T2> remap, Func<Exception,T2> failed, Func<Exception, T2> cancel, IDeferred<T2> deferred ) {
                 Debug.Assert(deferred != null);
                 m_remap = remap;
                 m_failed = failed;
@@ -110,18 +116,21 @@
 
             #region ICancellable implementation
 
-            public void Cancel() {
+            public void Cancel(Exception reason) {
                 if (m_cancel != null) {
                     try {
-                        m_deferred.Resolve(m_cancel());
+                        m_deferred.Resolve(m_cancel(reason));
                     } catch (Exception ex) {
                         Reject(ex);
                     }
                 } else {
-                    m_deferred.Cancel();
+                    m_deferred.Cancel(reason);
                 }
             }
 
+            public void Cancel() {
+                Cancel(null);
+            }
             #endregion
         }
 
@@ -163,6 +172,10 @@
             #region ICancellable implementation
 
             public void Cancel() {
+                Cancel(null);
+            }
+
+            public void Cancel(Exception reason) {
                 if (m_events.HasFlag(PromiseEventType.Cancelled)){
                     try {
                         m_handler();
@@ -178,10 +191,10 @@
         class ValueEventDescriptor : IDeferred<T> {
             readonly Action<T> m_success;
             readonly Action<Exception> m_failed;
-            readonly Action m_cancelled;
+            readonly Action<Exception> m_cancelled;
             readonly IDeferred<T> m_deferred;
 
-            public ValueEventDescriptor(Action<T> success, Action<Exception> failed, Action cancelled, IDeferred<T> deferred) {
+            public ValueEventDescriptor(Action<T> success, Action<Exception> failed, Action<Exception> cancelled, IDeferred<T> deferred) {
                 Debug.Assert(deferred != null);
 
                 m_success = success;
@@ -220,29 +233,33 @@
 
             #region ICancellable implementation
 
-            public void Cancel() {
+            public void Cancel(Exception reason) {
                 if (m_cancelled != null) {
                     try {
-                        m_cancelled();
+                        m_cancelled(reason);
                         m_deferred.Resolve(default(T));
-                    } catch(Exception ex) {
+                    } catch (Exception ex) {
                         Reject(ex);
                     }
                 } else {
-                    m_deferred.Cancel();
+                    m_deferred.Cancel(reason);
                 }
             }
 
+            public void Cancel() {
+                Cancel(null);
+            }
+
             #endregion
         }
 
         public class EventDescriptor : IDeferred<T> {
             readonly Action m_success;
             readonly Action<Exception> m_failed;
-            readonly Action m_cancelled;
-            readonly IDeferred<T> m_deferred;
+            readonly Action<Exception> m_cancelled;
+            readonly IDeferred m_deferred;
 
-            public EventDescriptor(Action success, Action<Exception> failed, Action cancelled, IDeferred<T> deferred) {
+            public EventDescriptor(Action success, Action<Exception> failed, Action<Exception> cancelled, IDeferred deferred) {
                 Debug.Assert(deferred != null);
 
                 m_success = success;
@@ -257,7 +274,7 @@
                 if (m_success != null) {
                     try {
                         m_success();
-                        m_deferred.Resolve(value);
+                        m_deferred.Resolve();
                     } catch (Exception ex) {
                         Reject(ex);
                     }
@@ -268,34 +285,36 @@
                 if (m_failed != null) {
                     try {
                         m_failed(error);
-                        m_deferred.Resolve(default(T));
-                    }catch (Exception ex) 
-                    {
+                        m_deferred.Resolve();
+                    } catch (Exception ex) {
                         m_deferred.Reject(ex);
                     }
                 } else {
                     m_deferred.Reject(error);
                 }
-
             }
 
             #endregion
 
             #region ICancellable implementation
 
-            public void Cancel() {
+            public void Cancel(Exception reason) {
                 if (m_cancelled != null) {
                     try {
-                        m_cancelled();
-                        m_deferred.Resolve(default(T));
+                        m_cancelled(reason);
+                        m_deferred.Resolve();
                     } catch (Exception ex) {
                         Reject(ex);
                     }
                 } else {
-                    m_deferred.Cancel();
+                    m_deferred.Cancel(reason);
                 }
             }
 
+            public void Cancel() {
+                Cancel(null);
+            }
+
             #endregion
         }
 
@@ -327,7 +346,7 @@
             return m_result;
         }
             
-        public IPromise<T> On(Action<T> success, Action<Exception> error, Action cancel) {
+        public IPromise<T> On(Action<T> success, Action<Exception> error, Action<Exception> cancel) {
             AddHandler(new ValueEventDescriptor(success, error, cancel, StubDeferred.instance));
             return this;
         }
@@ -347,10 +366,10 @@
             return this;
         }
 
-        public IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception, T2> error, Func<T2> cancel) {
+        public IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception, T2> error, Func<Exception, T2> cancel) {
             var promise = new Promise<T2>();
             if (mapper != null)
-                promise.On(Cancel, PromiseEventType.Cancelled);
+                promise.On((Action)null, null, Cancel);
             AddHandler(new RemapDescriptor<T2>(mapper, error, cancel, promise));
             return promise;
         }
@@ -358,7 +377,7 @@
         public IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception, T2> error) {
             var promise = new Promise<T2>();
             if (mapper != null)
-                promise.On(Cancel, PromiseEventType.Cancelled);
+                promise.On((Action)null, null, Cancel);
             AddHandler(new RemapDescriptor<T2>(mapper, error, null, promise));
             return promise;
         }
@@ -366,12 +385,12 @@
         public IPromise<T2> Then<T2>(Func<T, T2> mapper) {
             var promise = new Promise<T2>();
             if (mapper != null)
-                promise.On(Cancel, PromiseEventType.Cancelled);
+                promise.On((Action)null, null, Cancel);
             AddHandler(new RemapDescriptor<T2>(mapper, null, null, promise));
             return promise;
         }
 
-        public IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception, IPromise<T2>> error, Func<IPromise<T2>> cancel) {
+        public IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel) {
             // this promise will be resolved when an asyc operation is started
             var promise = new Promise<IPromise<T2>>();
 
@@ -404,7 +423,7 @@
                 result.On(
                     medium.Resolve,
                     medium.Reject,
-                    () => medium.Reject(new OperationCanceledException())
+                    medium.Cancel
                 );
                 medium.On(result.Cancel, PromiseEventType.Cancelled);
             } else {
@@ -425,46 +444,10 @@
             return Chain(chained, null, null);
         }
 
-        public IPromise<T2> Error<T2>(Func<Exception, T2> error) {
-            var promise = new Promise<T2>();
-            if (error != null)
-                On(
-                    (Action<T>)null,
-                    ex => {
-                        try {
-                            promise.Resolve(error(ex));
-                        } catch (Exception ex2) {
-                            promise.Reject(ex2);
-                        }
-                    }
-                );
-            else
-                Listen(PromiseEventType.Error, () => promise.Resolve(default(T2)));
-            return promise;
-        }
-
-        public IPromise<T2> Cancelled<T2>(Func<T2> handler) {
-            var promise = new Promise<T2>();
-            if (handler != null)
-                On(
-                    (Action<T>)null,
-                    null,
-                    () => {
-                        try {
-                            promise.Resolve(handler());
-                        } catch (Exception ex) {
-                            promise.Reject(ex);
-                        }
-                });
-            else
-                Listen(PromiseEventType.Cancelled, () => promise.Resolve(default(T2)));
-            return promise;
-        }
-
-        public IPromise Then(Action success, Action<Exception> error, Action cancel) {
-            var promise = new Promise<T>();
+        public IPromise Then(Action success, Action<Exception> error, Action<Exception> cancel) {
+            var promise = new Promise();
             if (success != null)
-                promise.On(Cancel, PromiseEventType.Cancelled);
+                promise.On(null, null, Cancel);
 
             AddHandler(new EventDescriptor(success, error, cancel, promise));
 
@@ -479,7 +462,7 @@
             return Then(success, null, null);
         }
 
-        public IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error, Func<IPromise> cancel) {
+        public IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) {
             var promise = new Promise<IPromise>();
 
             AddHandler(
@@ -493,7 +476,7 @@
 
             var medium = new Promise();
             if (chained != null)
-                medium.On(Cancel, PromiseEventType.Cancelled);
+                medium.On(null, null, Cancel);
 
             promise.On(
                 result => ConnectPromise(result, medium),
@@ -509,9 +492,9 @@
                 result.On(
                     medium.Resolve,
                     medium.Reject,
-                    () => medium.Reject(new OperationCanceledException())
+                    medium.Cancel
                 );
-                medium.On(result.Cancel, PromiseEventType.Cancelled);
+                medium.On(null, null, result.Cancel);
             } else {
                 medium.Reject(
                     new NullReferenceException(
@@ -530,7 +513,7 @@
             return Chain(chained, null, null);
         }
 
-        public IPromise On(Action success, Action<Exception> error, Action cancel) {
+        public IPromise On(Action success, Action<Exception> error, Action<Exception> cancel) {
             AddHandler(new EventDescriptor(success,error,cancel, StubDeferred.instance));
             return this;
         }
@@ -550,43 +533,6 @@
             return this;
         }
 
-        public IPromise Error(Action<Exception> error) {
-            var promise = new Promise();
-            if (error != null)
-                On(
-                    (Action<T>)null,
-                    ex => {
-                    try {
-                        error(ex);
-                        promise.Resolve();
-                    } catch (Exception ex2) {
-                        promise.Reject(ex2);
-                    }
-                });
-            else
-                Listen(PromiseEventType.Error, promise.Resolve);
-            return promise;
-        }
-
-        public IPromise Cancelled(Action handler) {
-            var promise = new Promise();
-            if (handler != null)
-                On(
-                    (Action<T>)null,
-                    null,
-                    () => {
-                        try {
-                            handler();
-                            promise.Resolve();
-                        } catch (Exception ex) {
-                            promise.Reject(ex);
-                        }
-                    });
-            else
-                Listen(PromiseEventType.Cancelled, promise.Resolve);
-            return promise;
-        }
-
         public IPromise<T2> Cast<T2>() {
             return (IPromise<T2>)this;
         }
@@ -601,8 +547,8 @@
             handler.Reject(error);
         }
 
-        protected override void SignalCancelled(IDeferred<T> handler) {
-            handler.Cancel();
+        protected override void SignalCancelled(IDeferred<T> handler, Exception reason) {
+            handler.Cancel(reason);
         }
 
         protected override void Listen(PromiseEventType events, Action handler) {
--- a/Implab/PromiseTransientException.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/PromiseTransientException.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -1,7 +1,6 @@
 using System;
 
 namespace Implab {
-
     [Serializable]
     public class PromiseTransientException : Exception {
         /// <summary>
--- a/Implab/SyncContextPromise.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/SyncContextPromise.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -1,4 +1,5 @@
 using System.Threading;
+using System;
 
 namespace Implab {
     public class SyncContextPromise<T> : Promise<T> {
@@ -13,12 +14,12 @@
             m_context.Post(x => base.SignalSuccess(handler), null);
         }
 
-        protected override void SignalError(IDeferred<T> handler, System.Exception error) {
+        protected override void SignalError(IDeferred<T> handler, Exception error) {
             m_context.Post(x => base.SignalError(handler, error), null);
         }
 
-        protected override void SignalCancelled(IDeferred<T> handler) {
-            m_context.Post(x => base.SignalCancelled(handler), null);
+        protected override void SignalCancelled(IDeferred<T> handler, Exception reason) {
+            m_context.Post(x => base.SignalCancelled(handler, reason), null);
         }
     }
 }
--- a/Implab/TaskController.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/Implab/TaskController.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -99,6 +99,13 @@
             }
         }
 
+        public void Cancel(Exception reason) {
+            lock (m_lock) {
+                if (!m_cancelled)
+                    m_cancelled = true;
+            }
+        }
+
         protected virtual void OnCancelled() {
             var temp = Cancelled;
             if (temp != null) {
--- a/MonoPlay/Program.cs	Mon Feb 16 17:48:39 2015 +0300
+++ b/MonoPlay/Program.cs	Tue Feb 17 18:16:26 2015 +0300
@@ -69,7 +69,7 @@
 
             var readers = new IPromise[readThreads];
             for (int i = 0; i < readThreads; i++)
-                readers[i] = AsyncPool.RunThread(reader1);
+                readers[i] = AsyncPool.RunThread(reader2);
 
             var writers = new IPromise[writeThreads];
             for (int i = 0; i < writeThreads; i++)