changeset 187:dd4a3590f9c6 ref20160224

Reworked cancelation handling, if the cancel handler isn't specified the OperationCanceledException will be handled by the error handler Any unhandled OperationCanceledException will cause the promise cancelation
author cin
date Tue, 19 Apr 2016 17:35:20 +0300
parents 75103928da09
children 3071220371f8
files Implab/AbstractTask.cs Implab/AbstractTaskT.cs Implab/ActionChainTask.cs Implab/ActionChainTaskBase.cs Implab/ActionChainTaskT.cs Implab/ActionTask.cs Implab/ActionTaskBase.cs Implab/ActionTaskT.cs Implab/Components/RunnableComponent.cs Implab/FuncChainTask.cs Implab/FuncChainTaskBase.cs Implab/FuncChainTaskT.cs Implab/FuncTask.cs Implab/FuncTaskBase.cs Implab/FuncTaskT.cs Implab/Implab.csproj
diffstat 16 files changed, 186 insertions(+), 109 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/AbstractTask.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -0,0 +1,45 @@
+using System;
+using System.Threading;
+
+namespace Implab {
+    /// <summary>
+    /// Базовый класс для реализации задачь. Задача представляет собой некторое
+    /// действие, которое можно иницировать и обработать результат его выполнения
+    ///  в виде обещания, для этого оно реализует интерфейс <see cref="IPromise"/>.
+    /// </summary>
+    /// <remarks>
+    /// Данный класс определяет стандартное поведение при обработки результатов, в частности
+    /// обработку <see cref="System.OperationCanceledException"/> и <see cref="PromiseTransientException"/>
+    /// </remarks>
+    public abstract class AbstractTask : AbstractPromise {
+        int m_cancelationLock;
+
+        /// <summary>
+        /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения.
+        /// </summary>
+        /// <returns><c>true</c>, if cancelation was locked, <c>false</c> otherwise.</returns>
+        protected bool LockCancelation() {
+            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
+        }
+
+
+
+        protected void SetErrorInternal(Exception error) {
+            // unwrap 
+            while (error is PromiseTransientException && error.InnerException != null)
+                error = error.InnerException;
+            
+            if (error is OperationCanceledException)
+                SetCancelled(error);
+            else
+                SetError(error);
+        }
+
+        protected void SetCancelledInternal(Exception reason) {
+            SetCancelled(
+                reason == null ? new OperationCanceledException() : reason is OperationCanceledException ? reason : new OperationCanceledException(null, reason)
+            );
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/AbstractTaskT.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -0,0 +1,36 @@
+using System;
+using System.Threading;
+
+namespace Implab {
+    public abstract class AbstractTask<T> : AbstractPromise<T> {
+        int m_cancelationLock;
+
+        /// <summary>
+        /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения.
+        /// </summary>
+        /// <returns><c>true</c>, if cancelation was locked, <c>false</c> otherwise.</returns>
+        protected bool LockCancelation() {
+            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
+        }
+
+
+
+        protected void SetErrorInternal(Exception error) {
+            // unwrap 
+            while (error is PromiseTransientException && error.InnerException != null)
+                error = error.InnerException;
+
+            if (error is OperationCanceledException)
+                SetCancelled(error);
+            else
+                SetError(error);
+        }
+
+        protected void SetCancelledInternal(Exception reason) {
+            SetCancelled(
+                reason == null ? new OperationCanceledException() : reason is OperationCanceledException ? reason : new OperationCanceledException(null, reason)
+            );
+        }
+    }
+}
+
--- a/Implab/ActionChainTask.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/ActionChainTask.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -21,8 +21,10 @@
             if (m_task != null && LockCancelation()) {
                 try {
                     var p = m_task();
-                    p.On(SetResult, HandleErrorInternal, SetCancelled);
+                    p.On(SetResult, HandleErrorInternal, HandleCancelInternal);
                     CancellationRequested(p.Cancel);
+                } catch (OperationCanceledException reason){
+                    HandleCancelInternal(reason);
                 } catch(Exception err) {
                     HandleErrorInternal(err);
                 }
--- a/Implab/ActionChainTaskBase.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/ActionChainTaskBase.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -2,12 +2,10 @@
 using System.Threading;
 
 namespace Implab {
-    public class ActionChainTaskBase : AbstractPromise {
+    public class ActionChainTaskBase : AbstractTask {
         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, bool autoCancellable) {
             m_error = error;
             m_cancel = cancel;
@@ -21,55 +19,44 @@
         }
 
         public override void CancelOperation(Exception reason) {
-            if (LockCancelation()) {
-                if (!(reason is OperationCanceledException))
-                    reason = reason != null ? new OperationCanceledException(null, reason) : new OperationCanceledException();
-                
-                if (m_cancel != null) {
-                    try {
-                        m_cancel(reason).On(SetResult, HandleErrorInternal, HandleCancelInternal);
-                    } catch (Exception err) {
-                        HandleErrorInternal(err);
-                    }
-                } else {
-                    HandleErrorInternal(reason);
+            if (LockCancelation())
+                // отмена вызвана до начала выполнения задачи
+                HandleCancelInternal(reason);
+        }
+
+        protected void HandleCancelInternal(Exception reason) {
+            if (m_cancel != null) {
+                try {
+                    // вызываем обработчик отмены
+                    var p = m_cancel(reason);
+                    p.On(SetResult, HandleErrorInternal, SetCancelledInternal);
+                    // сообщаем асинхронной операции, что клиент уже не хочет получать результат
+                    // т.е. если он инициировал отмену, задача отменилась, вызвался обрабочик отмены
+                    // отбработчику сообщили, что результат уже не нужен и уже сам обработчик решает
+                    // отдавать ли результат или подтвердить отмену (или вернуть ошибку).
+                    CancellationRequested(p.Cancel);
+                } catch (Exception err) {
+                    HandleErrorInternal(err);
                 }
+            } else {
+                HandleErrorInternal(reason ?? new OperationCanceledException());
             }
         }
 
-        void HandleCancelInternal(Exception reason) {
-            if (!(reason is OperationCanceledException))
-                reason = reason != null ? new OperationCanceledException(null, reason) : new OperationCanceledException();
-            HandleErrorInternal(reason);
-        }
-
-        void HandleErrorInternal(Exception error) {
+        protected void HandleErrorInternal(Exception error) {
             if (m_error != null) {
                 try {
                     var p = m_error(error);
-                    p.On(SetResult, SetError, SetCancelled);
+                    p.On(SetResult, SetErrorInternal, SetCancelledInternal);
                     CancellationRequested(p.Cancel);
                 } catch (Exception err) {
-                    error = err;
+                    SetErrorInternal(error);
                 }
             } else {
                 SetErrorInternal(error);
             }
         }
 
-        void SetErrorInternal(Exception error) {
-            while (error is PromiseTransientException)
-                error = error.InnerException;
-
-            if (error is OperationCanceledException)
-                SetCancelled(error);
-            else
-                SetError(error);
-        }
-
-        protected bool LockCancelation() {
-            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
-        }
     }
 }
 
--- a/Implab/ActionChainTaskT.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/ActionChainTaskT.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -12,8 +12,10 @@
             if (m_task != null && LockCancelation()) {
                 try {
                     var p = m_task(value);
-                    p.On(SetResult, HandleErrorInternal, SetCancelled);
+                    p.On(SetResult, HandleErrorInternal, HandleCancelInternal);
                     CancellationRequested(p.Cancel);
+                } catch (OperationCanceledException reason) {
+                    HandleCancelInternal(reason);
                 } catch(Exception err) {
                     HandleErrorInternal(err);
                 }
--- a/Implab/ActionTask.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/ActionTask.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -12,6 +12,8 @@
                 try {
                     m_task();
                     SetResult();
+                } catch(OperationCanceledException reason) {
+                    HandleCancelInternal(reason);
                 } catch(Exception err) {
                     HandleErrorInternal(err);
                 }
--- a/Implab/ActionTaskBase.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/ActionTaskBase.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -1,13 +1,10 @@
 using System;
-using System.Threading;
 
 namespace Implab {
-    public class ActionTaskBase : AbstractPromise {
+    public class ActionTaskBase : AbstractTask {
         readonly Action<Exception> m_cancel;
         readonly Action<Exception> m_error;
 
-        int m_cancelationLock;
-
         protected ActionTaskBase( Action<Exception> error, Action<Exception> cancel, bool autoCancellable) {
             m_error = error;
             m_cancel = cancel;
@@ -21,37 +18,36 @@
                 HandleErrorInternal(error);
         }
 
+        public override void CancelOperation(Exception reason) {
+            if (LockCancelation())
+                HandleCancelInternal(reason);
+        }
+
         protected void HandleErrorInternal(Exception error) {
             if (m_error != null) {
                 try {
                     m_error(error);
                     SetResult();
                 } catch(Exception err) {
-                    SetError(err);
+                    SetErrorInternal(err);
                 }
             } else {
-                SetError(error);
+                SetErrorInternal(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 void HandleCancelInternal(Exception error) {
+            if (m_cancel != null) {
+                try {
+                    m_cancel(error);
+                    SetResult();
+                } catch(Exception err) {
+                    HandleErrorInternal(err);
                 }
+            } else {
+                HandleErrorInternal(error ?? new OperationCanceledException());
             }
         }
-
-        protected bool LockCancelation() {
-            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
-        }
     }
 }
 
--- a/Implab/ActionTaskT.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/ActionTaskT.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -12,6 +12,8 @@
                 try {
                     m_task(value);
                     SetResult();
+                } catch(OperationCanceledException reason) {
+                    HandleCancelInternal(reason);
                 } catch(Exception err) {
                     HandleErrorInternal(err);
                 }
--- a/Implab/Components/RunnableComponent.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/Components/RunnableComponent.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -137,10 +137,6 @@
                             }
                         }
                         throw new PromiseTransientException(e);
-                    },
-                    r => {
-                        // handle cancellation as exception
-                        throw new OperationCanceledException("The operation has been cancelled", r);
                     }
                 );
 
@@ -194,7 +190,13 @@
             if (current == null) {
                 stop.Resolve();
             } else {
-                current.On(stop.Resolve, stop.Reject, e => stop.Resolve());
+                // связваем текущую операцию с операцией остановки
+                current.On(
+                    stop.Resolve, // если текущая операция заверщилась, то можно начинать остановку
+                    stop.Reject, // если текущая операция дала ошибку - то все плохо, нельзя продолжать
+                    e => stop.Resolve() // если текущая отменилась, то можно начинать остановку
+                );
+                // посылаем текущей операции сигнал остановки
                 current.Cancel();
             }
         }
--- a/Implab/FuncChainTask.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/FuncChainTask.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -13,8 +13,10 @@
             if (m_task != null && LockCancelation()) {
                 try {
                     var operation = m_task();
-                    operation.On(SetResult, HandleErrorInternal, SetCancelled);
+                    operation.On(SetResult, HandleErrorInternal, HandleCancelInternal);
                     CancellationRequested(operation.Cancel);
+                } catch (OperationCanceledException reason) {
+                    HandleCancelInternal(reason);
                 } catch (Exception err) {
                     HandleErrorInternal(err);
                 }
--- a/Implab/FuncChainTaskBase.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/FuncChainTaskBase.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -1,13 +1,10 @@
 using System;
-using System.Threading;
 
 namespace Implab {
-    public class FuncChainTaskBase<TResult> : AbstractPromise<TResult> {
+    public class FuncChainTaskBase<TResult> : AbstractTask<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, bool autoCancellable) {
             m_error = error;
             m_cancel = cancel;
@@ -21,37 +18,36 @@
         }
 
         public override void CancelOperation(Exception reason) {
-            if (LockCancelation()) {
-                if (m_cancel != null) {
-                    try {
-                        m_cancel(reason).On(SetResult, HandleErrorInternal, SetCancelled);
-                    } catch (Exception err) {
-                        HandleErrorInternal(err);
-                    }
-                } else {
-                    SetCancelled(reason);
-                }
-            }
-
+            if (LockCancelation())
+                HandleCancelInternal(reason);
         }
 
         protected void HandleErrorInternal(Exception error) {
             if (m_error != null) {
                 try {
-                    var operation = m_error(error);
-
-                    operation.On(SetResult, SetError, SetCancelled);
-                    CancellationRequested(operation.Cancel);
+                    var p = m_error(error);
+                    p.On(SetResult, SetErrorInternal, SetCancelledInternal);
+                    CancellationRequested(p.Cancel);
                 } catch(Exception err) {
-                    SetError(err);
+                    SetErrorInternal(err);
                 }
             } else {
-                SetError(error);
+                SetErrorInternal(error);
             }
         }
 
-        protected bool LockCancelation() {
-            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
+        protected void HandleCancelInternal(Exception reason) {
+            if (m_cancel != null) {
+                try {
+                    var p = m_cancel(reason);
+                    p.On(SetResult, HandleErrorInternal, SetCancelledInternal);
+                    CancellationRequested(p.Cancel);
+                } catch (Exception err) {
+                    HandleErrorInternal(err);
+                }
+            } else {
+                HandleErrorInternal(reason ?? new OperationCanceledException());
+            }
         }
     }
 }
--- a/Implab/FuncChainTaskT.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/FuncChainTaskT.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -14,6 +14,8 @@
                     var operation = m_task(value);
                     operation.On(SetResult, HandleErrorInternal, SetCancelled);
                     CancellationRequested(operation.Cancel);
+                } catch (OperationCanceledException reason) {
+                    HandleCancelInternal(reason);
                 } catch (Exception err) {
                     HandleErrorInternal(err);
                 }
--- a/Implab/FuncTask.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/FuncTask.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -13,6 +13,8 @@
             if (m_task != null && LockCancelation()) {
                 try {
                     SetResult(m_task());
+                } catch(OperationCanceledException reason)  {
+                    HandleCancelInternal(reason);
                 } catch(Exception err) {
                     HandleErrorInternal(err);
                 }
--- a/Implab/FuncTaskBase.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/FuncTaskBase.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -1,13 +1,10 @@
 using System;
-using System.Threading;
 
 namespace Implab {
-    public class FuncTaskBase<TResult> : AbstractPromise<TResult> {
+    public class FuncTaskBase<TResult> : AbstractTask<TResult> {
         readonly Func<Exception, TResult> m_cancel;
         readonly Func<Exception, TResult> m_error;
 
-        int m_cancelationLock;
-
         protected FuncTaskBase( Func<Exception, TResult> error, Func<Exception, TResult> cancel, bool autoCancellable) {
             m_error = error;
             m_cancel = cancel;
@@ -26,30 +23,30 @@
                 try {
                     SetResult(m_error(error));
                 } catch(Exception err) {
-                    SetError(err);
+                    SetErrorInternal(err);
                 }
             } else {
-                SetError(error);
+                SetErrorInternal(error);
             }
         }
 
         public override void CancelOperation(Exception reason) {
-            if (LockCancelation()) {
-                if (m_cancel != null) {
-                    try {
-                        SetResult(m_cancel(reason));
-                    } catch (Exception err) {
-                        HandleErrorInternal(err);
-                    }
-                } else {
-                    SetCancelled(reason);
+            if (LockCancelation())
+                HandleCancelInternal(reason);
+        }
+
+        protected void HandleCancelInternal(Exception reason) {
+            if (m_cancel != null) {
+                try {
+                    SetResult(m_cancel(reason));
+                } catch (Exception err) {
+                    HandleErrorInternal(err);
                 }
+            } else {
+                HandleErrorInternal(reason ?? new OperationCanceledException());
             }
         }
 
-        protected bool LockCancelation() {
-            return 0 == Interlocked.CompareExchange(ref m_cancelationLock, 1, 0);
-        }
     }
 }
 
--- a/Implab/FuncTaskT.cs	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/FuncTaskT.cs	Tue Apr 19 17:35:20 2016 +0300
@@ -12,7 +12,9 @@
             if (m_task != null && LockCancelation()) {
                 try {
                     SetResult(m_task(value));
-                } catch (Exception err) {
+                } catch(OperationCanceledException reason)  {
+                    HandleCancelInternal(reason);
+                } catch(Exception err) {
                     HandleErrorInternal(err);
                 }
             }
--- a/Implab/Implab.csproj	Tue Apr 19 00:50:14 2016 +0300
+++ b/Implab/Implab.csproj	Tue Apr 19 17:35:20 2016 +0300
@@ -193,6 +193,8 @@
     <Compile Include="Automaton\AutomatonConst.cs" />
     <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" />
     <Compile Include="Components\LazyAndWeak.cs" />
+    <Compile Include="AbstractTask.cs" />
+    <Compile Include="AbstractTaskT.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup />