diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -10,3 +10,4 @@ Implab.Fx/obj/
Implab.Fx/bin/
Implab.Fx.Test/bin/
Implab.Fx.Test/obj/
+_ReSharper.Implab/
diff --git a/Implab.Test/AsyncTests.cs b/Implab.Test/AsyncTests.cs
--- a/Implab.Test/AsyncTests.cs
+++ b/Implab.Test/AsyncTests.cs
@@ -1,10 +1,9 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Implab;
using System.Reflection;
using System.Threading;
-namespace Implab.Tests
+namespace Implab.Test
{
[TestClass]
public class AsyncTests
@@ -90,12 +89,39 @@ namespace Implab.Tests
public void PoolTest ()
{
var pid = Thread.CurrentThread.ManagedThreadId;
- var p = AsyncPool.Invoke (() => {
- return Thread.CurrentThread.ManagedThreadId;
- });
+ var p = AsyncPool.Invoke (() => Thread.CurrentThread.ManagedThreadId);
Assert.AreNotEqual (pid, p.Join ());
}
+
+ [TestMethod]
+ public void ComplexCase1Test() {
+ var flags = new bool[3];
+
+ // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
+
+ var p = PromiseHelper
+ .Sleep(200, "Alan")
+ .Cancelled(() => flags[0] = true)
+ .Chain(x =>
+ PromiseHelper
+ .Sleep(200, "Hi, " + x)
+ .Map( y => y )
+ .Cancelled(() => flags[1] = true)
+ )
+ .Cancelled(() => flags[2] = true);
+ Thread.Sleep(300);
+ p.Cancel();
+ try {
+ Assert.AreEqual(p.Join(), "Hi, Alan");
+ Assert.Fail("Shouldn't get here");
+ } catch(OperationCanceledException) {
+ }
+
+ Assert.IsFalse(flags[0]);
+ Assert.IsTrue(flags[1]);
+ Assert.IsTrue(flags[2]);
+ }
}
}
diff --git a/Implab.Test/Implab.Test.csproj b/Implab.Test/Implab.Test.csproj
--- a/Implab.Test/Implab.Test.csproj
+++ b/Implab.Test/Implab.Test.csproj
@@ -46,6 +46,7 @@
+
diff --git a/Implab.Test/PromiseHelper.cs b/Implab.Test/PromiseHelper.cs
new file mode 100644
--- /dev/null
+++ b/Implab.Test/PromiseHelper.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+
+namespace Implab.Test {
+ class PromiseHelper {
+ public static Promise Sleep(int timeout, T retVal) {
+ return AsyncPool.Invoke(() => {
+ Thread.Sleep(timeout);
+ return retVal;
+ });
+ }
+ }
+}
diff --git a/Implab.suo b/Implab.suo
index 0b6524115b93f134aba8070eab347a9c6b46817c..fe568ef8e83a685430ea861c1a089f2705ea5f4b
GIT binary patch
literal 44544
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 to cancel the whole promise chain, the parent promise will be cancelled only if it has only one promise
///
bool Cancel(bool dependencies);
+
+ ///
+ /// Registers handler for the case when the promise is cencelled. If the promise already cancelled the
+ /// handler will be invoked immediatelly.
+ ///
+ /// The handler
+ void HandleCancelled(Action handler);
}
}
diff --git a/Implab/Promise.cs b/Implab/Promise.cs
--- a/Implab/Promise.cs
+++ b/Implab/Promise.cs
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Reflection;
-using System.Text;
using System.Diagnostics;
using System.Threading;
@@ -10,9 +8,9 @@ namespace Implab {
public delegate void ErrorHandler(Exception e);
- public delegate void ResultHandler(T result);
- public delegate TNew ResultMapper(TSrc result);
- public delegate Promise ChainedOperation(TSrc result);
+ public delegate void ResultHandler(T result);
+ public delegate TNew ResultMapper(TSrc result);
+ public delegate Promise ChainedOperation(TSrc result);
///
/// Класс для асинхронного получения результатов. Так называемое "обещание".
@@ -55,20 +53,19 @@ namespace Implab {
public ErrorHandler errorHandler;
}
- IPromise m_parent;
+ readonly IPromise m_parent;
LinkedList m_resultHandlers = new LinkedList();
LinkedList m_cancelHandlers = new LinkedList();
- object m_lock = new Object();
- bool m_cancellable;
+ readonly object m_lock = new Object();
+ readonly bool m_cancellable;
+ int m_childrenCount = 0;
PromiseState m_state;
T m_result;
Exception m_error;
- int m_childrenCount;
-
public Promise() {
m_cancellable = true;
}
@@ -76,15 +73,14 @@ namespace Implab {
public Promise(IPromise parent, bool cancellable) {
m_cancellable = cancellable;
m_parent = parent;
+ if (parent != null)
+ parent.HandleCancelled(InternalCancel);
}
- ///
- /// Событие, возникающее при отмене асинхронной операции.
- ///
- ///
- /// Как правило используется для оповещения объекта, выполняющего асинхронную операцию, о том, что ее следует отменить.
- ///
- public event EventHandler Cancelled;
+ void InternalCancel() {
+ // don't try to cancel parent :)
+ Cancel(false);
+ }
///
/// Выполняет обещание, сообщая об успешном выполнении.
@@ -101,14 +97,7 @@ namespace Implab {
m_state = PromiseState.Resolved;
}
- // state has been changed to rejected new handlers can't be added
-
- foreach (var handler in m_resultHandlers)
- InvokeHandler(handler);
-
- /* ResultHandlerInfo handler;
- while (FetchNextHandler(out handler))
- InvokeHandler(handler); */
+ OnStateChanged();
}
///
@@ -126,14 +115,7 @@ namespace Implab {
m_state = PromiseState.Rejected;
}
- // state has been changed to rejected new handlers can't be added
-
- foreach (var handler in m_resultHandlers)
- InvokeHandler(handler);
-
- /*ResultHandlerInfo handler;
- while (FetchNextHandler(out handler))
- InvokeHandler(handler);*/
+ OnStateChanged();
}
///
@@ -144,6 +126,39 @@ namespace Implab {
return Cancel(true);
}
+ protected virtual void OnStateChanged() {
+ switch (m_state) {
+ case PromiseState.Resolved:
+ foreach (var resultHandlerInfo in m_resultHandlers)
+ try {
+ if (resultHandlerInfo.resultHandler != null)
+ resultHandlerInfo.resultHandler(m_result);
+ } catch (Exception e) {
+ try {
+ if (resultHandlerInfo.errorHandler != null)
+ resultHandlerInfo.errorHandler(e);
+ } catch { }
+ }
+ break;
+ case PromiseState.Cancelled:
+ foreach (var cancelHandler in m_cancelHandlers)
+ cancelHandler();
+ break;
+ case PromiseState.Rejected:
+ foreach (var resultHandlerInfo in m_resultHandlers)
+ try {
+ if (resultHandlerInfo.errorHandler != null)
+ resultHandlerInfo.errorHandler(m_error);
+ } catch { }
+ break;
+ default:
+ throw new InvalidOperationException(String.Format("Promise entered an invalid state {0}", m_state));
+ }
+
+ m_resultHandlers = null;
+ m_cancelHandlers = null;
+ }
+
///
/// Добавляет обработчики событий выполнения обещания.
///
@@ -162,15 +177,11 @@ namespace Implab {
if (success != null)
handlerInfo.resultHandler = x => {
- try {
- success(x);
- medium.Resolve(x);
- } catch (Exception e) {
- medium.Reject(e);
- }
+ success(x);
+ medium.Resolve(x);
};
else
- handlerInfo.resultHandler = x => medium.Resolve(x);
+ handlerInfo.resultHandler = medium.Resolve;
if (error != null)
handlerInfo.errorHandler = x => {
@@ -180,7 +191,7 @@ namespace Implab {
medium.Reject(x);
};
else
- handlerInfo.errorHandler = x => medium.Reject(x);
+ handlerInfo.errorHandler = medium.Reject;
AddHandler(handlerInfo);
@@ -203,6 +214,7 @@ namespace Implab {
AddHandler(new ResultHandlerInfo {
resultHandler = x => {
+ // to avoid handler being called multiple times we handle exception by ourselfs
try {
handler();
medium.Resolve(x);
@@ -234,20 +246,15 @@ namespace Implab {
throw new ArgumentNullException("mapper");
// создаем прицепленное обещание
- Promise chained = new Promise();
+ var chained = new Promise();
AddHandler(new ResultHandlerInfo() {
- resultHandler = delegate(T result) {
- try {
- // если преобразование выдаст исключение, то сработает reject сцепленного deferred
- chained.Resolve(mapper(result));
- } catch (Exception e) {
- chained.Reject(e);
- }
- },
+ resultHandler = result => chained.Resolve(mapper(result)),
errorHandler = delegate(Exception e) {
if (error != null)
- error(e);
+ try {
+ error(e);
+ } catch { }
// в случае ошибки нужно передать исключение дальше по цепочке
chained.Reject(e);
}
@@ -276,19 +283,21 @@ namespace Implab {
// создать посредника, к которому будут подвызяваться следующие обработчики.
// когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
// передать через него результаты работы.
- Promise medium = new Promise();
+ var medium = new Promise(this, true);
- AddHandler(new ResultHandlerInfo() {
+ AddHandler(new ResultHandlerInfo {
resultHandler = delegate(T result) {
- try {
- chained(result).Then(
- x => medium.Resolve(x),
- e => medium.Reject(e)
- );
- } catch (Exception e) {
- // если сцепленное действие выдало исключение вместо обещания, то передаем ошибку по цепочке
- medium.Reject(e);
- }
+ if (medium.State == PromiseState.Cancelled)
+ return;
+
+ var promise = chained(result);
+
+ // notify chained operation that it's not needed
+ medium.Cancelled(() => promise.Cancel());
+ promise.Then(
+ medium.Resolve,
+ medium.Reject
+ );
},
errorHandler = delegate(Exception e) {
if (error != null)
@@ -305,6 +314,22 @@ namespace Implab {
return Chain(chained, null);
}
+ public Promise Cancelled(Action handler) {
+ if (handler == null)
+ return this;
+ lock (m_lock) {
+ if (m_state == PromiseState.Unresolved)
+ m_cancelHandlers.AddLast(handler);
+ else if (m_state == PromiseState.Cancelled)
+ handler();
+ }
+ return this;
+ }
+
+ public void HandleCancelled(Action handler) {
+ Cancelled(handler);
+ }
+
///
/// Дожидается отложенного обещания и в случае успеха, возвращает
/// его, результат, в противном случае бросает исключение.
@@ -327,51 +352,37 @@ namespace Implab {
/// Время ожидания
/// Результат выполнения обещания
public T Join(int timeout) {
- ManualResetEvent evt = new ManualResetEvent(false);
+ var evt = new ManualResetEvent(false);
Anyway(() => evt.Set());
+ Cancelled(() => evt.Set());
if (!evt.WaitOne(timeout, true))
throw new TimeoutException();
- if (m_error != null)
- throw new TargetInvocationException(m_error);
- else
- return m_result;
+ switch (State) {
+ case PromiseState.Resolved:
+ return m_result;
+ case PromiseState.Cancelled:
+ throw new OperationCanceledException();
+ case PromiseState.Rejected:
+ throw new TargetInvocationException(m_error);
+ default:
+ throw new ApplicationException(String.Format("Invalid promise state {0}", State));
+ }
}
public T Join() {
return Join(Timeout.Infinite);
}
- ///
- /// Данный метод последовательно извлекает обработчики обещания и когда
- /// их больше не осталось - ставит состояние "разрешено".
- ///
- /// Информация об обработчике
- /// Признак того, что еще остались обработчики в очереди
- bool FetchNextHandler(out ResultHandlerInfo handler) {
- handler = default(ResultHandlerInfo);
-
- lock (this) {
- Debug.Assert(m_state != PromiseState.Unresolved);
-
- if (m_resultHandlers.Count > 0) {
- handler = m_resultHandlers.First.Value;
- m_resultHandlers.RemoveFirst();
- return true;
- } else {
- return false;
- }
- }
- }
-
void AddHandler(ResultHandlerInfo handler) {
bool invokeRequired = false;
- lock (this) {
- if (m_state == PromiseState.Unresolved)
+ lock (m_lock) {
+ m_childrenCount++;
+ if (m_state == PromiseState.Unresolved) {
m_resultHandlers.AddLast(handler);
- else
+ } else
invokeRequired = true;
}
@@ -381,18 +392,27 @@ namespace Implab {
}
void InvokeHandler(ResultHandlerInfo handler) {
- if (m_error == null) {
- try {
- if (handler.resultHandler != null)
- handler.resultHandler(m_result);
- } catch { }
- }
-
- if (m_error != null) {
- try {
- if (handler.errorHandler != null)
- handler.errorHandler(m_error);
- } catch { }
+ switch (m_state) {
+ case PromiseState.Resolved:
+ try {
+ if (handler.resultHandler != null)
+ handler.resultHandler(m_result);
+ } catch (Exception e) {
+ try {
+ if (handler.errorHandler != null)
+ handler.errorHandler(e);
+ } catch { }
+ }
+ break;
+ case PromiseState.Rejected:
+ try {
+ if (handler.errorHandler != null)
+ handler.errorHandler(m_error);
+ } catch { }
+ break;
+ default:
+ // do nothing
+ return;
}
}
@@ -426,15 +446,11 @@ namespace Implab {
}
}
- if (dependencies && m_parent != null && m_parent.IsExclusive) {
- // TODO syncronize IsExclusive, AddHandler, Cancel (maybe CancelExclusive)
- m_parent.Cancel(true);
- }
+ if (result)
+ OnStateChanged();
- if (result) {
- // state has been changed to cancelled, new handlers can't be added
- foreach (var handler in m_cancelHandlers)
- handler();
+ if (dependencies && m_parent != null && m_parent.IsExclusive) {
+ m_parent.Cancel(true);
}
return result;
diff --git a/Implab/TaskController.cs b/Implab/TaskController.cs
--- a/Implab/TaskController.cs
+++ b/Implab/TaskController.cs
@@ -14,9 +14,8 @@ namespace Implab
///
class TaskController
{
- object m_lock;
+ readonly object m_lock;
string m_message;
- bool m_cancelled;
float m_current;
float m_max;