# HG changeset patch
# User cin
# Date 2016-04-19 14:35:20
# Node ID dd4a3590f9c6d2a1e6380715b508399592b5ca55
# Parent 75103928da090cf50ce0feeaaacd9e867e4a90f8
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
diff --git a/Implab/AbstractTask.cs b/Implab/AbstractTask.cs
new file mode 100644
--- /dev/null
+++ b/Implab/AbstractTask.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Threading;
+
+namespace Implab {
+ ///
+ /// Базовый класс для реализации задачь. Задача представляет собой некторое
+ /// действие, которое можно иницировать и обработать результат его выполнения
+ /// в виде обещания, для этого оно реализует интерфейс .
+ ///
+ ///
+ /// Данный класс определяет стандартное поведение при обработки результатов, в частности
+ /// обработку и
+ ///
+ public abstract class AbstractTask : AbstractPromise {
+ int m_cancelationLock;
+
+ ///
+ /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения.
+ ///
+ /// true, if cancelation was locked, false otherwise.
+ 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)
+ );
+ }
+ }
+}
+
diff --git a/Implab/AbstractTaskT.cs b/Implab/AbstractTaskT.cs
new file mode 100644
--- /dev/null
+++ b/Implab/AbstractTaskT.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Threading;
+
+namespace Implab {
+ public abstract class AbstractTask : AbstractPromise {
+ int m_cancelationLock;
+
+ ///
+ /// Получает эксклюзивное право отмены задания, используется для отмены задания до начала его выполнения.
+ ///
+ /// true, if cancelation was locked, false otherwise.
+ 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)
+ );
+ }
+ }
+}
+
diff --git a/Implab/ActionChainTask.cs b/Implab/ActionChainTask.cs
--- a/Implab/ActionChainTask.cs
+++ b/Implab/ActionChainTask.cs
@@ -21,8 +21,10 @@ namespace Implab {
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);
}
diff --git a/Implab/ActionChainTaskBase.cs b/Implab/ActionChainTaskBase.cs
--- a/Implab/ActionChainTaskBase.cs
+++ b/Implab/ActionChainTaskBase.cs
@@ -2,12 +2,10 @@
using System.Threading;
namespace Implab {
- public class ActionChainTaskBase : AbstractPromise {
+ public class ActionChainTaskBase : AbstractTask {
readonly Func m_error;
readonly Func m_cancel;
- int m_cancelationLock;
-
protected ActionChainTaskBase(Func error, Func cancel, bool autoCancellable) {
m_error = error;
m_cancel = cancel;
@@ -21,55 +19,44 @@ namespace Implab {
}
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);
- }
}
}
diff --git a/Implab/ActionChainTaskT.cs b/Implab/ActionChainTaskT.cs
--- a/Implab/ActionChainTaskT.cs
+++ b/Implab/ActionChainTaskT.cs
@@ -12,8 +12,10 @@ namespace Implab {
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);
}
diff --git a/Implab/ActionTask.cs b/Implab/ActionTask.cs
--- a/Implab/ActionTask.cs
+++ b/Implab/ActionTask.cs
@@ -12,6 +12,8 @@ namespace Implab {
try {
m_task();
SetResult();
+ } catch(OperationCanceledException reason) {
+ HandleCancelInternal(reason);
} catch(Exception err) {
HandleErrorInternal(err);
}
diff --git a/Implab/ActionTaskBase.cs b/Implab/ActionTaskBase.cs
--- a/Implab/ActionTaskBase.cs
+++ b/Implab/ActionTaskBase.cs
@@ -1,13 +1,10 @@
using System;
-using System.Threading;
namespace Implab {
- public class ActionTaskBase : AbstractPromise {
+ public class ActionTaskBase : AbstractTask {
readonly Action m_cancel;
readonly Action m_error;
- int m_cancelationLock;
-
protected ActionTaskBase( Action error, Action cancel, bool autoCancellable) {
m_error = error;
m_cancel = cancel;
@@ -21,37 +18,36 @@ namespace Implab {
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);
- }
}
}
diff --git a/Implab/ActionTaskT.cs b/Implab/ActionTaskT.cs
--- a/Implab/ActionTaskT.cs
+++ b/Implab/ActionTaskT.cs
@@ -12,6 +12,8 @@ namespace Implab {
try {
m_task(value);
SetResult();
+ } catch(OperationCanceledException reason) {
+ HandleCancelInternal(reason);
} catch(Exception err) {
HandleErrorInternal(err);
}
diff --git a/Implab/Components/RunnableComponent.cs b/Implab/Components/RunnableComponent.cs
--- a/Implab/Components/RunnableComponent.cs
+++ b/Implab/Components/RunnableComponent.cs
@@ -137,10 +137,6 @@ namespace Implab.Components {
}
}
throw new PromiseTransientException(e);
- },
- r => {
- // handle cancellation as exception
- throw new OperationCanceledException("The operation has been cancelled", r);
}
);
@@ -194,7 +190,13 @@ namespace Implab.Components {
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();
}
}
diff --git a/Implab/FuncChainTask.cs b/Implab/FuncChainTask.cs
--- a/Implab/FuncChainTask.cs
+++ b/Implab/FuncChainTask.cs
@@ -13,8 +13,10 @@ namespace Implab {
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);
}
diff --git a/Implab/FuncChainTaskBase.cs b/Implab/FuncChainTaskBase.cs
--- a/Implab/FuncChainTaskBase.cs
+++ b/Implab/FuncChainTaskBase.cs
@@ -1,13 +1,10 @@
using System;
-using System.Threading;
namespace Implab {
- public class FuncChainTaskBase : AbstractPromise {
+ public class FuncChainTaskBase : AbstractTask {
readonly Func> m_error;
readonly Func> m_cancel;
- int m_cancelationLock;
-
protected FuncChainTaskBase( Func> error, Func> cancel, bool autoCancellable) {
m_error = error;
m_cancel = cancel;
@@ -21,37 +18,36 @@ namespace Implab {
}
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());
+ }
}
}
}
diff --git a/Implab/FuncChainTaskT.cs b/Implab/FuncChainTaskT.cs
--- a/Implab/FuncChainTaskT.cs
+++ b/Implab/FuncChainTaskT.cs
@@ -14,6 +14,8 @@ namespace Implab {
var operation = m_task(value);
operation.On(SetResult, HandleErrorInternal, SetCancelled);
CancellationRequested(operation.Cancel);
+ } catch (OperationCanceledException reason) {
+ HandleCancelInternal(reason);
} catch (Exception err) {
HandleErrorInternal(err);
}
diff --git a/Implab/FuncTask.cs b/Implab/FuncTask.cs
--- a/Implab/FuncTask.cs
+++ b/Implab/FuncTask.cs
@@ -13,6 +13,8 @@ namespace Implab {
if (m_task != null && LockCancelation()) {
try {
SetResult(m_task());
+ } catch(OperationCanceledException reason) {
+ HandleCancelInternal(reason);
} catch(Exception err) {
HandleErrorInternal(err);
}
diff --git a/Implab/FuncTaskBase.cs b/Implab/FuncTaskBase.cs
--- a/Implab/FuncTaskBase.cs
+++ b/Implab/FuncTaskBase.cs
@@ -1,13 +1,10 @@
using System;
-using System.Threading;
namespace Implab {
- public class FuncTaskBase : AbstractPromise {
+ public class FuncTaskBase : AbstractTask {
readonly Func m_cancel;
readonly Func m_error;
- int m_cancelationLock;
-
protected FuncTaskBase( Func error, Func cancel, bool autoCancellable) {
m_error = error;
m_cancel = cancel;
@@ -26,30 +23,30 @@ namespace Implab {
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);
- }
}
}
diff --git a/Implab/FuncTaskT.cs b/Implab/FuncTaskT.cs
--- a/Implab/FuncTaskT.cs
+++ b/Implab/FuncTaskT.cs
@@ -12,7 +12,9 @@ namespace Implab {
if (m_task != null && LockCancelation()) {
try {
SetResult(m_task(value));
- } catch (Exception err) {
+ } catch(OperationCanceledException reason) {
+ HandleCancelInternal(reason);
+ } catch(Exception err) {
HandleErrorInternal(err);
}
}
diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj
--- a/Implab/Implab.csproj
+++ b/Implab/Implab.csproj
@@ -193,6 +193,8 @@
+
+