@@ -0,0 +1,194 | |||||
|
1 | using System; | |||
|
2 | using System.Reflection; | |||
|
3 | using System.Threading; | |||
|
4 | using Implab.Parallels; | |||
|
5 | using Implab.Components; | |||
|
6 | ||||
|
7 | #if MONO | |||
|
8 | ||||
|
9 | using NUnit.Framework; | |||
|
10 | using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; | |||
|
11 | using TestMethodAttribute = NUnit.Framework.TestAttribute; | |||
|
12 | ||||
|
13 | #else | |||
|
14 | ||||
|
15 | using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
|
16 | ||||
|
17 | #endif | |||
|
18 | ||||
|
19 | namespace Implab.Test { | |||
|
20 | [TestClass] | |||
|
21 | public class RunnableComponentTests { | |||
|
22 | ||||
|
23 | static void ShouldThrow(Action action) { | |||
|
24 | try { | |||
|
25 | action(); | |||
|
26 | Assert.Fail(); | |||
|
27 | } catch(AssertionException) { | |||
|
28 | throw; | |||
|
29 | } catch { | |||
|
30 | } | |||
|
31 | } | |||
|
32 | ||||
|
33 | class Runnable : RunnableComponent { | |||
|
34 | public Runnable(bool initialized) : base(initialized) { | |||
|
35 | } | |||
|
36 | ||||
|
37 | public Action MockInit { | |||
|
38 | get; | |||
|
39 | set; | |||
|
40 | } | |||
|
41 | ||||
|
42 | public Func<IPromise> MockStart { | |||
|
43 | get; | |||
|
44 | set; | |||
|
45 | } | |||
|
46 | ||||
|
47 | public Func<IPromise> MockStop { | |||
|
48 | get; | |||
|
49 | set; | |||
|
50 | } | |||
|
51 | ||||
|
52 | protected override IPromise OnStart() { | |||
|
53 | return MockStart != null ? MockStart() : base.OnStart(); | |||
|
54 | } | |||
|
55 | ||||
|
56 | protected override IPromise OnStop() { | |||
|
57 | return MockStop != null ? MockStop() : base.OnStart(); | |||
|
58 | } | |||
|
59 | ||||
|
60 | protected override void OnInitialize() { | |||
|
61 | if (MockInit != null) | |||
|
62 | MockInit(); | |||
|
63 | } | |||
|
64 | } | |||
|
65 | ||||
|
66 | [TestMethod] | |||
|
67 | public void NormalFlowTest() { | |||
|
68 | var comp = new Runnable(false); | |||
|
69 | ||||
|
70 | Assert.AreEqual(ExecutionState.Created, comp.State); | |||
|
71 | ||||
|
72 | comp.Init(); | |||
|
73 | ||||
|
74 | Assert.AreEqual(ExecutionState.Ready, comp.State); | |||
|
75 | ||||
|
76 | comp.Start().Join(1000); | |||
|
77 | ||||
|
78 | Assert.AreEqual(ExecutionState.Running, comp.State); | |||
|
79 | ||||
|
80 | comp.Stop().Join(1000); | |||
|
81 | ||||
|
82 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |||
|
83 | ||||
|
84 | } | |||
|
85 | ||||
|
86 | [TestMethod] | |||
|
87 | public void InitFailTest() { | |||
|
88 | var comp = new Runnable(false) { | |||
|
89 | MockInit = () => { | |||
|
90 | throw new Exception("BAD"); | |||
|
91 | } | |||
|
92 | }; | |||
|
93 | ||||
|
94 | ShouldThrow(() => comp.Start()); | |||
|
95 | ShouldThrow(() => comp.Stop()); | |||
|
96 | Assert.AreEqual(ExecutionState.Created, comp.State); | |||
|
97 | ||||
|
98 | ShouldThrow(comp.Init); | |||
|
99 | ||||
|
100 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |||
|
101 | ||||
|
102 | ShouldThrow(() => comp.Start()); | |||
|
103 | ShouldThrow(() => comp.Stop()); | |||
|
104 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |||
|
105 | ||||
|
106 | comp.Dispose(); | |||
|
107 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |||
|
108 | } | |||
|
109 | ||||
|
110 | [TestMethod] | |||
|
111 | public void DisposedTest() { | |||
|
112 | ||||
|
113 | var comp = new Runnable(false); | |||
|
114 | comp.Dispose(); | |||
|
115 | ||||
|
116 | ShouldThrow(() => comp.Start()); | |||
|
117 | ShouldThrow(() => comp.Stop()); | |||
|
118 | ShouldThrow(comp.Init); | |||
|
119 | ||||
|
120 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |||
|
121 | } | |||
|
122 | ||||
|
123 | [TestMethod] | |||
|
124 | public void StartCancelTest() { | |||
|
125 | var comp = new Runnable(true) { | |||
|
126 | MockStart = () => PromiseHelper.Sleep(100000, 0) | |||
|
127 | }; | |||
|
128 | ||||
|
129 | var p = comp.Start(); | |||
|
130 | Assert.AreEqual(ExecutionState.Starting, comp.State); | |||
|
131 | p.Cancel(); | |||
|
132 | ShouldThrow(() => p.Join(1000)); | |||
|
133 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |||
|
134 | Assert.IsInstanceOfType(typeof(OperationCanceledException), comp.LastError); | |||
|
135 | ||||
|
136 | comp.Dispose(); | |||
|
137 | } | |||
|
138 | ||||
|
139 | [TestMethod] | |||
|
140 | public void StartStopTest() { | |||
|
141 | var stop = new Signal(); | |||
|
142 | var comp = new Runnable(true) { | |||
|
143 | MockStart = () => PromiseHelper.Sleep(100000, 0), | |||
|
144 | MockStop = () => AsyncPool.RunThread(stop.Wait) | |||
|
145 | }; | |||
|
146 | ||||
|
147 | var p1 = comp.Start(); | |||
|
148 | var p2 = comp.Stop(); | |||
|
149 | // should enter stopping state | |||
|
150 | ||||
|
151 | ShouldThrow(p1.Join); | |||
|
152 | Assert.IsTrue(p1.IsCancelled); | |||
|
153 | Assert.AreEqual(ExecutionState.Stopping, comp.State); | |||
|
154 | ||||
|
155 | stop.Set(); | |||
|
156 | p2.Join(1000); | |||
|
157 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |||
|
158 | } | |||
|
159 | ||||
|
160 | [TestMethod] | |||
|
161 | public void StartStopFailTest() { | |||
|
162 | var comp = new Runnable(true) { | |||
|
163 | MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); }) | |||
|
164 | }; | |||
|
165 | ||||
|
166 | comp.Start(); | |||
|
167 | var p = comp.Stop(); | |||
|
168 | // if Start fails to cancel, should fail to stop | |||
|
169 | ShouldThrow(() => p.Join(1000)); | |||
|
170 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |||
|
171 | Assert.IsNotNull(comp.LastError); | |||
|
172 | Assert.AreEqual("I'm dead", comp.LastError.Message); | |||
|
173 | } | |||
|
174 | ||||
|
175 | [TestMethod] | |||
|
176 | public void StopCancelTest() { | |||
|
177 | var comp = new Runnable(true) { | |||
|
178 | MockStop = () => PromiseHelper.Sleep(100000, 0) | |||
|
179 | }; | |||
|
180 | ||||
|
181 | comp.Start(); | |||
|
182 | var p = comp.Stop(); | |||
|
183 | Assert.AreEqual(ExecutionState.Stopping, comp.State); | |||
|
184 | p.Cancel(); | |||
|
185 | ShouldThrow(() => p.Join(1000)); | |||
|
186 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |||
|
187 | Assert.IsInstanceOfType(typeof(OperationCanceledException), comp.LastError); | |||
|
188 | ||||
|
189 | comp.Dispose(); | |||
|
190 | } | |||
|
191 | ||||
|
192 | } | |||
|
193 | } | |||
|
194 |
@@ -58,6 +58,7 | |||||
58 | <Compile Include="PromiseHelper.cs" /> |
|
58 | <Compile Include="PromiseHelper.cs" /> | |
59 | <Compile Include="Properties\AssemblyInfo.cs" /> |
|
59 | <Compile Include="Properties\AssemblyInfo.cs" /> | |
60 | <Compile Include="CancelationTests.cs" /> |
|
60 | <Compile Include="CancelationTests.cs" /> | |
|
61 | <Compile Include="RunnableComponentTests.cs" /> | |||
61 | </ItemGroup> |
|
62 | </ItemGroup> | |
62 | <ItemGroup> |
|
63 | <ItemGroup> | |
63 | <ProjectReference Include="..\Implab\Implab.csproj"> |
|
64 | <ProjectReference Include="..\Implab\Implab.csproj"> |
@@ -77,18 +77,19 namespace Implab { | |||||
77 | /// <param name="error">ΠΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π²ΠΎΠ·Π½ΠΈΠΊΡΠ΅Π΅ ΠΏΡΠΈ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ</param> |
|
77 | /// <param name="error">ΠΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π²ΠΎΠ·Π½ΠΈΠΊΡΠ΅Π΅ ΠΏΡΠΈ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ</param> | |
78 | /// <exception cref="InvalidOperationException">ΠΠ°Π½Π½ΠΎΠ΅ ΠΎΠ±Π΅ΡΠ°Π½ΠΈΠ΅ ΡΠΆΠ΅ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΎ</exception> |
|
78 | /// <exception cref="InvalidOperationException">ΠΠ°Π½Π½ΠΎΠ΅ ΠΎΠ±Π΅ΡΠ°Π½ΠΈΠ΅ ΡΠΆΠ΅ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΎ</exception> | |
79 | protected void SetError(Exception error) { |
|
79 | protected void SetError(Exception error) { | |
|
80 | while (error is PromiseTransientException) | |||
|
81 | error = error.InnerException; | |||
|
82 | ||||
|
83 | var isCancel = error is OperationCanceledException; | |||
|
84 | ||||
80 | if (BeginTransit()) { |
|
85 | if (BeginTransit()) { | |
81 |
|
|
86 | m_error = isCancel ? error.InnerException : error; | |
82 | m_error = error.InnerException; |
|
87 | CompleteTransit(isCancel ? CANCELLED_STATE : REJECTED_STATE); | |
83 | CompleteTransit(CANCELLED_STATE); |
|
88 | ||
84 | } else { |
|
|||
85 | m_error = error is PromiseTransientException ? error.InnerException : error; |
|
|||
86 | CompleteTransit(REJECTED_STATE); |
|
|||
87 | } |
|
|||
88 | Signal(); |
|
89 | Signal(); | |
89 | } else { |
|
90 | } else { | |
90 | WaitTransition(); |
|
91 | WaitTransition(); | |
91 | if (m_state == SUCCEEDED_STATE) |
|
92 | if (!isCancel || m_state == SUCCEEDED_STATE) | |
92 | throw new InvalidOperationException("The promise is already resolved"); |
|
93 | throw new InvalidOperationException("The promise is already resolved"); | |
93 | } |
|
94 | } | |
94 | } |
|
95 | } |
@@ -4,6 +4,15 namespace Implab { | |||||
4 | public class ActionChainTask : ActionChainTaskBase, IDeferred { |
|
4 | public class ActionChainTask : ActionChainTaskBase, IDeferred { | |
5 | readonly Func<IPromise> m_task; |
|
5 | readonly Func<IPromise> m_task; | |
6 |
|
6 | |||
|
7 | /// <summary> | |||
|
8 | /// Initializes a new instance of the <see cref="Implab.ActionChainTask"/> class. | |||
|
9 | /// </summary> | |||
|
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> | |||
|
12 | /// <param name="cancel">The cancellation handler.</param> | |||
|
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()"/>, | |||
|
15 | /// after that all requests are directed to the task.</param> | |||
7 | 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) { | |
8 | m_task = task; |
|
17 | m_task = task; | |
9 | } |
|
18 | } |
@@ -1,8 +1,7 | |||||
1 | using System; |
|
1 | using System; | |
2 | using Implab.Formats; |
|
|||
3 |
|
2 | |||
4 | namespace Implab.Components { |
|
3 | namespace Implab.Components { | |
5 | public class RunnableComponent : Disposable, IRunnable, IInitializable { |
|
4 | public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable { | |
6 | enum Commands { |
|
5 | enum Commands { | |
7 | Ok = 0, |
|
6 | Ok = 0, | |
8 | Fail, |
|
7 | Fail, | |
@@ -19,8 +18,11 namespace Implab.Components { | |||||
19 | static StateMachine() { |
|
18 | static StateMachine() { | |
20 | _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; |
|
19 | _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; | |
21 |
|
20 | |||
22 |
Edge(ExecutionState.Created, ExecutionState. |
|
21 | Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init); | |
23 |
Edge(ExecutionState.Created, ExecutionState. |
|
22 | Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose); | |
|
23 | ||||
|
24 | Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok); | |||
|
25 | Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail); | |||
24 |
|
26 | |||
25 | Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); |
|
27 | Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); | |
26 | Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); |
|
28 | Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); | |
@@ -36,7 +38,8 namespace Implab.Components { | |||||
36 |
|
38 | |||
37 | Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); |
|
39 | Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); | |
38 | Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); |
|
40 | Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); | |
39 | Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose); |
|
41 | ||
|
42 | Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose); | |||
40 | } |
|
43 | } | |
41 |
|
44 | |||
42 | static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { |
|
45 | static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { | |
@@ -70,72 +73,93 namespace Implab.Components { | |||||
70 | m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); |
|
73 | m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); | |
71 | } |
|
74 | } | |
72 |
|
75 | |||
|
76 | protected virtual int DisposeTimeout { | |||
|
77 | get { | |||
|
78 | return 10000; | |||
|
79 | } | |||
|
80 | } | |||
|
81 | ||||
73 | void ThrowInvalidCommand(Commands cmd) { |
|
82 | void ThrowInvalidCommand(Commands cmd) { | |
|
83 | if (m_stateMachine.State == ExecutionState.Disposed) | |||
|
84 | throw new ObjectDisposedException(ToString()); | |||
|
85 | ||||
74 | throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); |
|
86 | throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); | |
75 | } |
|
87 | } | |
76 |
|
88 | |||
77 |
|
|
89 | void Move(Commands cmd) { | |
78 |
|
|
90 | if (!m_stateMachine.Move(cmd)) | |
79 | if (!m_stateMachine.Move(cmd)) |
|
91 | ThrowInvalidCommand(cmd); | |
80 | ThrowInvalidCommand(cmd); |
|
|||
81 | } |
|
92 | } | |
82 |
|
93 | |||
83 | protected void Fail(Exception err) { |
|
94 | void Invoke(Commands cmd, Action action) { | |
84 |
lock (m_stateMachine) |
|
95 | lock (m_stateMachine) | |
85 | if (!m_stateMachine.Move(Commands.Fail)) |
|
96 | Move(cmd); | |
86 | ThrowInvalidCommand(Commands.Fail); |
|
97 | ||
87 |
|
||||
88 | m_lastError = err; |
|
|||
89 | } |
|
|||
90 | } |
|
|||
91 |
|
||||
92 | protected void Success() { |
|
|||
93 | Move(Commands.Ok); |
|
|||
94 | } |
|
|||
95 |
|
||||
96 | protected void Invoke(Commands cmd, Action action) { |
|
|||
97 | Move(cmd); |
|
|||
98 | try { |
|
98 | try { | |
99 | action(); |
|
99 | action(); | |
100 | Move(Commands.Ok); |
|
100 | lock(m_stateMachine) | |
|
101 | Move(Commands.Ok); | |||
|
102 | ||||
101 | } catch (Exception err) { |
|
103 | } catch (Exception err) { | |
102 |
|
|
104 | lock (m_stateMachine) { | |
|
105 | Move(Commands.Fail); | |||
|
106 | m_lastError = err; | |||
|
107 | } | |||
103 | throw; |
|
108 | throw; | |
104 | } |
|
109 | } | |
105 | } |
|
110 | } | |
106 |
|
111 | |||
107 |
|
|
112 | IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) { | |
108 | Move(cmd); |
|
113 | IPromise promise = null; | |
109 |
|
|
114 | IPromise prev; | |
110 |
|
115 | |||
111 | IPromise promise = null; |
|
116 | var task = new ActionChainTask(action, null, null, true); | |
|
117 | ||||
|
118 | lock (m_stateMachine) { | |||
|
119 | Move(cmd); | |||
|
120 | ||||
|
121 | prev = m_pending; | |||
112 |
|
122 | |||
113 |
promise = |
|
123 | promise = task.Then( | |
114 | () => { |
|
124 | () => { | |
115 | lock(m_stateMachine) { |
|
125 | lock(m_stateMachine) { | |
116 | if (m_pending == promise) { |
|
126 | if (m_pending == promise) { | |
117 |
|
|
127 | Move(Commands.Ok); | |
118 |
|
|
128 | m_pending = null; | |
|
129 | } | |||
119 | } |
|
130 | } | |
|
131 | }, e => { | |||
|
132 | lock(m_stateMachine) { | |||
|
133 | if (m_pending == promise) { | |||
|
134 | Move(Commands.Fail); | |||
|
135 | m_pending = null; | |||
|
136 | m_lastError = e; | |||
|
137 | } | |||
|
138 | } | |||
|
139 | throw new PromiseTransientException(e); | |||
|
140 | }, | |||
|
141 | r => { | |||
|
142 | lock(m_stateMachine) { | |||
|
143 | if (m_pending == promise) { | |||
|
144 | Move(Commands.Fail); | |||
|
145 | m_pending = null; | |||
|
146 | m_lastError = new OperationCanceledException("The operation has been cancelled", r); | |||
|
147 | } | |||
|
148 | ||||
|
149 | } | |||
|
150 | throw new OperationCanceledException("The operation has been cancelled", r); | |||
120 | } |
|
151 | } | |
121 |
|
|
152 | ); | |
122 | if (m_pending == promise) { |
|
|||
123 | m_pending = null; |
|
|||
124 | Fail( |
|
|||
125 | } |
|
|||
126 | } |
|
|||
127 | ); |
|
|||
128 |
|
153 | |||
|
154 | m_pending = promise; | |||
|
155 | } | |||
129 |
|
156 | |||
|
157 | if (prev == null) | |||
|
158 | task.Resolve(); | |||
|
159 | else | |||
|
160 | chain(prev, task); | |||
130 |
|
161 | |||
131 |
return |
|
162 | return promise; | |
132 | Success, |
|
|||
133 | Fail |
|
|||
134 | ); |
|
|||
135 | } |
|
|||
136 |
|
||||
137 | void AddPending(IPromise result) { |
|
|||
138 |
|
||||
139 | } |
|
163 | } | |
140 |
|
164 | |||
141 |
|
165 | |||
@@ -153,43 +177,86 namespace Implab.Components { | |||||
153 | #region IRunnable implementation |
|
177 | #region IRunnable implementation | |
154 |
|
178 | |||
155 | public IPromise Start() { |
|
179 | public IPromise Start() { | |
156 |
|
|
180 | return InvokeAsync(Commands.Start, OnStart, null); | |
157 |
|
||||
158 | return Safe.InvokePromise(OnStart).Then( |
|
|||
159 | () => { |
|
|||
160 | Move(Commands.Ok); |
|
|||
161 | Run(); |
|
|||
162 | }, |
|
|||
163 | () => { |
|
|||
164 | Move(Commands.Fail); |
|
|||
165 | } |
|
|||
166 | ); |
|
|||
167 | } |
|
181 | } | |
168 |
|
182 | |||
169 | protected virtual IPromise OnStart() { |
|
183 | protected virtual IPromise OnStart() { | |
170 | return Promise.SUCCESS; |
|
184 | return Promise.SUCCESS; | |
171 | } |
|
185 | } | |
172 |
|
186 | |||
173 | protected virtual void Run() { |
|
187 | public IPromise Stop() { | |
|
188 | return InvokeAsync(Commands.Stop, OnStop, StopPending).Then(Dispose); | |||
|
189 | } | |||
|
190 | ||||
|
191 | protected virtual IPromise OnStop() { | |||
|
192 | return Promise.SUCCESS; | |||
174 | } |
|
193 | } | |
175 |
|
194 | |||
176 | public IPromise Stop() { |
|
195 | /// <summary> | |
177 | throw new NotImplementedException(); |
|
196 | /// Stops the current operation if one exists. | |
|
197 | /// </summary> | |||
|
198 | /// <param name="current">Current.</param> | |||
|
199 | /// <param name="stop">Stop.</param> | |||
|
200 | protected virtual void StopPending(IPromise current, IDeferred stop) { | |||
|
201 | if (current == null) { | |||
|
202 | stop.Resolve(); | |||
|
203 | } else { | |||
|
204 | current.On(stop.Resolve, stop.Reject, stop.CancelOperation); | |||
|
205 | current.Cancel(); | |||
|
206 | } | |||
178 | } |
|
207 | } | |
179 |
|
208 | |||
180 | public ExecutionState State { |
|
209 | public ExecutionState State { | |
181 | get { |
|
210 | get { | |
182 | throw new NotImplementedException(); |
|
211 | return m_stateMachine.State; | |
183 | } |
|
212 | } | |
184 | } |
|
213 | } | |
185 |
|
214 | |||
186 | public Exception LastError { |
|
215 | public Exception LastError { | |
187 | get { |
|
216 | get { | |
188 | throw new NotImplementedException(); |
|
217 | return m_lastError; | |
189 | } |
|
218 | } | |
190 | } |
|
219 | } | |
191 |
|
220 | |||
192 | #endregion |
|
221 | #endregion | |
|
222 | ||||
|
223 | #region IDisposable implementation | |||
|
224 | ||||
|
225 | public void Dispose() { | |||
|
226 | IPromise pending; | |||
|
227 | lock (m_stateMachine) { | |||
|
228 | if (m_stateMachine.State == ExecutionState.Disposed) | |||
|
229 | return; | |||
|
230 | ||||
|
231 | Move(Commands.Dispose); | |||
|
232 | ||||
|
233 | GC.SuppressFinalize(this); | |||
|
234 | ||||
|
235 | pending = m_pending; | |||
|
236 | m_pending = null; | |||
|
237 | } | |||
|
238 | if (pending != null) { | |||
|
239 | pending.Cancel(); | |||
|
240 | pending.Timeout(DisposeTimeout).On( | |||
|
241 | () => Dispose(true, null), | |||
|
242 | err => Dispose(true, err), | |||
|
243 | reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason)) | |||
|
244 | ); | |||
|
245 | } else { | |||
|
246 | Dispose(true, m_lastError); | |||
|
247 | } | |||
|
248 | } | |||
|
249 | ||||
|
250 | ~RunnableComponent() { | |||
|
251 | Dispose(false, null); | |||
|
252 | } | |||
|
253 | ||||
|
254 | #endregion | |||
|
255 | ||||
|
256 | protected virtual void Dispose(bool disposing, Exception lastError) { | |||
|
257 | ||||
|
258 | } | |||
|
259 | ||||
193 | } |
|
260 | } | |
194 | } |
|
261 | } | |
195 |
|
262 |
@@ -3,11 +3,6 using System; | |||||
3 | using Implab.Diagnostics; |
|
3 | using Implab.Diagnostics; | |
4 | using System.Collections.Generic; |
|
4 | using System.Collections.Generic; | |
5 |
|
5 | |||
6 |
|
||||
7 | #if NET_4_5 |
|
|||
8 | using System.Threading.Tasks; |
|
|||
9 | #endif |
|
|||
10 |
|
||||
11 | namespace Implab { |
|
6 | namespace Implab { | |
12 | public static class PromiseExtensions { |
|
7 | public static class PromiseExtensions { | |
13 | public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) { |
|
8 | public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) { | |
@@ -17,12 +12,12 namespace Implab { | |||||
17 | return that; |
|
12 | return that; | |
18 |
|
13 | |||
19 | var p = new SyncContextPromise<T>(context); |
|
14 | var p = new SyncContextPromise<T>(context); | |
20 | p.On(that.Cancel, PromiseEventType.Cancelled); |
|
15 | p.CancellationRequested(that.Cancel); | |
21 |
|
16 | |||
22 | that.On( |
|
17 | that.On( | |
23 | p.Resolve, |
|
18 | p.Resolve, | |
24 | p.Reject, |
|
19 | p.Reject, | |
25 | p.Cancel |
|
20 | p.CancelOperation | |
26 | ); |
|
21 | ); | |
27 | return p; |
|
22 | return p; | |
28 | } |
|
23 | } | |
@@ -32,13 +27,12 namespace Implab { | |||||
32 | Safe.ArgumentNotNull(context, "context"); |
|
27 | Safe.ArgumentNotNull(context, "context"); | |
33 |
|
28 | |||
34 | var p = new SyncContextPromise<T>(context); |
|
29 | var p = new SyncContextPromise<T>(context); | |
35 | p.On(that.Cancel, PromiseEventType.Cancelled); |
|
30 | p.CancellationRequested(that.Cancel); | |
36 |
|
||||
37 |
|
31 | |||
38 | that.On( |
|
32 | that.On( | |
39 | p.Resolve, |
|
33 | p.Resolve, | |
40 | p.Reject, |
|
34 | p.Reject, | |
41 | p.Cancel |
|
35 | p.CancelOperation | |
42 | ); |
|
36 | ); | |
43 | return p; |
|
37 | return p; | |
44 | } |
|
38 | } | |
@@ -77,8 +71,8 namespace Implab { | |||||
77 | }; |
|
71 | }; | |
78 | } |
|
72 | } | |
79 |
|
73 | |||
80 | static void CancelCallback(object cookie) { |
|
74 | static void CancelByTimeoutCallback(object cookie) { | |
81 | ((ICancellable)cookie).Cancel(); |
|
75 | ((ICancellable)cookie).Cancel(new TimeoutException()); | |
82 | } |
|
76 | } | |
83 |
|
77 | |||
84 | /// <summary> |
|
78 | /// <summary> | |
@@ -89,7 +83,7 namespace Implab { | |||||
89 | /// <typeparam name="TPromise">The 1st type parameter.</typeparam> |
|
83 | /// <typeparam name="TPromise">The 1st type parameter.</typeparam> | |
90 | public static TPromise Timeout<TPromise>(this TPromise that, int milliseconds) where TPromise : IPromise { |
|
84 | public static TPromise Timeout<TPromise>(this TPromise that, int milliseconds) where TPromise : IPromise { | |
91 | Safe.ArgumentNotNull(that, "that"); |
|
85 | Safe.ArgumentNotNull(that, "that"); | |
92 | var timer = new Timer(CancelCallback, that, milliseconds, -1); |
|
86 | var timer = new Timer(CancelByTimeoutCallback, that, milliseconds, -1); | |
93 | that.On(timer.Dispose, PromiseEventType.All); |
|
87 | that.On(timer.Dispose, PromiseEventType.All); | |
94 | return that; |
|
88 | return that; | |
95 | } |
|
89 | } | |
@@ -180,8 +174,7 namespace Implab { | |||||
180 |
|
174 | |||
181 | var d = new ActionTask(success, error, cancel, false); |
|
175 | var d = new ActionTask(success, error, cancel, false); | |
182 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
176 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
183 | if (success != null) |
|
177 | d.CancellationRequested(that.Cancel); | |
184 | d.CancellationRequested(that.Cancel); |
|
|||
185 | return d; |
|
178 | return d; | |
186 | } |
|
179 | } | |
187 |
|
180 | |||
@@ -198,8 +191,7 namespace Implab { | |||||
198 |
|
191 | |||
199 | var d = new FuncTask<T>(success, error, cancel, false); |
|
192 | var d = new FuncTask<T>(success, error, cancel, false); | |
200 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
193 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
201 | if (success != null) |
|
194 | d.CancellationRequested(that.Cancel); | |
202 | d.CancellationRequested(that.Cancel); |
|
|||
203 | return d; |
|
195 | return d; | |
204 | } |
|
196 | } | |
205 |
|
197 | |||
@@ -215,8 +207,7 namespace Implab { | |||||
215 | Safe.ArgumentNotNull(that, "that"); |
|
207 | Safe.ArgumentNotNull(that, "that"); | |
216 | var d = new FuncTask<T,T2>(success, error, cancel, false); |
|
208 | var d = new FuncTask<T,T2>(success, error, cancel, false); | |
217 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
209 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
218 | if (success != null) |
|
210 | d.CancellationRequested(that.Cancel); | |
219 | d.CancellationRequested(that.Cancel); |
|
|||
220 | return d; |
|
211 | return d; | |
221 | } |
|
212 | } | |
222 |
|
213 | |||
@@ -234,8 +225,7 namespace Implab { | |||||
234 |
|
225 | |||
235 | var d = new ActionChainTask(success, error, cancel, false); |
|
226 | var d = new ActionChainTask(success, error, cancel, false); | |
236 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
227 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
237 | if (success != null) |
|
228 | d.CancellationRequested(that.Cancel); | |
238 | d.CancellationRequested(that.Cancel); |
|
|||
239 | return d; |
|
229 | return d; | |
240 | } |
|
230 | } | |
241 |
|
231 |
General Comments 0
You need to be logged in to leave comments.
Login now