Mercurial > pub > ImplabNet
view Implab/PromiseT.cs @ 136:e9e7940c7d98 v2
shared locks + tests
author | cin |
---|---|
date | Mon, 16 Feb 2015 01:14:09 +0300 |
parents | 656815cb7147 |
children | f75cfa58e3d4 |
line wrap: on
line source
using System; using System.Diagnostics; namespace Implab { /// <summary> /// Класс для асинхронного получения результатов. Так называемое "обещание". /// </summary> /// <typeparam name="T">Тип получаемого результата</typeparam> /// <remarks> /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции, /// клиент получив такое обещание может установить ряд обратных вызово для получения /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para> /// <para> /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на /// данные события клиент должен использовать методы <c>Then</c>. /// </para> /// <para> /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой), /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о /// выполнении обещания. /// </para> /// <para> /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался, /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении /// обещания. /// </para> /// <para> /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует /// использовать соответствующую форму методе <c>Then</c>. /// </para> /// <para> /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать /// только инициатор обещания иначе могут возникнуть противоречия. /// </para> /// </remarks> public class Promise<T> : AbstractPromise<IDeferred<T>>, IPromise<T>, IDeferred<T> { class StubDeferred : IDeferred<T> { public static readonly StubDeferred instance = new StubDeferred(); StubDeferred() { } #region IDeferred implementation public void Resolve(T value) { } public void Reject(Exception error) { } #endregion #region ICancellable implementation public void Cancel() { } #endregion } class RemapDescriptor<T2> : IDeferred<T> { readonly Func<T,T2> m_remap; readonly Func<Exception,T2> m_failed; readonly Func<T2> m_cancel; readonly IDeferred<T2> m_deferred; public RemapDescriptor(Func<T,T2> remap, Func<Exception,T2> failed, Func<T2> cancel, IDeferred<T2> deferred ) { Debug.Assert(deferred != null); m_remap = remap; m_failed = failed; m_cancel = cancel; m_deferred = deferred; } #region IDeferred implementation public void Resolve(T value) { if (m_remap != null) { try { m_deferred.Resolve(m_remap(value)); } catch (Exception ex) { Reject(ex); } } } public void Reject(Exception error) { if (m_failed != null) { try { m_deferred.Resolve(m_failed(error)); } catch (Exception ex) { m_deferred.Reject(ex); } } else { m_deferred.Reject(error); } } #endregion #region ICancellable implementation public void Cancel() { if (m_cancel != null) { try { m_deferred.Resolve(m_cancel()); } catch (Exception ex) { Reject(ex); } } else { m_deferred.Cancel(); } } #endregion } class ListenerDescriptor : IDeferred<T> { readonly Action m_handler; readonly PromiseEventType m_events; public ListenerDescriptor(Action handler, PromiseEventType events) { Debug.Assert(handler != null); m_handler = handler; m_events = events; } #region IDeferred implementation public void Resolve(T value) { if (m_events.HasFlag(PromiseEventType.Success)) { try { m_handler(); // Analysis disable once EmptyGeneralCatchClause } catch { } } } public void Reject(Exception error) { if (m_events.HasFlag(PromiseEventType.Error)){ try { m_handler(); // Analysis disable once EmptyGeneralCatchClause } catch { } } } #endregion #region ICancellable implementation public void Cancel() { if (m_events.HasFlag(PromiseEventType.Cancelled)){ try { m_handler(); // Analysis disable once EmptyGeneralCatchClause } catch { } } } #endregion } class ValueEventDescriptor : IDeferred<T> { readonly Action<T> m_success; readonly Action<Exception> m_failed; readonly Action m_cancelled; readonly IDeferred<T> m_deferred; public ValueEventDescriptor(Action<T> success, Action<Exception> failed, Action cancelled, IDeferred<T> deferred) { Debug.Assert(deferred != null); m_success = success; m_failed = failed; m_cancelled = cancelled; m_deferred = deferred; } #region IDeferred implementation public void Resolve(T value) { if (m_success != null) { try { m_success(value); m_deferred.Resolve(value); } catch (Exception ex) { Reject(ex); } } } public void Reject(Exception error) { if (m_failed != null) { try { m_failed(error); m_deferred.Resolve(default(T)); } catch(Exception ex) { m_deferred.Reject(ex); } } else { m_deferred.Reject(error); } } #endregion #region ICancellable implementation public void Cancel() { if (m_cancelled != null) { try { m_cancelled(); m_deferred.Resolve(default(T)); } catch(Exception ex) { Reject(ex); } } else { m_deferred.Cancel(); } } #endregion } public class EventDescriptor : IDeferred<T> { readonly Action m_success; readonly Action<Exception> m_failed; readonly Action m_cancelled; readonly IDeferred<T> m_deferred; public EventDescriptor(Action success, Action<Exception> failed, Action cancelled, IDeferred<T> deferred) { Debug.Assert(deferred != null); m_success = success; m_failed = failed; m_cancelled = cancelled; m_deferred = deferred; } #region IDeferred implementation public void Resolve(T value) { if (m_success != null) { try { m_success(); m_deferred.Resolve(value); } catch (Exception ex) { Reject(ex); } } } public void Reject(Exception error) { if (m_failed != null) { try { m_failed(error); m_deferred.Resolve(default(T)); }catch (Exception ex) { m_deferred.Reject(ex); } } else { m_deferred.Reject(error); } } #endregion #region ICancellable implementation public void Cancel() { if (m_cancelled != null) { try { m_cancelled(); m_deferred.Resolve(default(T)); } catch (Exception ex) { Reject(ex); } } else { m_deferred.Cancel(); } } #endregion } T m_result; public virtual void Resolve(T value) { if (BeginSetResult()) { m_result = value; EndSetResult(); } } public void Reject(Exception error) { SetError(error); } public Type PromiseType { get { return typeof(T); } } public new T Join() { WaitResult(-1); return m_result; } public new T Join(int timeout) { WaitResult(timeout); return m_result; } public IPromise<T> On(Action<T> success, Action<Exception> error, Action cancel) { AddHandler(new ValueEventDescriptor(success, error, cancel, StubDeferred.instance)); return this; } public IPromise<T> On(Action<T> success, Action<Exception> error) { AddHandler(new ValueEventDescriptor(success, error, null, StubDeferred.instance)); return this; } public IPromise<T> On(Action<T> success) { AddHandler(new ValueEventDescriptor(success, null, null, StubDeferred.instance)); return this; } public IPromise<T> On(Action handler, PromiseEventType events) { Listen(events, handler); return this; } public IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception, T2> error, Func<T2> cancel) { var promise = new Promise<T2>(); if (mapper != null) promise.On(Cancel, PromiseEventType.Cancelled); AddHandler(new RemapDescriptor<T2>(mapper, error, cancel, promise)); return promise; } public IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception, T2> error) { var promise = new Promise<T2>(); if (mapper != null) promise.On(Cancel, PromiseEventType.Cancelled); AddHandler(new RemapDescriptor<T2>(mapper, error, null, promise)); return promise; } public IPromise<T2> Then<T2>(Func<T, T2> mapper) { var promise = new Promise<T2>(); if (mapper != null) promise.On(Cancel, PromiseEventType.Cancelled); AddHandler(new RemapDescriptor<T2>(mapper, null, null, promise)); return promise; } public IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception, IPromise<T2>> error, Func<IPromise<T2>> cancel) { // this promise will be resolved when an asyc operation is started var promise = new Promise<IPromise<T2>>(); AddHandler(new RemapDescriptor<IPromise<T2>>( chained, error, cancel, promise )); var medium = new Promise<T2>(); if (chained != null) medium.On(Cancel, PromiseEventType.Cancelled); // we need to connect started async operation with the medium // if the async operation hasn't been started by the some reason // report is to the medium promise.On( result => ConnectPromise<T2>(result, medium), medium.Reject, medium.Cancel ); return medium; } static void ConnectPromise<T2>(IPromise<T2> result, Promise<T2> medium) { if (result != null) { result.On( medium.Resolve, medium.Reject, () => medium.Reject(new OperationCanceledException()) ); medium.On(result.Cancel, PromiseEventType.Cancelled); } else { medium.Reject( new NullReferenceException( "The chained asynchronous operation returned" + " 'null' where the promise instance is expected" ) ); } } public IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception, IPromise<T2>> error) { return Chain(chained, error, null); } public IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained) { return Chain(chained, null, null); } public IPromise<T2> Error<T2>(Func<Exception, T2> error) { var promise = new Promise<T2>(); if (error != null) On( (Action<T>)null, ex => { try { promise.Resolve(error(ex)); } catch (Exception ex2) { promise.Reject(ex2); } } ); else Listen(PromiseEventType.Error, () => promise.Resolve(default(T2))); return promise; } public IPromise<T2> Cancelled<T2>(Func<T2> handler) { var promise = new Promise<T2>(); if (handler != null) On( (Action<T>)null, null, () => { try { promise.Resolve(handler()); } catch (Exception ex) { promise.Reject(ex); } }); else Listen(PromiseEventType.Cancelled, () => promise.Resolve(default(T2))); return promise; } public IPromise Then(Action success, Action<Exception> error, Action cancel) { var promise = new Promise<T>(); if (success != null) promise.On(Cancel, PromiseEventType.Cancelled); AddHandler(new EventDescriptor(success, error, cancel, promise)); return promise; } public IPromise Then(Action success, Action<Exception> error) { return Then(success, error, null); } public IPromise Then(Action success) { return Then(success, null, null); } public IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error, Func<IPromise> cancel) { var promise = new Promise<IPromise>(); AddHandler( new RemapDescriptor<IPromise>( x => chained(), error, cancel, promise ) ); var medium = new Promise(); if (chained != null) medium.On(Cancel, PromiseEventType.Cancelled); promise.On( result => ConnectPromise(result, medium), medium.Reject, medium.Cancel ); return medium; } static void ConnectPromise(IPromise result, Promise medium) { if (result != null) { result.On( medium.Resolve, medium.Reject, () => medium.Reject(new OperationCanceledException()) ); medium.On(result.Cancel, PromiseEventType.Cancelled); } else { medium.Reject( new NullReferenceException( "The chained asynchronous operation returned" + " 'null' where the promise instance is expected" ) ); } } public IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error) { return Chain(chained, error, null); } public IPromise Chain(Func<IPromise> chained) { return Chain(chained, null, null); } public IPromise On(Action success, Action<Exception> error, Action cancel) { AddHandler(new EventDescriptor(success,error,cancel, StubDeferred.instance)); return this; } public IPromise On(Action success, Action<Exception> error) { AddHandler(new EventDescriptor(success, error, null, StubDeferred.instance)); return this; } public IPromise On(Action success) { Listen(PromiseEventType.Success, success); return this; } IPromise IPromise.On(Action handler, PromiseEventType events) { Listen(events,handler); return this; } public IPromise Error(Action<Exception> error) { var promise = new Promise(); if (error != null) On( (Action<T>)null, ex => { try { error(ex); promise.Resolve(); } catch (Exception ex2) { promise.Reject(ex2); } }); else Listen(PromiseEventType.Error, promise.Resolve); return promise; } public IPromise Cancelled(Action handler) { var promise = new Promise(); if (handler != null) On( (Action<T>)null, null, () => { try { handler(); promise.Resolve(); } catch (Exception ex) { promise.Reject(ex); } }); else Listen(PromiseEventType.Cancelled, promise.Resolve); return promise; } public IPromise<T2> Cast<T2>() { return (IPromise<T2>)this; } #region implemented abstract members of AbstractPromise protected override void SignalSuccess(IDeferred<T> handler) { handler.Resolve(m_result); } protected override void SignalError(IDeferred<T> handler, Exception error) { handler.Reject(error); } protected override void SignalCancelled(IDeferred<T> handler) { handler.Cancel(); } protected override void Listen(PromiseEventType events, Action handler) { if (handler != null) AddHandler(new ListenerDescriptor(handler, events)); } #endregion public static IPromise<T> ResultToPromise(T value) { var p = new Promise<T>(); p.Resolve(value); return p; } public static IPromise<T> ExceptionToPromise(Exception error) { var p = new Promise<T>(); p.Reject(error); return p; } } }