# HG changeset patch # User cin # Date 2014-11-10 15:00:28 # Node ID d4e38929ce36ce64cc0273cbc689fb35390bfbf8 # Parent 4d308952fd5ed59a8bec6d8fdf90caadf2d5cfdc promises refactoring diff --git a/Implab.Fx/AnimationHelpers.cs b/Implab.Fx/AnimationHelpers.cs --- a/Implab.Fx/AnimationHelpers.cs +++ b/Implab.Fx/AnimationHelpers.cs @@ -42,7 +42,13 @@ namespace Implab.Fx { var anim = ctl.AnimateTransparency(0); - return anim.Play().DispatchToControl(ctl).Then(frm => frm.Close()); + return anim + .Play() + .DispatchToControl(ctl) + .Then(frm => { + frm.Close(); + return frm; + }); } public static IPromise OverlayFadeIn(this Form that, T overlay) where T : Form diff --git a/Implab.Fx/ControlBoundPromise.cs b/Implab.Fx/ControlBoundPromise.cs --- a/Implab.Fx/ControlBoundPromise.cs +++ b/Implab.Fx/ControlBoundPromise.cs @@ -19,9 +19,9 @@ namespace Implab.Fx { m_target = target; } - protected override void InvokeHandler(HandlerDescriptor handler) { + protected override void InvokeHandler(AbstractHandler handler) { if (m_target.InvokeRequired) - m_target.BeginInvoke(new Action(base.InvokeHandler), handler); + m_target.BeginInvoke(new Action(base.InvokeHandler), handler); else base.InvokeHandler(handler); } diff --git a/Implab.Test/AsyncTests.cs b/Implab.Test/AsyncTests.cs --- a/Implab.Test/AsyncTests.cs +++ b/Implab.Test/AsyncTests.cs @@ -426,8 +426,11 @@ namespace Implab.Test { hemStarted.Set(); // запускаем две асинхронные операции var result = PromiseHelper - .Sleep(10000, "HEM ENABLED!!!") - .Then(s => pSurvive.Resolve(false)); + .Sleep(100000000, "HEM ENABLED!!!") + .Then(s => { + pSurvive.Resolve(false); + return s; + }); result .Cancelled(() => pSurvive.Resolve(true)); diff --git a/Implab/IPromiseT.cs b/Implab/IPromiseT.cs --- a/Implab/IPromiseT.cs +++ b/Implab/IPromiseT.cs @@ -13,12 +13,6 @@ namespace Implab { void On(Action success); - IPromise Then(Action success, Func error, Action cancel); - - IPromise Then(Action success, Func error); - - IPromise Then(Action success); - IPromise Then(Func mapper, Func error, Action cancel); IPromise Then(Func mapper, Func error); diff --git a/Implab/Implab.csproj b/Implab/Implab.csproj --- a/Implab/Implab.csproj +++ b/Implab/Implab.csproj @@ -148,6 +148,8 @@ + + diff --git a/Implab/Parallels/MTCustomQueue.cs b/Implab/Parallels/MTCustomQueue.cs new file mode 100644 --- /dev/null +++ b/Implab/Parallels/MTCustomQueue.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Collections; + +namespace Implab.Parallels { + public class MTCustomQueue : IEnumerable where TNode : MTCustomQueueNode { + TNode m_first; + TNode m_last; + + public void Enqueue(TNode next) { + Thread.MemoryBarrier(); + + var last = m_last; + + // Interlocaked.CompareExchange implies Thread.MemoryBarrier(); + // to ensure that the next node is completely constructed + while (last != Interlocked.CompareExchange(ref m_last, next, last)) + last = m_last; + + if (last != null) + last.next = next; + else + m_first = next; + } + + public bool TryDequeue(out TNode node) { + TNode first; + TNode next; + node = null; + + Thread.MemoryBarrier(); + do { + first = m_first; + if (first == null) + return false; + next = first.next; + if (next == null) { + // this is the last element, + // then try to update the tail + if (first != Interlocked.CompareExchange(ref m_last, null, first)) { + // this is the race condition + if (m_last == null) + // the queue is empty + return false; + // tail has been changed, we need to restart + continue; + } + + // tail succesfully updated and first.next will never be changed + // other readers will fail due to inconsistency m_last != m_fist && m_first.next == null + // however the parallel writer may update the m_first since the m_last is null + + // so we need to fix inconsistency by setting m_first to null or if it has been + // updated by the writer already then we should just to give up + Interlocked.CompareExchange(ref m_first, null, first); + break; + + } + if (first == Interlocked.CompareExchange(ref m_first, next, first)) + // head succesfully updated + break; + } while (true); + + node = first; + return true; + } + + #region IEnumerable implementation + + class Enumerator : IEnumerator { + TNode m_current; + TNode m_first; + + public Enumerator(TNode first) { + m_first = first; + } + + #region IEnumerator implementation + + public bool MoveNext() { + m_current = m_current == null ? m_first : m_current.next; + return m_current != null; + } + + public void Reset() { + m_current = null; + } + + object IEnumerator.Current { + get { + if (m_current == null) + throw new InvalidOperationException(); + return m_current; + } + } + + #endregion + + #region IDisposable implementation + + public void Dispose() { + } + + #endregion + + #region IEnumerator implementation + + public TNode Current { + get { + if (m_current == null) + throw new InvalidOperationException(); + return m_current; + } + } + + #endregion + } + + public IEnumerator GetEnumerator() { + return new Enumerator(m_first); + } + + #endregion + + #region IEnumerable implementation + + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + #endregion + } +} + diff --git a/Implab/Parallels/MTCustomQueueNode.cs b/Implab/Parallels/MTCustomQueueNode.cs new file mode 100644 --- /dev/null +++ b/Implab/Parallels/MTCustomQueueNode.cs @@ -0,0 +1,6 @@ +namespace Implab.Parallels { + public class MTCustomQueueNode where TNode : MTCustomQueueNode { + public TNode next; + } +} + diff --git a/Implab/Promise.cs b/Implab/Promise.cs --- a/Implab/Promise.cs +++ b/Implab/Promise.cs @@ -42,53 +42,68 @@ namespace Implab { /// public class Promise : IPromise { - protected struct HandlerDescriptor { - public Action resultHandler; - public Func errorHandler; - public Action cancellHandler; - public Promise medium; + protected abstract class AbstractHandler : MTCustomQueueNode { + public abstract void Resolve(T result); + public abstract void Reject(Exception error); + public abstract void Cancel(); + } + + protected class HandlerDescriptor : AbstractHandler { + + readonly Func m_resultHandler; + readonly Func m_errorHandler; + readonly Action m_cancellHandler; + readonly Promise m_medium; - public void Resolve(T result) { - if (resultHandler != null) { + public HandlerDescriptor(Func resultHandler, Func errorHandler, Action cancelHandler, Promise medium) { + m_resultHandler = resultHandler; + m_errorHandler = errorHandler; + m_cancellHandler = cancelHandler; + m_medium = medium; + } + + public override void Resolve(T result) { + if (m_resultHandler != null) { try { - resultHandler(result); + if (m_medium != null) + m_medium.Resolve(m_resultHandler(result)); + else + m_resultHandler(result); } catch (Exception e) { Reject(e); - return; } - } - if (medium != null) - medium.Resolve(result); + } else if(m_medium != null) + m_medium.Resolve(default(T2)); } - public void Reject(Exception err) { - if (errorHandler != null) { + public override void Reject(Exception error) { + if (m_errorHandler != null) { try { - var res = errorHandler(err); - if (medium != null) - medium.Resolve(res); + var res = m_errorHandler(error); + if (m_medium != null) + m_medium.Resolve(res); /*} catch (TransientPromiseException err2) { if (medium != null) medium.Reject(err2.InnerException);*/ } catch (Exception err2) { - if (medium != null) - medium.Reject(err2); + if (m_medium != null) + m_medium.Reject(err2); } - } else if (medium != null) - medium.Reject(err); + } else if (m_medium != null) + m_medium.Reject(error); } - public void Cancel() { - if (cancellHandler != null) { + public override void Cancel() { + if (m_cancellHandler != null) { try { - cancellHandler(); + m_cancellHandler(); } catch (Exception err) { Reject(err); return; } } - if (medium != null) - medium.Cancel(); + if (m_medium != null) + m_medium.Cancel(); } } @@ -103,14 +118,15 @@ namespace Implab { T m_result; Exception m_error; - readonly MTQueue m_handlers = new MTQueue(); + readonly MTCustomQueue m_handlers = new MTCustomQueue(); + //readonly MTQueue m_handlers = new MTQueue(); public Promise() { } public Promise(IPromise parent) { if (parent != null) - AddHandler( + AddHandler( null, null, () => { @@ -215,49 +231,6 @@ namespace Implab { } } - public IPromise Then(Action success, Func error, Action cancel) { - if (success == null && error == null && cancel == null) - return this; - - var medium = new Promise(this); - - AddHandler(success, error, cancel, medium, true); - - return medium; - } - - /// - /// Adds new handlers to this promise. - /// - /// The handler of the successfully completed operation. - /// This handler will recieve an operation result as a parameter. - /// Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation. - /// The new promise chained to this one. - public IPromise Then(Action success, Func error) { - if (success == null && error == null) - return this; - - var medium = new Promise(this); - - AddHandler(success, error, null, medium, true); - - return medium; - } - - - - - public IPromise Then(Action success) { - if (success == null) - return this; - - var medium = new Promise(this); - - AddHandler(success, null, null, medium, true); - - return medium; - } - /// /// Последний обработчик в цепочки обещаний. /// @@ -279,13 +252,19 @@ namespace Implab { if (success == null && error == null && cancel == null) return; - Func errorHandler = null; - if (error != null) - errorHandler = err => { - error(err); + AddHandler( + success != null ? new Func(x => { + success(x); + return x; + }) : null, + error != null ? new Func(e => { + error(e); return default(T); - }; - AddHandler(success, errorHandler, cancel, null, false); + }) : null, + cancel, + null, + false + ); } public void On(Action success, Action error) { @@ -299,7 +278,10 @@ namespace Implab { public void On(Action handler, PromiseEventType events) { Safe.ArgumentNotNull(handler, "handler"); - Action success = events.HasFlag(PromiseEventType.Success) ? new Action(x => handler()) : null; + Func success = events.HasFlag(PromiseEventType.Success) ? new Func(x => { + handler(); + return x; + }) : null; Func error = events.HasFlag(PromiseEventType.Error) ? new Func(e => { handler(); return default(T); @@ -363,39 +345,11 @@ namespace Implab { // создаем прицепленное обещание var medium = new Promise(this); - Action resultHandler = result => medium.Resolve(mapper(result)); - Func errorHandler; - if (error != null) - errorHandler = e => { - try { - medium.Resolve(error(e)); - } catch (Exception e2) { - // в случае ошибки нужно передать исключение дальше по цепочке - medium.Reject(e2); - } - return default(T); - }; - else - errorHandler = e => { - medium.Reject(e); - return default(T); - }; - - Action cancelHandler; - if (cancel != null) - cancelHandler = () => { - cancel(); - medium.Cancel(); - }; - else - cancelHandler = medium.Cancel; - - AddHandler( - resultHandler, - errorHandler, - cancelHandler, - null, + mapper, + error, + cancel, + medium, true ); @@ -431,9 +385,9 @@ namespace Implab { // передать через него результаты работы. var medium = new Promise(this); - Action resultHandler = delegate(T result) { + Func resultHandler = delegate(T result) { if (medium.IsCancelled) - return; + return default(T); var promise = chained(result); @@ -454,6 +408,8 @@ namespace Implab { promise.Cancel(); } ); + + return default(T); }; Func errorHandler; @@ -534,7 +490,10 @@ namespace Implab { var medium = new Promise(this); AddHandler( - x => handler(), + x => { + handler(); + return x; + }, e => { handler(); throw new TransientPromiseException(e); @@ -600,16 +559,11 @@ namespace Implab { return Join(Timeout.Infinite); } - void AddHandler(Action success, Func error, Action cancel, Promise medium, bool inc) { + void AddHandler(Func success, Func error, Action cancel, Promise medium, bool inc) { if (inc) Interlocked.Increment(ref m_childrenCount); - var handler = new HandlerDescriptor { - resultHandler = success, - errorHandler = error, - cancellHandler = cancel, - medium = medium - }; + AbstractHandler handler = new HandlerDescriptor(success, error, cancel, medium); bool queued; @@ -631,7 +585,7 @@ namespace Implab { InvokeHandler(handler); } - protected virtual void InvokeHandler(HandlerDescriptor handler) { + protected virtual void InvokeHandler(AbstractHandler handler) { switch (m_state) { case SUCCEEDED_STATE: handler.Resolve(m_result); @@ -649,7 +603,7 @@ namespace Implab { } void OnStateChanged() { - HandlerDescriptor handler; + AbstractHandler handler; while (m_handlers.TryDequeue(out handler)) InvokeHandler(handler); } @@ -688,16 +642,13 @@ namespace Implab { var dest = i; if (promises[i] != null) { - promises[i].Then( + promises[i].On( x => { result[dest] = x; if (Interlocked.Decrement(ref pending) == 0) promise.Resolve(result); }, - e => { - promise.Reject(e); - return default(T); - } + promise.Reject ); } else { if (Interlocked.Decrement(ref pending) == 0) @@ -776,7 +727,10 @@ namespace Implab { IPromise IPromise.Then(Action success, Action error, Action cancel) { return Then( - success != null ? new Action(x => success()) : null, + success != null ? new Func(x => { + success(); + return x; + }) : null, error != null ? new Func(e => { error(e); return default(T); @@ -787,7 +741,10 @@ namespace Implab { IPromise IPromise.Then(Action success, Action error) { return Then( - success != null ? new Action(x => success()) : null, + success != null ? new Func(x => { + success(); + return x; + }) : null, error != null ? new Func(e => { error(e); return default(T); @@ -797,7 +754,10 @@ namespace Implab { IPromise IPromise.Then(Action success) { Safe.ArgumentNotNull(success, "success"); - return Then(x => success()); + return Then(x => { + success(); + return x; + }); } IPromise IPromise.Chain(Func chained, Func error, Action cancel) { @@ -809,9 +769,9 @@ namespace Implab { var medium = new Promise(this); - Action resultHandler = delegate { + Func resultHandler = delegate { if (medium.IsCancelled) - return; + return default(T); var promise = chained(); @@ -828,6 +788,8 @@ namespace Implab { if (promise.IsExclusive) promise.Cancel(); }); + + return default(T); }; Func errorHandler; diff --git a/Implab/SyncContextPromise.cs b/Implab/SyncContextPromise.cs --- a/Implab/SyncContextPromise.cs +++ b/Implab/SyncContextPromise.cs @@ -14,7 +14,7 @@ namespace Implab { Safe.ArgumentNotNull(context, "context"); m_context = context; } - protected override void InvokeHandler(HandlerDescriptor handler) { + protected override void InvokeHandler(AbstractHandler handler) { m_context.Post(x => base.InvokeHandler(handler),null); } }