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