# HG changeset patch
# User cin
# Date 2018-01-24 16:24:10
# Node ID b1e0ffdf34514bbebd7c80df2ca39256fb63670c
# Parent cbe10ac0731e4edb4d43386f1a3fc3454f6f925c
working on promises
diff --git a/Implab/AbstractEvent.cs b/Implab/AbstractEvent.cs
--- a/Implab/AbstractEvent.cs
+++ b/Implab/AbstractEvent.cs
@@ -5,28 +5,61 @@ using System.Reflection;
using System.Diagnostics;
namespace Implab {
+ ///
+ /// Abstract class for creation of custom one-shot thread safe events.
+ ///
+ ///
+ ///
+ /// An event is something that should happen in the future and the
+ /// triggering of the event causes execution of some pending actions
+ /// which are formely event handlers. One-shot events occur only once
+ /// and any handler added after the event is triggered should run
+ /// without a delay.
+ ///
+ ///
+ /// The lifecycle of the one-shot event is tipically consists of following
+ /// phases.
+ ///
+ /// Pending state. This is the initial state of the event. Any
+ /// handler added to the event will be queued for the future execution.
+ ///
+ /// Transitional state. This is intermediate state between pending
+ /// and fulfilled states, during this state internal initialization and storing
+ /// of the result occurs.
+ ///
+ /// Fulfilled state. The event contains the result, all queued
+ /// handlers are signalled to run and newly added handlers are executed
+ /// immediatelly.
+ ///
+ ///
+ ///
+ ///
public abstract class AbstractEvent where THandler : class {
-
const int PENDING_SATE = 0;
- protected const int TRANSITIONAL_STATE = 1;
- protected const int SUCCEEDED_STATE = 2;
- protected const int REJECTED_STATE = 3;
+ const int TRANSITIONAL_STATE = 1;
+
+ const int FULFILLED_STATE = 2;
volatile int m_state;
- Exception m_error;
THandler m_handler;
SimpleAsyncQueue m_extraHandlers;
+ public bool IsFulfilled {
+ get {
+ return m_state > TRANSITIONAL_STATE;
+ }
+ }
+
#region state managment
protected bool BeginTransit() {
return PENDING_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, PENDING_SATE);
}
- protected void CompleteTransit(int state) {
+ protected void CompleteTransit() {
#if DEBUG
- if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE))
+ if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, FULFILLED_STATE, TRANSITIONAL_STATE))
throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
#else
m_state = state;
@@ -43,47 +76,13 @@ namespace Implab {
}
}
- protected bool BeginSetResult() {
- if (!BeginTransit()) {
- WaitTransition();
- return false;
- }
- return true;
- }
- protected void EndSetResult() {
- CompleteTransit(SUCCEEDED_STATE);
- }
-
-
-
- ///
- /// Выполняет обещание, сообщая об ошибке
- ///
- ///
- /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
- /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
- /// будут проигнорированы.
- ///
- /// Исключение возникшее при выполнении операции
- /// Данное обещание уже выполнено
- protected void SetError(Exception error) {
- if (BeginTransit()) {
- m_error = error;
- CompleteTransit(REJECTED_STATE);
- } else {
- WaitTransition();
- if (m_state == SUCCEEDED_STATE)
- throw new InvalidOperationException("The promise is already resolved");
- }
- }
-
- protected abstract void SignalHandler(THandler handler, int signal);
+ protected abstract void SignalHandler(THandler handler);
void Signal() {
THandler handler;
while (TryDequeueHandler(out handler))
- SignalHandler(handler, m_state);
+ SignalHandler(handler);
}
#endregion
@@ -94,46 +93,41 @@ namespace Implab {
protected void WaitResult(int timeout) {
if (!(IsFulfilled || GetFulfillSignal().Wait(timeout)))
throw new TimeoutException();
-
- if (IsRejected)
- Rethrow();
}
- 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);
- }
+
#endregion
#region handlers managment
protected void AddHandler(THandler handler) {
- if (m_state > 1) {
+ if (IsFulfilled) {
// the promise is in the resolved state, just invoke the handler
- SignalHandler(handler, m_state);
+ SignalHandler(handler);
} else {
- if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) {
- if (m_extraHandlers == null)
- // compare-exchange will fprotect from loosing already created queue
- Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue(), null);
- m_extraHandlers.Enqueue(handler);
- }
+ EnqueueHandler(handler);
- if (m_state > 1 && TryDequeueHandler(out handler))
+ if (IsFulfilled && TryDequeueHandler(out handler))
// if the promise have been resolved while we was adding the handler to the queue
// we can't guarantee that someone is still processing it
// therefore we need to fetch a handler from the queue and execute it
// note that fetched handler may be not the one that we have added
// even we can fetch no handlers at all :)
- SignalHandler(handler, m_state);
+ SignalHandler(handler);
}
}
+ void EnqueueHandler(THandler handler) {
+ if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) {
+ if (m_extraHandlers == null)
+ // compare-exchange will protect from loosing already created queue
+ Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue(), null);
+ m_extraHandlers.Enqueue(handler);
+ }
+ }
+
bool TryDequeueHandler(out THandler handler) {
handler = Interlocked.Exchange(ref m_handler, null);
if (handler != null)
@@ -142,29 +136,6 @@ namespace Implab {
}
#endregion
-
- #region IPromise implementation
-
- public bool IsFulfilled {
- get {
- return m_state > TRANSITIONAL_STATE;
- }
- }
-
- public bool IsRejected {
- get {
- return m_state == REJECTED_STATE;
- }
- }
-
- #endregion
-
- public Exception RejectReason {
- get {
- return m_error;
- }
- }
-
}
}
diff --git a/Implab/AbstractPromise.cs b/Implab/AbstractPromise.cs
--- a/Implab/AbstractPromise.cs
+++ b/Implab/AbstractPromise.cs
@@ -1,50 +1,77 @@
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_handler;
- readonly Action m_error;
+ readonly Action m_resolve;
+ readonly Action m_reject;
+
+ readonly IDeferred m_deferred;
public HandlerDescriptor(Action success, Action error) {
- m_handler = success;
- m_error = error;
+ m_resolve = success;
+ m_reject = error;
}
public void SignalSuccess() {
- if (m_handler != null) {
- try {
- m_handler();
- // Analysis disable once EmptyGeneralCatchClause
- } catch {
- }
+ try {
+ if (m_resolve != null)
+ m_resolve();
+ m_deferred.Resolve();
+ } catch (Exception ex) {
+ m_deferred.Reject(ex);
}
}
public void SignalError(Exception err) {
- if (m_error != null) {
+ if (m_reject != null) {
try {
- m_error(err);
- // Analysis disable once EmptyGeneralCatchClause
- } catch {
+ 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, int signal) {
- switch (signal) {
- case SUCCEEDED_STATE:
+ protected override void SignalHandler(HandlerDescriptor handler) {
+ switch (m_state) {
+ case PromiseState.Resolved:
handler.SignalSuccess();
break;
- case REJECTED_STATE:
+ case PromiseState.Rejected:
handler.SignalError(RejectReason);
break;
default:
- throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", signal));
+ throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", m_state));
}
}
@@ -56,12 +83,47 @@ namespace Implab {
#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));
}
@@ -72,16 +134,13 @@ namespace Implab {
public void Join() {
WaitResult(-1);
+ if (IsRejected)
+ Rethrow();
}
public void Join(int timeout) {
WaitResult(timeout);
}
-
- protected void SetResult() {
- if(BeginSetResult())
- EndSetResult();
- }
}
}
diff --git a/Implab/IPromise.cs b/Implab/IPromise.cs
--- a/Implab/IPromise.cs
+++ b/Implab/IPromise.cs
@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
namespace Implab {
- public interface IPromise: ICancellable {
+ public interface IPromise {
///
/// Тип результата, получаемого через данное обещание.
diff --git a/Implab/Promise.cs b/Implab/Promise.cs
--- a/Implab/Promise.cs
+++ b/Implab/Promise.cs
@@ -2,24 +2,24 @@
using Implab.Parallels;
namespace Implab {
- public class Promise : AbstractPromise, IDeferred {
+ public class Promise : AbstractPromise {
public static readonly IPromise Success;
static Promise() {
Success = new SuccessPromise();
}
- public void Resolve() {
+ internal void ResolvePromise() {
SetResult();
}
- public void Reject(Exception error) {
+ internal void RejectPromise(Exception error) {
SetError(error);
- }
-
- public static IPromise FromException(Exception exception) {
- return new FailedPromise(exception);
- }
+ }
+
+ public static IPromise Reject(Exception exception) {
+ return new FailedPromise(exception);
+ }
}
}
diff --git a/Implab/PromiseState.cs b/Implab/PromiseState.cs
new file mode 100644
--- /dev/null
+++ b/Implab/PromiseState.cs
@@ -0,0 +1,9 @@
+namespace Implab {
+ public enum PromiseState {
+ Pending,
+
+ Resolved,
+
+ Rejected
+ }
+}
\ No newline at end of file