Mercurial > pub > ImplabNet
view Implab/AbstractPromise.cs @ 243:b1e0ffdf3451 v3
working on promises
author | cin |
---|---|
date | Wed, 24 Jan 2018 19:24:10 +0300 |
parents | cbe10ac0731e |
children | eee3e49dd1ff |
line wrap: on
line source
using System; using System.Diagnostics; using System.Reflection; using Implab.Parallels; namespace Implab { public abstract class AbstractPromise : AbstractEvent<AbstractPromise.HandlerDescriptor>, IPromise { public class HandlerDescriptor { readonly Action m_resolve; readonly Action<Exception> m_reject; readonly IDeferred m_deferred; public HandlerDescriptor(Action success, Action<Exception> error) { m_resolve = success; m_reject = error; } public void SignalSuccess() { try { if (m_resolve != null) m_resolve(); m_deferred.Resolve(); } catch (Exception ex) { m_deferred.Reject(ex); } } public void SignalError(Exception err) { if (m_reject != null) { try { m_reject(err); m_deferred.Resolve(); } catch (Exception ex) { m_deferred.Reject(ex); } } } } PromiseState m_state; Exception m_error; public bool IsRejected { get { return m_state == PromiseState.Rejected; } } public bool IsResolved { get { return m_state == PromiseState.Resolved; } } public Exception RejectReason { get { return m_error; } } #region implemented abstract members of AbstractPromise protected override void SignalHandler(HandlerDescriptor handler) { switch (m_state) { case PromiseState.Resolved: handler.SignalSuccess(); break; case PromiseState.Rejected: handler.SignalError(RejectReason); break; default: throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", m_state)); } } protected override Signal GetFulfillSignal() { var signal = new Signal(); On(signal.Set, e => signal.Set()); return signal; } #endregion protected void CompleteResolve() { m_state = PromiseState.Resolved; CompleteTransit(); } public Type ResultType { get { return typeof(void); } } /// <summary> /// Выполняет обещание, сообщая об ошибке /// </summary> /// <remarks> /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные /// будут проигнорированы. /// </remarks> /// <param name="error">Исключение возникшее при выполнении операции</param> /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception> protected void SetError(Exception error) { if (BeginTransit()) { m_error = error; m_state = PromiseState.Rejected; CompleteTransit(); } else { WaitTransition(); if (m_state == PromiseState.Resolved) throw new InvalidOperationException("The promise is already resolved"); } } protected void Rethrow() { Debug.Assert(m_error != null); if (m_error is OperationCanceledException) throw new OperationCanceledException("Operation cancelled", m_error); else throw new TargetInvocationException(m_error); } public void On(Action success, Action<Exception> error) { AddHandler(new HandlerDescriptor(success, error)); } public IPromise<T> Cast<T>() { throw new InvalidCastException(); } public void Join() { WaitResult(-1); if (IsRejected) Rethrow(); } public void Join(int timeout) { WaitResult(timeout); } } }