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);
        }
    }
}