using System; using System.Diagnostics; using System.Reflection; using Implab.Parallels; namespace Implab { public abstract class AbstractPromise : AbstractEvent, IPromise { public class HandlerDescriptor { readonly Action m_resolve; readonly Action m_reject; readonly IDeferred m_deferred; public HandlerDescriptor(Action success, Action 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); } } /// /// Выполняет обещание, сообщая об ошибке /// /// /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные /// будут проигнорированы. /// /// Исключение возникшее при выполнении операции /// Данное обещание уже выполнено 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 error) { AddHandler(new HandlerDescriptor(success, error)); } public IPromise Cast() { throw new InvalidCastException(); } public void Join() { WaitResult(-1); if (IsRejected) Rethrow(); } public void Join(int timeout) { WaitResult(timeout); } } }