##// END OF EJS Templates
working on promises
cin -
r244:eee3e49dd1ff v3
parent child
Show More
@@ -0,0 +1,52
1 using System;
2 using System.Diagnostics;
3
4 namespace Implab {
5 /// <summary>
6 /// This class is responsible for the promise resolution, dispatching and chaining
7 /// </summary>
8 public class Deferred : IResolvable {
9
10 readonly AbstractPromise m_promise;
11 readonly IDispatcher m_dispatcher;
12
13 internal Deferred(AbstractPromise promise, IDispatcher dispatcher) {
14 Debug.Assert(promise != null);
15 m_promise = promise;
16 m_dispatcher = dispatcher;
17 }
18
19 public IPromise Promise {
20 get { return m_promise; }
21 }
22
23 public void Reject(Exception error) {
24 m_promise.Reject(error);
25 }
26
27 public void Resolve() {
28 m_promise.Resolve();
29 }
30
31 public void Resolve(IPromise thenable) {
32 if (thenable == null)
33 Reject(new Exception("The promise or task are expected"));
34 if (thenable == m_promise)
35 Reject(new Exception("The promise cannot be resolved with oneself"));
36
37 else if (m_dispatcher != null)
38 // dispatch (see ecma-262/6.0: 25.4.1.3.2 Promise Resolve Functions)
39 m_dispatcher.Enqueue(() => thenable.Then(this));
40 else
41 thenable.Then(this);
42 }
43
44 void Chain(IPromise thenable) {
45 try {
46 thenable.Then(this);
47 } catch (Exception err) {
48 Reject(err);
49 }
50 }
51 }
52 } No newline at end of file
@@ -0,0 +1,7
1 using System;
2
3 namespace Implab {
4 public interface IDispatcher {
5 void Enqueue(Action job);
6 }
7 } No newline at end of file
@@ -0,0 +1,26
1 using System;
2
3 namespace Implab {
4 /// <summary>
5 /// Deferred result, usually used by asynchronous services as the service part of the promise.
6 /// </summary>
7 public interface IResolvable {
8
9 void Resolve();
10
11 void Resolve(IPromise thenable);
12
13 /// <summary>
14 /// Reject the promise with the specified error.
15 /// </summary>
16 /// <param name="error">The reason why the promise is rejected.</param>
17 /// <remarks>
18 /// Some exceptions are treated in a special case:
19 /// <see cref="OperationCanceledException"/> is interpreted as call to <see cref="Cancel()"/> method,
20 /// and <see cref="PromiseTransientException"/> is always unwrapped and its
21 /// <see cref="PromiseTransientException.InnerException"> is used as the reason to reject promise.
22 /// </remarks>
23 void Reject(Exception error);
24 }
25 }
26
@@ -35,31 +35,31 namespace Implab {
35 35 /// </para>
36 36 /// </remarks>
37 37 public abstract class AbstractEvent<THandler> where THandler : class {
38 const int PENDING_SATE = 0;
38 const int PendingState = 0;
39 39
40 const int TRANSITIONAL_STATE = 1;
40 const int TransitionalState = 1;
41 41
42 const int FULFILLED_STATE = 2;
42 const int ResolvedState = 2;
43 43
44 44 volatile int m_state;
45 45
46 46 THandler m_handler;
47 47 SimpleAsyncQueue<THandler> m_extraHandlers;
48 48
49 public bool IsFulfilled {
49 public bool IsResolved {
50 50 get {
51 return m_state > TRANSITIONAL_STATE;
51 return m_state > TransitionalState;
52 52 }
53 53 }
54 54
55 55 #region state managment
56 56 protected bool BeginTransit() {
57 return PENDING_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, PENDING_SATE);
57 return PendingState == Interlocked.CompareExchange(ref m_state, TransitionalState, PendingState);
58 58 }
59 59
60 60 protected void CompleteTransit() {
61 61 #if DEBUG
62 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, FULFILLED_STATE, TRANSITIONAL_STATE))
62 if (TransitionalState != Interlocked.CompareExchange(ref m_state, ResolvedState, TransitionalState))
63 63 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
64 64 #else
65 65 m_state = state;
@@ -68,11 +68,11 namespace Implab {
68 68 }
69 69
70 70 protected void WaitTransition() {
71 if (m_state == TRANSITIONAL_STATE) {
71 if (m_state == TransitionalState) {
72 72 SpinWait spin;
73 73 do {
74 74 spin.SpinOnce();
75 } while (m_state == TRANSITIONAL_STATE);
75 } while (m_state == TransitionalState);
76 76 }
77 77 }
78 78
@@ -91,7 +91,7 namespace Implab {
91 91
92 92 #region synchronization traits
93 93 protected void WaitResult(int timeout) {
94 if (!(IsFulfilled || GetFulfillSignal().Wait(timeout)))
94 if (!(IsResolved || GetFulfillSignal().Wait(timeout)))
95 95 throw new TimeoutException();
96 96 }
97 97
@@ -102,13 +102,13 namespace Implab {
102 102
103 103 protected void AddHandler(THandler handler) {
104 104
105 if (IsFulfilled) {
105 if (IsResolved) {
106 106 // the promise is in the resolved state, just invoke the handler
107 107 SignalHandler(handler);
108 108 } else {
109 109 EnqueueHandler(handler);
110 110
111 if (IsFulfilled && TryDequeueHandler(out handler))
111 if (IsResolved && TryDequeueHandler(out handler))
112 112 // if the promise have been resolved while we was adding the handler to the queue
113 113 // we can't guarantee that someone is still processing it
114 114 // therefore we need to fetch a handler from the queue and execute it
@@ -4,36 +4,21 using System.Reflection;
4 4 using Implab.Parallels;
5 5
6 6 namespace Implab {
7 public abstract class AbstractPromise : AbstractEvent<AbstractPromise.HandlerDescriptor>, IPromise {
8 public class HandlerDescriptor {
9 readonly Action m_resolve;
10 readonly Action<Exception> m_reject;
7 public class AbstractPromise : AbstractEvent<IResolvable>, IPromise {
11 8
12 readonly IDeferred m_deferred;
13 public HandlerDescriptor(Action success, Action<Exception> error) {
14 m_resolve = success;
15 m_reject = error;
9 class ResolvableSignal : IResolvable {
10 public Signal Signal { get; private set; }
11 public ResolvableSignal() {
12 Signal = new Signal();
16 13 }
17 14
18 public void SignalSuccess() {
19 try {
20 if (m_resolve != null)
21 m_resolve();
22 m_deferred.Resolve();
23 } catch (Exception ex) {
24 m_deferred.Reject(ex);
25 }
15
16 public void Reject(Exception error) {
17 Signal.Set();
26 18 }
27 19
28 public void SignalError(Exception err) {
29 if (m_reject != null) {
30 try {
31 m_reject(err);
32 m_deferred.Resolve();
33 } catch (Exception ex) {
34 m_deferred.Reject(ex);
35 }
36 }
20 public void Resolve() {
21 Signal.Set();
37 22 }
38 23 }
39 24
@@ -47,9 +32,9 namespace Implab {
47 32 }
48 33 }
49 34
50 public bool IsResolved {
35 public bool IsFulfilled {
51 36 get {
52 return m_state == PromiseState.Resolved;
37 return m_state == PromiseState.Fulfilled;
53 38 }
54 39 }
55 40
@@ -60,15 +45,29 namespace Implab {
60 45 }
61 46
62 47
48 internal void Resolve() {
49 if (BeginTransit())
50 CompleteResolve();
51 }
52
53 internal void Reject(Exception reason) {
54 if (BeginTransit()) {
55 m_error = reason;
56 m_state = PromiseState.Rejected;
57 CompleteTransit();
58 }
59 }
60
61
63 62 #region implemented abstract members of AbstractPromise
64 63
65 protected override void SignalHandler(HandlerDescriptor handler) {
64 protected override void SignalHandler(IResolvable handler) {
66 65 switch (m_state) {
67 case PromiseState.Resolved:
68 handler.SignalSuccess();
66 case PromiseState.Fulfilled:
67 handler.Resolve();
69 68 break;
70 69 case PromiseState.Rejected:
71 handler.SignalError(RejectReason);
70 handler.Reject(RejectReason);
72 71 break;
73 72 default:
74 73 throw new InvalidOperationException(String.Format("Invalid promise signal: {0}", m_state));
@@ -76,15 +75,15 namespace Implab {
76 75 }
77 76
78 77 protected override Signal GetFulfillSignal() {
79 var signal = new Signal();
80 On(signal.Set, e => signal.Set());
81 return signal;
78 var next = new ResolvableSignal();
79 Then(next);
80 return next.Signal;
82 81 }
83 82
84 83 #endregion
85 84
86 85 protected void CompleteResolve() {
87 m_state = PromiseState.Resolved;
86 m_state = PromiseState.Fulfilled;
88 87 CompleteTransit();
89 88 }
90 89
@@ -94,27 +93,6 namespace Implab {
94 93 }
95 94 }
96 95
97 /// <summary>
98 /// Выполняет обещание, сообщая об ошибке
99 /// </summary>
100 /// <remarks>
101 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
102 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
103 /// будут проигнорированы.
104 /// </remarks>
105 /// <param name="error">Исключение возникшее при выполнении операции</param>
106 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
107 protected void SetError(Exception error) {
108 if (BeginTransit()) {
109 m_error = error;
110 m_state = PromiseState.Rejected;
111 CompleteTransit();
112 } else {
113 WaitTransition();
114 if (m_state == PromiseState.Resolved)
115 throw new InvalidOperationException("The promise is already resolved");
116 }
117 }
118 96
119 97 protected void Rethrow() {
120 98 Debug.Assert(m_error != null);
@@ -124,8 +102,8 namespace Implab {
124 102 throw new TargetInvocationException(m_error);
125 103 }
126 104
127 public void On(Action success, Action<Exception> error) {
128 AddHandler(new HandlerDescriptor(success, error));
105 public void Then(IResolvable next) {
106 AddHandler(next);
129 107 }
130 108
131 109 public IPromise<T> Cast<T>() {
@@ -1,7 +1,7
1 1 using System;
2 2
3 3 namespace Implab {
4 public class ActionChainTask : ActionChainTaskBase, IDeferred {
4 public class ActionChainTask : ActionChainTaskBase, IResolvable {
5 5 readonly Func<IPromise> m_task;
6 6
7 7 /// <summary>
@@ -1,7 +1,7
1 1 using System;
2 2
3 3 namespace Implab {
4 public class ActionTask : ActionTaskBase, IDeferred {
4 public class ActionTask : ActionTaskBase, IResolvable {
5 5 readonly Action m_task;
6 6 public ActionTask(Action task, Action<Exception> error, Action<Exception> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
7 7 m_task = task;
@@ -272,7 +272,7 namespace Implab.Components {
272 272 throw new NotImplementedException();
273 273 }
274 274
275 IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) {
275 IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IResolvable> chain) {
276 276 IPromise promise = null;
277 277 IPromise prev;
278 278
@@ -344,7 +344,7 namespace Implab.Components {
344 344 /// </summary>
345 345 /// <param name="current">Current.</param>
346 346 /// <param name="stop">Stop.</param>
347 protected virtual void StopPending(IPromise current, IDeferred stop) {
347 protected virtual void StopPending(IPromise current, IResolvable stop) {
348 348 if (current == null) {
349 349 stop.Resolve();
350 350 } else {
@@ -1,7 +1,7
1 1 using System;
2 2
3 3 namespace Implab {
4 public class FuncChainTask<TResult> : FuncChainTaskBase<TResult>, IDeferred {
4 public class FuncChainTask<TResult> : FuncChainTaskBase<TResult>, IResolvable {
5 5 readonly Func<IPromise<TResult>> m_task;
6 6
7 7 public FuncChainTask(Func<IPromise<TResult>> task, Func<Exception, IPromise<TResult>> error, Func<Exception, IPromise<TResult>> cancel, bool autoCancellable)
@@ -2,7 +2,7
2 2 using System.Threading;
3 3
4 4 namespace Implab {
5 public class FuncTask<T> : FuncTaskBase<T>, IDeferred {
5 public class FuncTask<T> : FuncTaskBase<T>, IResolvable {
6 6 readonly Func<T> m_task;
7 7
8 8 public FuncTask(Func<T> task, Func<Exception, T> error, Func<Exception, T> cancel, bool autoCancellable) : base(error, cancel, autoCancellable) {
@@ -14,11 +14,11 namespace Implab {
14 14 /// <summary>
15 15 /// Обещание является выполненым, либо успешно, либо с ошибкой, либо отменено.
16 16 /// </summary>
17 bool IsFulfilled { get; }
17 bool IsResolved { get; }
18 18
19 19 bool IsRejected { get; }
20 20
21 bool IsResolved { get; }
21 bool IsFulfilled { get; }
22 22
23 23 /// <summary>
24 24 /// Исключение возникшее в результате выполнения обещания, либо причина отмены.
@@ -31,23 +31,11 namespace Implab {
31 31 /// <param name="success">The handler called on the successful promise completion.</param>
32 32 /// <param name="error">The handler is called if an error while completing the promise occurred.</param>
33 33 /// <returns>The current promise.</returns>
34 void On(Action success, Action<Exception> error);
34 void Then(IResolvable next);
35 35
36 36 /// <summary>
37 37 /// Преобразует результат обещания к заданному типу и возвращает новое обещание.
38 38 /// </summary>
39 39 IPromise<T> Cast<T>();
40
41 /// <summary>
42 /// Синхронизирует текущий поток с обещанием.
43 /// </summary>
44 void Join();
45 /// <summary>
46 /// Синхронизирует текущий поток с обещанием.
47 /// </summary>
48 /// <param name="timeout">Время ожидания, по его истечению возникнет исключение.</param>
49 /// <exception cref="TimeoutException">Превышено время ожидания.</exception>
50 void Join(int timeout);
51
52 40 }
53 41 }
@@ -2,7 +2,7 namespace Implab {
2 2 public enum PromiseState {
3 3 Pending,
4 4
5 Resolved,
5 Fulfilled,
6 6
7 7 Rejected
8 8 }
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now