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