view Implab/Promise`1.cs @ 261:05a87f575512 v3

Добавлена метка v3.0.6 для набора изменений 547a2fc0d93e
author cin
date Fri, 13 Apr 2018 19:15:11 +0300
parents 5cb4826c2c2a
children
line wrap: on
line source

using System;
using System.Diagnostics;
using System.Reflection;
using Implab.Parallels;

namespace Implab {
    public class Promise<T> : AbstractEvent<IResolvable<T>>, IPromise<T> {

        class ResolvableSignal : IResolvable<T> {
            public Signal Signal { get; private set; }
            public ResolvableSignal() {
                Signal = new Signal();
            }


            public void Reject(Exception error) {
                Signal.Set();
            }

            public void Resolve(T result) {
                Signal.Set();
            }
        }

        class ResolvableWrapper : IResolvable<T> {
            readonly IResolvable m_resolvable;
            public ResolvableWrapper(IResolvable resolvable) {
                m_resolvable = resolvable;
            }

            public void Reject(Exception reason) {
                m_resolvable.Reject(reason);
            }

            public void Resolve(T value) {
                m_resolvable.Resolve();
            }
        }

        PromiseState m_state;

        T m_result;

        Exception m_error;

        public bool IsRejected {
            get {
                return m_state == PromiseState.Rejected;
            }
        }

        public bool IsFulfilled {
            get {
                return m_state == PromiseState.Fulfilled;
            }
        }

        public Exception RejectReason {
            get {
                return m_error;
            }
        }


        internal void ResolvePromise(T result) {
            if (BeginTransit()) {
                m_result = result;
                m_state = PromiseState.Fulfilled;
                CompleteTransit();
            }
        }

        internal void RejectPromise(Exception reason) {
            if (BeginTransit()) {
                m_error = reason;
                m_state = PromiseState.Rejected;
                CompleteTransit();
            }
        }


        #region implemented abstract members of AbstractPromise

        protected override void SignalHandler(IResolvable<T> handler) {
            switch (m_state) {
                case PromiseState.Fulfilled:
                    handler.Resolve(m_result);
                    break;
                case PromiseState.Rejected:
                    handler.Reject(RejectReason);
                    break;
                default:
                    throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", m_state));
            }
        }

        protected void WaitResult(int timeout) {
            if (!(IsResolved || GetFulfillSignal().Wait(timeout)))
                throw new TimeoutException();
        }

        protected Signal GetFulfillSignal() {
            var next = new ResolvableSignal();
            Then(next);
            return next.Signal;
        }

        #endregion

        public Type ResultType {
            get {
                return typeof(void);
            }
        }


        protected void Rethrow() {
            if (m_error is OperationCanceledException)
                throw new OperationCanceledException("Operation cancelled", m_error);
            else
                throw new TargetInvocationException(m_error);
        }

        public void Then(IResolvable<T> next) {
            AddHandler(next);
        }

        public void Then(IResolvable next) {
            AddHandler(new ResolvableWrapper(next));
        }

        public IPromise<T2> Cast<T2>() {
            return (IPromise<T2>)this;
        }

        void IPromise.Join() {
            Join();
        }

        void IPromise.Join(int timeout) {
            Join(timeout);
        }

        public T Join() {
            WaitResult(-1);
            if (IsRejected)
                Rethrow();
            return m_result;
        }

        public T Join(int timeout) {
            WaitResult(timeout);
            if (IsRejected)
                Rethrow();
            return m_result;
        }
    }
}