# HG changeset patch # User cin # Date 2014-04-09 22:39:29 # Node ID b255e4aeef1757bf4f600c61579fef621f0887c5 # Parent 8eca2652d2ff9617fb00fa7b9c78f4038ade95cc removed the reference to the parent from the promise object this allows resolved promises to release parents and results they are holding. Added complete set of operations to IPromiseBase interface Subscribing to the cancellation event of the promise should not affect it's IsExclusive property More tests. diff --git a/Implab.Test/AsyncTests.cs b/Implab.Test/AsyncTests.cs --- a/Implab.Test/AsyncTests.cs +++ b/Implab.Test/AsyncTests.cs @@ -328,6 +328,59 @@ namespace Implab.Test { Assert.IsTrue(flags[1]); Assert.IsTrue(flags[2]); } + + [TestMethod] + public void ChainedCancel1Test() { + // при отмене сцепленной асинхронной операции все обещание должно + // завершаться ошибкой OperationCanceledException + var p = PromiseHelper + .Sleep(1, "Hi, HAL!") + .Chain(x => { + // запускаем две асинхронные операции + var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!"); + // вторая операция отменяет первую до завершения + PromiseHelper + .Sleep(100, "HAL, STOP!") + .Then(() => result.Cancel()); + return result; + }); + try { + p.Join(); + } catch (TargetInvocationException err) { + Assert.IsTrue(err.InnerException is OperationCanceledException); + } + } + + [TestMethod] + public void ChainedCancel2Test() { + // при отмене цепочки обещаний, вложенные операции также должны отменяться + IPromiseBase p = null; + var pSurvive = new Promise(); + var hemStarted = new ManualResetEvent(false); + p = PromiseHelper + .Sleep(1, "Hi, HAL!") + .Chain(x => { + hemStarted.Set(); + // запускаем две асинхронные операции + var result = PromiseHelper + .Sleep(1000, "HEM ENABLED!!!") + .Then(s => pSurvive.Resolve(false)); + + result + .Cancelled(() => pSurvive.Resolve(true)); + + return result; + }); + + hemStarted.WaitOne(); + p.Cancel(); + + try { + p.Join(); + } catch (OperationCanceledException) { + Assert.IsTrue(pSurvive.Join()); + } + } } } diff --git a/Implab.suo b/Implab.suo index 8db46086f47396d82a71e4463e1728188d310d43..7d0dced8f9843cdcbf2e228d319a32e165d49e3c GIT binary patch literal 71168 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 diff --git a/Implab/IPromise.cs b/Implab/IPromise.cs --- a/Implab/IPromise.cs +++ b/Implab/IPromise.cs @@ -8,14 +8,13 @@ namespace Implab public interface IPromise: IPromiseBase { - T Join(); - - T Join(int timeout); + new T Join(); + new T Join(int timeout); IPromise Then(ResultHandler success, ErrorHandler error); IPromise Then(ResultHandler success, ErrorHandler error); IPromise Then(ResultHandler success); - IPromise Error(ErrorHandler error); + new IPromise Error(ErrorHandler error); IPromise Error(ErrorHandler error); IPromise Map(ResultMapper mapper, ErrorHandler error); @@ -24,9 +23,9 @@ namespace Implab IPromise Chain(ChainedOperation chained, ErrorHandler error); IPromise Chain(ChainedOperation chained); - IPromise Cancelled(Action handler); - IPromise Finally(Action handler); - IPromise Anyway(Action handler); + new IPromise Cancelled(Action handler); + new IPromise Finally(Action handler); + new IPromise Anyway(Action handler); } } diff --git a/Implab/IPromiseBase.cs b/Implab/IPromiseBase.cs --- a/Implab/IPromiseBase.cs +++ b/Implab/IPromiseBase.cs @@ -23,8 +23,15 @@ namespace Implab { IPromiseBase Then(Action success,ErrorHandler error); IPromiseBase Then(Action success); + IPromiseBase Error(ErrorHandler error); + IPromiseBase Anyway(Action handler); + IPromiseBase Finally(Action handler); + IPromiseBase Cancelled(Action handler); IPromise Cast(); + void Join(); + void Join(int timeout); + } } diff --git a/Implab/Promise.cs b/Implab/Promise.cs --- a/Implab/Promise.cs +++ b/Implab/Promise.cs @@ -11,7 +11,7 @@ namespace Implab { public delegate T ErrorHandler(Exception e); public delegate void ResultHandler(T result); public delegate TNew ResultMapper(TSrc result); - public delegate IPromise ChainedOperation(TSrc result); + public delegate IPromise ChainedOperation(TSrc result); /// /// Класс для асинхронного получения результатов. Так называемое "обещание". @@ -86,7 +86,6 @@ namespace Implab { const int RejectedState = 3; const int CancelledState = 4; - readonly IPromiseBase m_parent; readonly bool m_cancellable; int m_childrenCount = 0; @@ -102,12 +101,15 @@ namespace Implab { public Promise(IPromiseBase parent, bool cancellable) { m_cancellable = cancellable; - m_parent = parent; - } - - void InternalCancel() { - // don't try to cancel parent :) - Cancel(false); + if (parent != null) + AddHandler( + null, + null, + () => { + if (parent.IsExclusive) + parent.Cancel(); + } + ); } bool BeginTransit() { @@ -159,6 +161,16 @@ namespace Implab { } /// + /// Выполняет обещание, сообщая РѕР± успешном выполнении. Результатом выполнения будет пустое значения. + /// + /// + /// Данный вариант удобен РІ случаях, РєРѕРіРґР° интересен факт выполнения операции, нежели полученное значение. + /// + public void Resolve() { + Resolve(default(T)); + } + + /// /// Выполняет обещание, сообщая РѕР± ошибке /// /// @@ -185,7 +197,18 @@ namespace Implab { /// /// true Операция была отменена, обработчики РЅРµ Р±СѓРґСѓС‚ вызваны.false отмена РЅРµ возможна, поскольку обещание уже выполнено Рё обработчики отработали. public bool Cancel() { - return Cancel(true); + if (BeginTransit()) { + CompleteTransit(CancelledState); + OnStateChanged(); + return true; + } else { + return false; + } + } + + // сделано для возвращаемого типа void + protected void InternalCancel() { + Cancel(); } /// @@ -229,13 +252,11 @@ namespace Implab { return medium; } - public IPromiseBase Then(Action success,ErrorHandler error) - { + public IPromiseBase Then(Action success, ErrorHandler error) { return Then(x => success(), error); } - public IPromiseBase Then(Action success) - { + public IPromiseBase Then(Action success) { return Then(x => success()); } @@ -267,7 +288,7 @@ namespace Implab { errorHandler = x => { try { medium.Resolve(error(x)); - } catch(Exception e) { + } catch (Exception e) { medium.Reject(e); } }; @@ -338,7 +359,7 @@ namespace Implab { if (handler == null) return this; - var medium = new Promise(); + var medium = new Promise(this,true); AddHandler( x => { @@ -377,7 +398,7 @@ namespace Implab { throw new ArgumentNullException("mapper"); // создаем прицепленное обещание - var chained = new Promise(); + var chained = new Promise(this,true); ResultHandler resultHandler = result => chained.Resolve(mapper(result)); ErrorHandler errorHandler = delegate(Exception e) { @@ -427,12 +448,21 @@ namespace Implab { var promise = chained(result); - // notify chained operation that it's not needed - medium.Cancelled(() => promise.Cancel()); promise.Then( x => medium.Resolve(x), e => medium.Reject(e) ); + + // notify chained operation that it's not needed anymore + // РїРѕСЂСЏРґРѕРє вызова Then, Cancelled важен, поскольку РѕС‚ этого + // зависит IsExclusive + medium.Cancelled(() => { + if(promise.IsExclusive) + promise.Cancel(); + }); + + // внешняя отмена связанной операции рассматривается как ошибка + promise.Cancelled(() => medium.Reject(new OperationCanceledException())); }; ErrorHandler errorHandler = delegate(Exception e) { @@ -531,7 +561,8 @@ namespace Implab { } void AddHandler(ResultHandler success, ErrorHandler error, Action cancel) { - Interlocked.Increment(ref m_childrenCount); + if (success != null || error != null) + Interlocked.Increment(ref m_childrenCount); HandlerDescriptor handler = new HandlerDescriptor { resultHandler = success, @@ -588,20 +619,6 @@ namespace Implab { } } - protected bool Cancel(bool dependencies) { - if (BeginTransit()) { - CompleteTransit(CancelledState); - OnStateChanged(); - - if (dependencies && m_parent != null && m_parent.IsExclusive) - m_parent.Cancel(); - - return true; - } else { - return false; - } - } - /// /// Объединяет несколько обещаний РІ РѕРґРЅРѕ, результатом которого является массив результатов РґСЂСѓРіРёС… обещаний. /// Если хотябы РѕРґРЅРѕ РёР· переданных обещаний РЅРµ будет выполнено, то РЅРѕРІРѕРµ обещение тоже РЅРµ будет выполнено. @@ -629,20 +646,25 @@ namespace Implab { for (int i = 0; i < promises.Count; i++) { var dest = i; - promises[i].Then( - x => { - result[dest] = x; - if(Interlocked.Decrement(ref pending) == 0) - promise.Resolve(result); - }, - e => promise.Reject(e) - ); + if (promises[i] != null) { + promises[i].Then( + x => { + result[dest] = x; + if (Interlocked.Decrement(ref pending) == 0) + promise.Resolve(result); + }, + e => promise.Reject(e) + ); + } else { + if (Interlocked.Decrement(ref pending) == 0) + promise.Resolve(result); + } } promise.Cancelled( () => { - foreach(var d in promises) - if(d.IsExclusive) + foreach (var d in promises) + if (d != null && d.IsExclusive) d.Cancel(); } ); @@ -650,6 +672,47 @@ namespace Implab { return promise; } + /// + /// Объединяет несколько обещаний РІ РѕРґРЅРѕ. Результирующее обещание будет выполнено РїСЂРё + /// выполнении всех указанных обещаний. РџСЂРё этом возвращаемые значения первичных обещаний + /// игнорируются. + /// + /// Коллекция первичных обещаний, которые Р±СѓРґСѓС‚ объеденены РІ РѕРґРЅРѕ. + /// РќРѕРІРѕРµ обещание, объединяющее РІ себе переданные. + /// + /// Если РІ коллекции встречаюься null, то РѕРЅРё воспринимаются как выполненные обещания. + /// + public static IPromiseBase CreateComposite(ICollection promises) { + if (promises == null) + throw new ArgumentNullException(); + if (promises.Count == 0) + return Promise.ResultToPromise(null); + + int countdown = promises.Count; + + var result = new Promise(); + + foreach (var d in promises) { + if (d == null) { + if (Interlocked.Decrement(ref countdown) == 0) + result.Resolve(null); + } else { + d.Then(() => { + if (Interlocked.Decrement(ref countdown) == 0) + result.Resolve(null); + }); + } + } + + result.Cancelled(() => { + foreach (var d in promises) + if (d != null && d.IsExclusive) + d.Cancel(); + }); + + return result; + } + public static Promise ResultToPromise(T result) { var p = new Promise(); p.Resolve(result); @@ -665,5 +728,35 @@ namespace Implab { return p; } + #region IPromiseBase explicit implementation + + IPromiseBase IPromiseBase.Error(ErrorHandler error) { + return Error(error); + } + + IPromiseBase IPromiseBase.Anyway(Action handler) { + return Anyway(handler); + } + + IPromiseBase IPromiseBase.Finally(Action handler) { + return Finally(handler); + } + + IPromiseBase IPromiseBase.Cancelled(Action handler) { + return Cancelled(handler); + } + + void IPromiseBase.Join() { + Join(); + } + + void IPromiseBase.Join(int timeout) { + Join(timeout); + } + + #endregion + + + } }