##// 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
@@ -1,141 +1,141
1 1 using System;
2 2 using Implab.Parallels;
3 3 using System.Threading;
4 4 using System.Reflection;
5 5 using System.Diagnostics;
6 6
7 7 namespace Implab {
8 8 /// <summary>
9 9 /// Abstract class for creation of custom one-shot thread safe events.
10 10 /// </summary>
11 11 /// <remarks>
12 12 /// <para>
13 13 /// An event is something that should happen in the future and the
14 14 /// triggering of the event causes execution of some pending actions
15 15 /// which are formely event handlers. One-shot events occur only once
16 16 /// and any handler added after the event is triggered should run
17 17 /// without a delay.
18 18 /// </para>
19 19 /// <para>
20 20 /// The lifecycle of the one-shot event is tipically consists of following
21 21 /// phases.
22 22 /// <list>
23 23 /// <description>Pending state. This is the initial state of the event. Any
24 24 /// handler added to the event will be queued for the future execution.
25 25 /// </description>
26 26 /// <description>Transitional state. This is intermediate state between pending
27 27 /// and fulfilled states, during this state internal initialization and storing
28 28 /// of the result occurs.
29 29 /// </description>
30 30 /// <description>Fulfilled state. The event contains the result, all queued
31 31 /// handlers are signalled to run and newly added handlers are executed
32 32 /// immediatelly.
33 33 /// </description>
34 34 /// </list>
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;
66 66 #endif
67 67 Signal();
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
79 79
80 80 protected abstract void SignalHandler(THandler handler);
81 81
82 82 void Signal() {
83 83 THandler handler;
84 84 while (TryDequeueHandler(out handler))
85 85 SignalHandler(handler);
86 86 }
87 87
88 88 #endregion
89 89
90 90 protected abstract Signal GetFulfillSignal();
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
98 98
99 99 #endregion
100 100
101 101 #region handlers managment
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
115 115 // note that fetched handler may be not the one that we have added
116 116 // even we can fetch no handlers at all :)
117 117 SignalHandler(handler);
118 118 }
119 119
120 120 }
121 121
122 122 void EnqueueHandler(THandler handler) {
123 123 if (Interlocked.CompareExchange(ref m_handler, handler, null) != null) {
124 124 if (m_extraHandlers == null)
125 125 // compare-exchange will protect from loosing already created queue
126 126 Interlocked.CompareExchange(ref m_extraHandlers, new SimpleAsyncQueue<THandler>(), null);
127 127 m_extraHandlers.Enqueue(handler);
128 128 }
129 129 }
130 130
131 131 bool TryDequeueHandler(out THandler handler) {
132 132 handler = Interlocked.Exchange(ref m_handler, null);
133 133 if (handler != null)
134 134 return true;
135 135 return m_extraHandlers != null && m_extraHandlers.TryDequeue(out handler);
136 136 }
137 137
138 138 #endregion
139 139 }
140 140 }
141 141
@@ -1,146 +1,124
1 1 using System;
2 2 using System.Diagnostics;
3 3 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
40 25 PromiseState m_state;
41 26
42 27 Exception m_error;
43 28
44 29 public bool IsRejected {
45 30 get {
46 31 return m_state == PromiseState.Rejected;
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
56 41 public Exception RejectReason {
57 42 get {
58 43 return m_error;
59 44 }
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));
75 74 }
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
91 90 public Type ResultType {
92 91 get {
93 92 return typeof(void);
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);
121 99 if (m_error is OperationCanceledException)
122 100 throw new OperationCanceledException("Operation cancelled", m_error);
123 101 else
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>() {
132 110 throw new InvalidCastException();
133 111 }
134 112
135 113 public void Join() {
136 114 WaitResult(-1);
137 115 if (IsRejected)
138 116 Rethrow();
139 117 }
140 118
141 119 public void Join(int timeout) {
142 120 WaitResult(timeout);
143 121 }
144 122 }
145 123 }
146 124
@@ -1,34 +1,34
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>
8 8 /// Initializes a new instance of the <see cref="Implab.ActionChainTask"/> class.
9 9 /// </summary>
10 10 /// <param name="task">The operation which will be performed when the <see cref="Resolve()"/> is called.</param>
11 11 /// <param name="error">The error handler which will invoke when the <see cref="Reject(Exception)"/> is called or when the task fails with an error.</param>
12 12 /// <param name="cancel">The cancellation handler.</param>
13 13 /// <param name="autoCancellable">If set to <c>true</c> will automatically accept
14 14 /// all cancel requests before the task is started with <see cref="Resolve()"/>,
15 15 /// after that all requests are directed to the task.</param>
16 16 public ActionChainTask(Func<IPromise> task, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel, bool autoCancellable) : base(error,cancel, autoCancellable) {
17 17 m_task = task;
18 18 }
19 19
20 20 public void Resolve() {
21 21 if (m_task != null && LockCancelation()) {
22 22 try {
23 23 var p = m_task();
24 24 p.On(SetResult, HandleErrorInternal, HandleCancelInternal);
25 25 CancellationRequested(p.Cancel);
26 26 } catch(Exception err) {
27 27 SetErrorInternal(err);
28 28 }
29 29 }
30 30 }
31 31
32 32 }
33 33 }
34 34
@@ -1,22 +1,22
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;
8 8 }
9 9
10 10 public void Resolve() {
11 11 if (m_task != null && LockCancelation()) {
12 12 try {
13 13 m_task();
14 14 SetResult();
15 15 } catch(Exception err) {
16 16 SetErrorInternal(err);
17 17 }
18 18 }
19 19 }
20 20 }
21 21 }
22 22
@@ -1,411 +1,411
1 1 using System;
2 2 using System.Diagnostics.CodeAnalysis;
3 3
4 4 namespace Implab.Components {
5 5 public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable {
6 6 enum Commands {
7 7 Ok = 0,
8 8 Fail,
9 9 Init,
10 10 Start,
11 11 Stop,
12 12 Dispose,
13 13 Reset,
14 14 Last = Reset
15 15 }
16 16
17 17 class StateMachine {
18 18 public static readonly ExecutionState[,] ReusableTransitions;
19 19 public static readonly ExecutionState[,] NonreusableTransitions;
20 20
21 21 class StateBuilder {
22 22 readonly ExecutionState[,] m_states;
23 23
24 24 public ExecutionState[,] States {
25 25 get { return m_states; }
26 26 }
27 27 public StateBuilder(ExecutionState[,] states) {
28 28 m_states = states;
29 29 }
30 30
31 31 public StateBuilder() {
32 32 m_states = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
33 33 }
34 34
35 35 public StateBuilder Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
36 36 m_states[(int)s1, (int)cmd] = s2;
37 37 return this;
38 38 }
39 39
40 40 public StateBuilder Clone() {
41 41 return new StateBuilder((ExecutionState[,])m_states.Clone());
42 42 }
43 43 }
44 44
45 45 static StateMachine() {
46 46 ReusableTransitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
47 47
48 48 var common = new StateBuilder()
49 49 .Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init)
50 50 .Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose)
51 51
52 52 .Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok)
53 53 .Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail)
54 54
55 55 .Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start)
56 56 .Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose)
57 57
58 58 .Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok)
59 59 .Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail)
60 60 .Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop)
61 61 .Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose)
62 62
63 63 .Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail)
64 64 .Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop)
65 65 .Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose)
66 66
67 67 .Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose)
68 68 .Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset)
69 69
70 70 .Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail)
71 71 .Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose)
72 72
73 73 .Edge(ExecutionState.Disposed, ExecutionState.Disposed, Commands.Dispose);
74 74
75 75 var reusable = common
76 76 .Clone()
77 77 .Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok);
78 78
79 79 var nonreusable = common
80 80 .Clone()
81 81 .Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
82 82
83 83 NonreusableTransitions = nonreusable.States;
84 84 ReusableTransitions = reusable.States;
85 85
86 86 }
87 87
88 88 readonly ExecutionState[,] m_states;
89 89
90 90 public ExecutionState State {
91 91 get;
92 92 private set;
93 93 }
94 94
95 95 public StateMachine(ExecutionState[,] states, ExecutionState initial) {
96 96 State = initial;
97 97 m_states = states;
98 98 }
99 99
100 100 public bool Move(Commands cmd) {
101 101 var next = m_states[(int)State, (int)cmd];
102 102 if (next == ExecutionState.Undefined)
103 103 return false;
104 104 State = next;
105 105 return true;
106 106 }
107 107 }
108 108
109 109 IPromise m_pending;
110 110 Exception m_lastError;
111 111
112 112 readonly StateMachine m_stateMachine;
113 113 readonly bool m_reusable;
114 114 public event EventHandler<StateChangeEventArgs> StateChanged;
115 115
116 116 /// <summary>
117 117 /// Initializes component state.
118 118 /// </summary>
119 119 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param>
120 120 /// <param name="reusable">If set, the component may start after it has been stopped, otherwise the component is disposed after being stopped.</param>
121 121 protected RunnableComponent(bool initialized, bool reusable) {
122 122 m_stateMachine = new StateMachine(
123 123 reusable ? StateMachine.ReusableTransitions : StateMachine.NonreusableTransitions,
124 124 initialized ? ExecutionState.Ready : ExecutionState.Created
125 125 );
126 126 m_reusable = reusable;
127 127 }
128 128
129 129 /// <summary>
130 130 /// Initializes component state. The component created with this constructor is not reusable, i.e. it will be disposed after stop.
131 131 /// </summary>
132 132 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param>
133 133 protected RunnableComponent(bool initialized) : this(initialized, false) {
134 134 }
135 135
136 136 void ThrowInvalidCommand(Commands cmd) {
137 137 if (m_stateMachine.State == ExecutionState.Disposed)
138 138 throw new ObjectDisposedException(ToString());
139 139
140 140 throw new InvalidOperationException(String.Format("Command {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
141 141 }
142 142
143 143 bool MoveIfInState(Commands cmd, IPromise pending, Exception error, ExecutionState state) {
144 144 ExecutionState prev, current;
145 145 lock (m_stateMachine) {
146 146 if (m_stateMachine.State != state)
147 147 return false;
148 148
149 149 prev = m_stateMachine.State;
150 150 if (!m_stateMachine.Move(cmd))
151 151 ThrowInvalidCommand(cmd);
152 152 current = m_stateMachine.State;
153 153
154 154 m_pending = pending;
155 155 m_lastError = error;
156 156 }
157 157 if (prev != current)
158 158 OnStateChanged(prev, current, error);
159 159 return true;
160 160 }
161 161
162 162 bool MoveIfPending(Commands cmd, IPromise pending, Exception error, IPromise expected) {
163 163 ExecutionState prev, current;
164 164 lock (m_stateMachine) {
165 165 if (m_pending != expected)
166 166 return false;
167 167 prev = m_stateMachine.State;
168 168 if (!m_stateMachine.Move(cmd))
169 169 ThrowInvalidCommand(cmd);
170 170 current = m_stateMachine.State;
171 171 m_pending = pending;
172 172 m_lastError = error;
173 173 }
174 174 if (prev != current)
175 175 OnStateChanged(prev, current, error);
176 176 return true;
177 177 }
178 178
179 179 IPromise Move(Commands cmd, IPromise pending, Exception error) {
180 180 ExecutionState prev, current;
181 181 IPromise ret;
182 182 lock (m_stateMachine) {
183 183 prev = m_stateMachine.State;
184 184 if (!m_stateMachine.Move(cmd))
185 185 ThrowInvalidCommand(cmd);
186 186 current = m_stateMachine.State;
187 187
188 188 ret = m_pending;
189 189 m_pending = pending;
190 190 m_lastError = error;
191 191
192 192 }
193 193 if (prev != current)
194 194 OnStateChanged(prev, current, error);
195 195 return ret;
196 196 }
197 197
198 198 /// <summary>
199 199 /// Handles the state of the component change event, raises the <see cref="StateChanged"/> event, handles
200 200 /// the transition to the <see cref="ExecutionState.Disposed"/> state (calls <see cref="Dispose(bool)"/> method).
201 201 /// </summary>
202 202 /// <param name="previous">The previous state</param>
203 203 /// <param name="current">The current state</param>
204 204 /// <param name="error">The last error if any.</param>
205 205 /// <remarks>
206 206 /// <para>
207 207 /// If the previous state and the current state are same this method isn't called, such situiation is treated
208 208 /// as the component hasn't changed it's state.
209 209 /// </para>
210 210 /// <para>
211 211 /// When overriding this method ensure the call is made to the base implementation, otherwise it will lead to
212 212 /// the wrong behavior of the component.
213 213 /// </para>
214 214 /// </remarks>
215 215 protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) {
216 216 StateChanged.DispatchEvent(
217 217 this,
218 218 new StateChangeEventArgs {
219 219 State = current,
220 220 LastError = error
221 221 }
222 222 );
223 223 if (current == ExecutionState.Disposed) {
224 224 GC.SuppressFinalize(this);
225 225 Dispose(true);
226 226 }
227 227 }
228 228
229 229 /// <summary>
230 230 /// Moves the component from running to failed state.
231 231 /// </summary>
232 232 /// <param name="error">The exception which is describing the error.</param>
233 233 protected bool Fail(Exception error) {
234 234 return MoveIfInState(Commands.Fail, null, error, ExecutionState.Running);
235 235 }
236 236
237 237 /// <summary>
238 238 /// Tries to reset <see cref="ExecutionState.Failed"/> state to <see cref="ExecutionState.Ready"/>.
239 239 /// </summary>
240 240 /// <returns>True if component is reset to <see cref="ExecutionState.Ready"/>, false if the componet wasn't
241 241 /// in <see cref="ExecutionState.Failed"/> state.</returns>
242 242 /// <remarks>
243 243 /// This method checks the current state of the component and if it's in <see cref="ExecutionState.Failed"/>
244 244 /// moves component to <see cref="ExecutionState.Initializing"/>.
245 245 /// The <see cref="OnResetState()"/> is called and if this method completes succesfully the component moved
246 246 /// to <see cref="ExecutionState.Ready"/> state, otherwise the component is moved to <see cref="ExecutionState.Failed"/>
247 247 /// state. If <see cref="OnResetState()"/> throws an exception it will be propagated by this method to the caller.
248 248 /// </remarks>
249 249 protected bool ResetState() {
250 250 if (!MoveIfInState(Commands.Reset, null, null, ExecutionState.Failed))
251 251 return false;
252 252
253 253 try {
254 254 OnResetState();
255 255 Move(Commands.Ok, null, null);
256 256 return true;
257 257 } catch (Exception err) {
258 258 Move(Commands.Fail, null, err);
259 259 throw;
260 260 }
261 261 }
262 262
263 263 /// <summary>
264 264 /// This method is called by <see cref="ResetState"/> to reinitialize component in the failed state.
265 265 /// </summary>
266 266 /// <remarks>
267 267 /// Default implementation throws <see cref="NotImplementedException"/> which will cause the component
268 268 /// fail to reset it's state and it left in <see cref="ExecutionState.Failed"/> state.
269 269 /// If this method doesn't throw exceptions the component is moved to <see cref="ExecutionState.Ready"/> state.
270 270 /// </remarks>
271 271 protected virtual void OnResetState() {
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
279 279 var task = new ActionChainTask(action, null, null, true);
280 280
281 281 Action<Exception> errorOrCancel = e => {
282 282 if (e == null)
283 283 e = new OperationCanceledException();
284 284 MoveIfPending(Commands.Fail, null, e, promise);
285 285 throw new PromiseTransientException(e);
286 286 };
287 287
288 288 promise = task.Then(
289 289 () => MoveIfPending(Commands.Ok, null, null, promise),
290 290 errorOrCancel,
291 291 errorOrCancel
292 292 );
293 293
294 294 prev = Move(cmd, promise, null);
295 295
296 296 if (prev == null)
297 297 task.Resolve();
298 298 else
299 299 chain(prev, task);
300 300
301 301 return promise;
302 302 }
303 303
304 304
305 305 #region IInitializable implementation
306 306
307 307 public void Initialize() {
308 308 Move(Commands.Init, null, null);
309 309
310 310 try {
311 311 OnInitialize();
312 312 Move(Commands.Ok, null, null);
313 313 } catch (Exception err) {
314 314 Move(Commands.Fail, null, err);
315 315 throw;
316 316 }
317 317 }
318 318
319 319 protected virtual void OnInitialize() {
320 320 }
321 321
322 322 #endregion
323 323
324 324 #region IRunnable implementation
325 325
326 326 public IPromise Start() {
327 327 return InvokeAsync(Commands.Start, OnStart, null);
328 328 }
329 329
330 330 protected virtual IPromise OnStart() {
331 331 return Promise.Success;
332 332 }
333 333
334 334 public IPromise Stop() {
335 335 return InvokeAsync(Commands.Stop, OnStop, StopPending);
336 336 }
337 337
338 338 protected virtual IPromise OnStop() {
339 339 return Promise.Success;
340 340 }
341 341
342 342 /// <summary>
343 343 /// Stops the current operation if one exists.
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 {
351 351 // связваем текущую операцию с операцией остановки
352 352 current.On(
353 353 stop.Resolve, // если текущая операция заверщилась, то можно начинать остановку
354 354 stop.Reject, // если текущая операция дала ошибку - то все плохо, нельзя продолжать
355 355 e => stop.Resolve() // если текущая отменилась, то можно начинать остановку
356 356 );
357 357 // посылаем текущей операции сигнал остановки
358 358 current.Cancel();
359 359 }
360 360 }
361 361
362 362 public ExecutionState State {
363 363 get {
364 364 return m_stateMachine.State;
365 365 }
366 366 }
367 367
368 368 public Exception LastError {
369 369 get {
370 370 return m_lastError;
371 371 }
372 372 }
373 373
374 374 #endregion
375 375
376 376 #region IDisposable implementation
377 377
378 378 /// <summary>
379 379 /// Releases all resource used by the <see cref="Implab.Components.RunnableComponent"/> object.
380 380 /// </summary>
381 381 /// <remarks>
382 382 /// <para>Will not try to stop the component, it will just release all resources.
383 383 /// To cleanup the component gracefully use <see cref="Stop()"/> method.</para>
384 384 /// <para>
385 385 /// In normal cases the <see cref="Dispose()"/> method shouldn't be called, the call to the <see cref="Stop()"/>
386 386 /// method is sufficient to cleanup the component. Call <see cref="Dispose()"/> only to cleanup after errors,
387 387 /// especially if <see cref="Stop"/> method is failed. Using this method insted of <see cref="Stop()"/> may
388 388 /// lead to the data loss by the component.
389 389 /// </para></remarks>
390 390 [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")]
391 391 public void Dispose() {
392 392 Move(Commands.Dispose, null, null);
393 393 }
394 394
395 395 ~RunnableComponent() {
396 396 Dispose(false);
397 397 }
398 398
399 399 #endregion
400 400
401 401 /// <summary>
402 402 /// Releases all resources used by the component, called automatically, override this method to implement your cleanup.
403 403 /// </summary>
404 404 /// <param name="disposing">true if this method is called during normal dispose process.</param>
405 405 /// <param name="pending">The operation which is currenty pending</param>
406 406 protected virtual void Dispose(bool disposing) {
407 407 }
408 408
409 409 }
410 410 }
411 411
@@ -1,24 +1,24
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)
8 8 : base(error, cancel, autoCancellable) {
9 9 m_task = task;
10 10 }
11 11
12 12 public void Resolve() {
13 13 if (m_task != null && LockCancelation()) {
14 14 try {
15 15 var operation = m_task();
16 16 operation.On(SetResult, HandleErrorInternal, HandleCancelInternal);
17 17 CancellationRequested(operation.Cancel);
18 18 } catch (Exception err) {
19 19 SetErrorInternal(err);
20 20 }
21 21 }
22 22 }
23 23 }
24 24 } No newline at end of file
@@ -1,23 +1,23
1 1 using System;
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) {
9 9 m_task = task;
10 10 }
11 11
12 12 public void Resolve() {
13 13 if (m_task != null && LockCancelation()) {
14 14 try {
15 15 SetResult(m_task());
16 16 } catch(Exception err) {
17 17 SetErrorInternal(err);
18 18 }
19 19 }
20 20 }
21 21 }
22 22 }
23 23
@@ -1,53 +1,41
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5
6 6 namespace Implab {
7 7 public interface IPromise {
8 8
9 9 /// <summary>
10 10 /// Тип результата, получаемого через данное обещание.
11 11 /// </summary>
12 12 Type ResultType { get; }
13 13
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 /// Исключение возникшее в результате выполнения обещания, либо причина отмены.
25 25 /// </summary>
26 26 Exception RejectReason { get; }
27 27
28 28 /// <summary>
29 29 /// Adds specified listeners to the current promise.
30 30 /// </summary>
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 }
@@ -1,9 +1,9
1 1 namespace Implab {
2 2 public enum PromiseState {
3 3 Pending,
4 4
5 Resolved,
5 Fulfilled,
6 6
7 7 Rejected
8 8 }
9 9 } No newline at end of file
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