Auto status change to "Under Review"
@@ -1,52 +1,52 | |||||
1 | using System; |
|
1 | using System; | |
2 | using Implab.Components; |
|
2 | using Implab.Components; | |
3 |
|
3 | |||
4 | namespace Implab.Test.Mock { |
|
4 | namespace Implab.Test.Mock { | |
5 | class MockRunnableComponent : RunnableComponent { |
|
5 | class MockRunnableComponent : RunnableComponent { | |
6 | public MockRunnableComponent(bool initialized) : base(initialized) { |
|
6 | public MockRunnableComponent(bool initialized) : base(initialized) { | |
7 | } |
|
7 | } | |
8 |
|
8 | |||
9 | public MockRunnableComponent(bool initialized, bool reusable) : base(initialized, reusable) { |
|
9 | public MockRunnableComponent(bool initialized, bool reusable) : base(initialized, reusable) { | |
10 | } |
|
10 | } | |
11 |
|
11 | |||
12 | public Action MockInit { |
|
12 | public Action MockInit { | |
13 | get; |
|
13 | get; | |
14 | set; |
|
14 | set; | |
15 | } |
|
15 | } | |
16 |
|
16 | |||
17 | public Func<IPromise> MockStart { |
|
17 | public Func<IPromise> MockStart { | |
18 | get; |
|
18 | get; | |
19 | set; |
|
19 | set; | |
20 | } |
|
20 | } | |
21 |
|
21 | |||
22 | public Func<IPromise> MockStop { |
|
22 | public Func<IPromise> MockStop { | |
23 | get; |
|
23 | get; | |
24 | set; |
|
24 | set; | |
25 | } |
|
25 | } | |
26 |
|
26 | |||
27 |
public Action<bool |
|
27 | public Action<bool> MockDispose { | |
28 | get; |
|
28 | get; | |
29 | set; |
|
29 | set; | |
30 | } |
|
30 | } | |
31 |
|
31 | |||
32 | protected override IPromise OnStart() { |
|
32 | protected override IPromise OnStart() { | |
33 | return MockStart != null ? Safe.Run(MockStart).Chain(base.OnStart) : Safe.Run(base.OnStart); |
|
33 | return MockStart != null ? Safe.Run(MockStart).Chain(base.OnStart) : Safe.Run(base.OnStart); | |
34 | } |
|
34 | } | |
35 |
|
35 | |||
36 | protected override IPromise OnStop() { |
|
36 | protected override IPromise OnStop() { | |
37 | return MockStop != null ? Safe.Run(MockStop).Chain(base.OnStop) : Safe.Run(base.OnStop); |
|
37 | return MockStop != null ? Safe.Run(MockStop).Chain(base.OnStop) : Safe.Run(base.OnStop); | |
38 | } |
|
38 | } | |
39 |
|
39 | |||
40 | protected override void OnInitialize() { |
|
40 | protected override void OnInitialize() { | |
41 | if (MockInit != null) |
|
41 | if (MockInit != null) | |
42 | MockInit(); |
|
42 | MockInit(); | |
43 | } |
|
43 | } | |
44 |
|
44 | |||
45 |
protected override void Dispose(bool disposing |
|
45 | protected override void Dispose(bool disposing) { | |
46 | if (MockDispose != null) |
|
46 | if (MockDispose != null) | |
47 |
MockDispose(disposing |
|
47 | MockDispose(disposing); | |
48 |
base.Dispose(disposing |
|
48 | base.Dispose(disposing); | |
49 | } |
|
49 | } | |
50 | } |
|
50 | } | |
51 | } |
|
51 | } | |
52 |
|
52 |
@@ -1,230 +1,228 | |||||
1 | using System; |
|
1 | using System; | |
2 | using System.Reflection; |
|
2 | using System.Reflection; | |
3 | using System.Threading; |
|
3 | using System.Threading; | |
4 | using Implab.Parallels; |
|
4 | using Implab.Parallels; | |
5 | using Implab.Components; |
|
5 | using Implab.Components; | |
6 | using Implab.Test.Mock; |
|
6 | using Implab.Test.Mock; | |
7 |
|
7 | |||
8 | #if MONO |
|
8 | #if MONO | |
9 |
|
9 | |||
10 | using NUnit.Framework; |
|
10 | using NUnit.Framework; | |
11 | using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; |
|
11 | using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; | |
12 | using TestMethodAttribute = NUnit.Framework.TestAttribute; |
|
12 | using TestMethodAttribute = NUnit.Framework.TestAttribute; | |
13 | using AssertFailedException = NUnit.Framework.AssertionException; |
|
13 | using AssertFailedException = NUnit.Framework.AssertionException; | |
14 | #else |
|
14 | #else | |
15 |
|
15 | |||
16 | using Microsoft.VisualStudio.TestTools.UnitTesting; |
|
16 | using Microsoft.VisualStudio.TestTools.UnitTesting; | |
17 |
|
17 | |||
18 | #endif |
|
18 | #endif | |
19 |
|
19 | |||
20 | namespace Implab.Test { |
|
20 | namespace Implab.Test { | |
21 | [TestClass] |
|
21 | [TestClass] | |
22 | public class RunnableComponentTests { |
|
22 | public class RunnableComponentTests { | |
23 |
|
23 | |||
24 | static void ShouldThrow(Action action) { |
|
24 | static void ShouldThrow(Action action) { | |
25 | try { |
|
25 | try { | |
26 | action(); |
|
26 | action(); | |
27 | Assert.Fail(); |
|
27 | Assert.Fail(); | |
28 | } catch (AssertFailedException) { |
|
28 | } catch (AssertFailedException) { | |
29 | throw; |
|
29 | throw; | |
30 | } catch { |
|
30 | } catch { | |
31 | } |
|
31 | } | |
32 | } |
|
32 | } | |
33 |
|
33 | |||
34 |
|
34 | |||
35 |
|
35 | |||
36 | [TestMethod] |
|
36 | [TestMethod] | |
37 | public void NormalFlowTest() { |
|
37 | public void NormalFlowTest() { | |
38 | var comp = new MockRunnableComponent(false); |
|
38 | var comp = new MockRunnableComponent(false); | |
39 |
|
39 | |||
40 | Assert.AreEqual(ExecutionState.Created, comp.State); |
|
40 | Assert.AreEqual(ExecutionState.Created, comp.State); | |
41 |
|
41 | |||
42 | comp.Initialize(); |
|
42 | comp.Initialize(); | |
43 |
|
43 | |||
44 | Assert.AreEqual(ExecutionState.Ready, comp.State); |
|
44 | Assert.AreEqual(ExecutionState.Ready, comp.State); | |
45 |
|
45 | |||
46 | comp.Start().Join(1000); |
|
46 | comp.Start().Join(1000); | |
47 |
|
47 | |||
48 | Assert.AreEqual(ExecutionState.Running, comp.State); |
|
48 | Assert.AreEqual(ExecutionState.Running, comp.State); | |
49 |
|
49 | |||
50 | comp.Stop().Join(1000); |
|
50 | comp.Stop().Join(1000); | |
51 |
|
51 | |||
52 | Assert.AreEqual(ExecutionState.Disposed, comp.State); |
|
52 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |
53 |
|
53 | |||
54 | } |
|
54 | } | |
55 |
|
55 | |||
56 | [TestMethod] |
|
56 | [TestMethod] | |
57 | public void InitFailTest() { |
|
57 | public void InitFailTest() { | |
58 | var comp = new MockRunnableComponent(false) { |
|
58 | var comp = new MockRunnableComponent(false) { | |
59 | MockInit = () => { |
|
59 | MockInit = () => { | |
60 | throw new Exception("BAD"); |
|
60 | throw new Exception("BAD"); | |
61 | } |
|
61 | } | |
62 | }; |
|
62 | }; | |
63 |
|
63 | |||
64 | ShouldThrow(() => comp.Start()); |
|
64 | ShouldThrow(() => comp.Start()); | |
65 | ShouldThrow(() => comp.Stop()); |
|
65 | ShouldThrow(() => comp.Stop()); | |
66 | Assert.AreEqual(ExecutionState.Created, comp.State); |
|
66 | Assert.AreEqual(ExecutionState.Created, comp.State); | |
67 |
|
67 | |||
68 | ShouldThrow(comp.Initialize); |
|
68 | ShouldThrow(comp.Initialize); | |
69 |
|
69 | |||
70 | Assert.AreEqual(ExecutionState.Failed, comp.State); |
|
70 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |
71 |
|
71 | |||
72 | ShouldThrow(() => comp.Start()); |
|
72 | ShouldThrow(() => comp.Start()); | |
73 | ShouldThrow(() => comp.Stop()); |
|
73 | ShouldThrow(() => comp.Stop()); | |
74 | Assert.AreEqual(ExecutionState.Failed, comp.State); |
|
74 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |
75 |
|
75 | |||
76 | comp.Dispose(); |
|
76 | comp.Dispose(); | |
77 | Assert.AreEqual(ExecutionState.Disposed, comp.State); |
|
77 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |
78 | } |
|
78 | } | |
79 |
|
79 | |||
80 | [TestMethod] |
|
80 | [TestMethod] | |
81 | public void DisposedTest() { |
|
81 | public void DisposedTest() { | |
82 |
|
82 | |||
83 | var comp = new MockRunnableComponent(false); |
|
83 | var comp = new MockRunnableComponent(false); | |
84 | comp.Dispose(); |
|
84 | comp.Dispose(); | |
85 |
|
85 | |||
86 | ShouldThrow(() => comp.Start()); |
|
86 | ShouldThrow(() => comp.Start()); | |
87 | ShouldThrow(() => comp.Stop()); |
|
87 | ShouldThrow(() => comp.Stop()); | |
88 | ShouldThrow(comp.Initialize); |
|
88 | ShouldThrow(comp.Initialize); | |
89 |
|
89 | |||
90 | Assert.AreEqual(ExecutionState.Disposed, comp.State); |
|
90 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |
91 | } |
|
91 | } | |
92 |
|
92 | |||
93 | [TestMethod] |
|
93 | [TestMethod] | |
94 | public void ShouldCallDisposeOnStop() { |
|
94 | public void ShouldCallDisposeOnStop() { | |
95 | var comp = new MockRunnableComponent(true); |
|
95 | var comp = new MockRunnableComponent(true); | |
96 |
|
96 | |||
97 | bool disposed = false; |
|
97 | bool disposed = false; | |
98 |
comp.MockDispose = (disposing |
|
98 | comp.MockDispose = (disposing) => { | |
99 | disposed = true; |
|
99 | disposed = true; | |
100 | }; |
|
100 | }; | |
101 |
|
101 | |||
102 | comp.Start().Join(1000); |
|
102 | comp.Start().Join(1000); | |
103 | comp.Stop().Join(1000); |
|
103 | comp.Stop().Join(1000); | |
104 |
|
104 | |||
105 | ShouldThrow(() => comp.Start()); |
|
105 | ShouldThrow(() => comp.Start()); | |
106 | ShouldThrow(() => comp.Stop()); |
|
106 | ShouldThrow(() => comp.Stop()); | |
107 | ShouldThrow(comp.Initialize); |
|
107 | ShouldThrow(comp.Initialize); | |
108 |
|
108 | |||
109 | Assert.AreEqual(ExecutionState.Disposed, comp.State); |
|
109 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |
110 | Assert.IsTrue(disposed); |
|
110 | Assert.IsTrue(disposed); | |
111 | } |
|
111 | } | |
112 |
|
112 | |||
113 | [TestMethod] |
|
113 | [TestMethod] | |
114 | public void ShouldNotCallDisposeOnStop() { |
|
114 | public void ShouldNotCallDisposeOnStop() { | |
115 | var comp = new MockRunnableComponent(true, true); |
|
115 | var comp = new MockRunnableComponent(true, true); | |
116 |
|
116 | |||
117 | bool disposed = false; |
|
117 | bool disposed = false; | |
118 |
comp.MockDispose = (disposing |
|
118 | comp.MockDispose = (disposing) => { | |
119 | disposed = true; |
|
119 | disposed = true; | |
120 | }; |
|
120 | }; | |
121 |
|
121 | |||
122 | comp.Start().Join(1000); |
|
122 | comp.Start().Join(1000); | |
123 | comp.Stop().Join(1000); |
|
123 | comp.Stop().Join(1000); | |
124 |
|
124 | |||
125 | Assert.AreEqual(ExecutionState.Ready, comp.State); |
|
125 | Assert.AreEqual(ExecutionState.Ready, comp.State); | |
126 | Assert.IsFalse(disposed); |
|
126 | Assert.IsFalse(disposed); | |
127 | } |
|
127 | } | |
128 |
|
128 | |||
129 | [TestMethod] |
|
129 | [TestMethod] | |
130 | public void SelfDisposeOnStop() { |
|
130 | public void SelfDisposeOnStop() { | |
131 | var comp = new MockRunnableComponent(true, true); |
|
131 | var comp = new MockRunnableComponent(true, true); | |
132 |
|
132 | |||
133 | bool disposed = false; |
|
133 | bool disposed = false; | |
134 | Exception lastError = null; |
|
134 | comp.MockDispose = (disposing) => { | |
135 | comp.MockDispose = (disposing, error) => { |
|
|||
136 | disposed = true; |
|
135 | disposed = true; | |
137 | lastError = error; |
|
|||
138 | }; |
|
136 | }; | |
139 |
|
137 | |||
140 | comp.Start().Join(1000); |
|
138 | comp.Start().Join(1000); | |
141 | comp.Stop().Join(1000); |
|
139 | comp.Stop().Join(1000); | |
142 |
|
140 | |||
143 | Assert.AreEqual(ExecutionState.Ready, comp.State); |
|
141 | Assert.AreEqual(ExecutionState.Ready, comp.State); | |
144 | Assert.IsFalse(disposed); |
|
142 | Assert.IsFalse(disposed); | |
145 |
|
143 | |||
146 | comp.MockStop = () => { |
|
144 | comp.MockStop = () => { | |
147 | comp.Dispose(); |
|
145 | comp.Dispose(); | |
148 | return Promise.Success; |
|
146 | return Promise.Success; | |
149 | }; |
|
147 | }; | |
150 |
|
148 | |||
151 | comp.Start().Join(1000); |
|
149 | comp.Start().Join(1000); | |
152 | comp.Stop().Join(1000); |
|
150 | comp.Stop().Join(1000); | |
153 |
|
151 | |||
154 | Assert.AreEqual(ExecutionState.Disposed, comp.State); |
|
152 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |
155 | Assert.IsTrue(disposed); |
|
153 | Assert.IsTrue(disposed); | |
156 | } |
|
154 | } | |
157 |
|
155 | |||
158 | [TestMethod] |
|
156 | [TestMethod] | |
159 | public void StartCancelTest() { |
|
157 | public void StartCancelTest() { | |
160 | var comp = new MockRunnableComponent(true) { |
|
158 | var comp = new MockRunnableComponent(true) { | |
161 | MockStart = () => PromiseHelper.Sleep(100000, 0) |
|
159 | MockStart = () => PromiseHelper.Sleep(100000, 0) | |
162 | }; |
|
160 | }; | |
163 |
|
161 | |||
164 | var p = comp.Start(); |
|
162 | var p = comp.Start(); | |
165 | Assert.AreEqual(ExecutionState.Starting, comp.State); |
|
163 | Assert.AreEqual(ExecutionState.Starting, comp.State); | |
166 | p.Cancel(); |
|
164 | p.Cancel(); | |
167 | ShouldThrow(() => p.Join(1000)); |
|
165 | ShouldThrow(() => p.Join(1000)); | |
168 | Assert.AreEqual(ExecutionState.Failed, comp.State); |
|
166 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |
169 |
|
167 | |||
170 | Assert.IsTrue(comp.LastError is OperationCanceledException); |
|
168 | Assert.IsTrue(comp.LastError is OperationCanceledException); | |
171 |
|
169 | |||
172 | comp.Dispose(); |
|
170 | comp.Dispose(); | |
173 | } |
|
171 | } | |
174 |
|
172 | |||
175 | [TestMethod] |
|
173 | [TestMethod] | |
176 | public void StartStopTest() { |
|
174 | public void StartStopTest() { | |
177 | var stop = new Signal(); |
|
175 | var stop = new Signal(); | |
178 | var comp = new MockRunnableComponent(true) { |
|
176 | var comp = new MockRunnableComponent(true) { | |
179 | MockStart = () => PromiseHelper.Sleep(100000, 0), |
|
177 | MockStart = () => PromiseHelper.Sleep(100000, 0), | |
180 | MockStop = () => AsyncPool.RunThread(stop.Wait) |
|
178 | MockStop = () => AsyncPool.RunThread(stop.Wait) | |
181 | }; |
|
179 | }; | |
182 |
|
180 | |||
183 | var p1 = comp.Start(); |
|
181 | var p1 = comp.Start(); | |
184 | var p2 = comp.Stop(); |
|
182 | var p2 = comp.Stop(); | |
185 | // should enter stopping state |
|
183 | // should enter stopping state | |
186 |
|
184 | |||
187 | ShouldThrow(p1.Join); |
|
185 | ShouldThrow(p1.Join); | |
188 | Assert.IsTrue(p1.IsCancelled); |
|
186 | Assert.IsTrue(p1.IsCancelled); | |
189 | Assert.AreEqual(ExecutionState.Stopping, comp.State); |
|
187 | Assert.AreEqual(ExecutionState.Stopping, comp.State); | |
190 |
|
188 | |||
191 | stop.Set(); |
|
189 | stop.Set(); | |
192 | p2.Join(1000); |
|
190 | p2.Join(1000); | |
193 | Assert.AreEqual(ExecutionState.Disposed, comp.State); |
|
191 | Assert.AreEqual(ExecutionState.Disposed, comp.State); | |
194 | } |
|
192 | } | |
195 |
|
193 | |||
196 | [TestMethod] |
|
194 | [TestMethod] | |
197 | public void StartStopFailTest() { |
|
195 | public void StartStopFailTest() { | |
198 | var comp = new MockRunnableComponent(true) { |
|
196 | var comp = new MockRunnableComponent(true) { | |
199 | MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); }) |
|
197 | MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); }) | |
200 | }; |
|
198 | }; | |
201 |
|
199 | |||
202 | comp.Start(); |
|
200 | comp.Start(); | |
203 | var p = comp.Stop(); |
|
201 | var p = comp.Stop(); | |
204 | // if Start fails to cancel, should fail to stop |
|
202 | // if Start fails to cancel, should fail to stop | |
205 | ShouldThrow(() => p.Join(1000)); |
|
203 | ShouldThrow(() => p.Join(1000)); | |
206 | Assert.AreEqual(ExecutionState.Failed, comp.State); |
|
204 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |
207 | Assert.IsNotNull(comp.LastError); |
|
205 | Assert.IsNotNull(comp.LastError); | |
208 | Assert.AreEqual("I'm dead", comp.LastError.Message); |
|
206 | Assert.AreEqual("I'm dead", comp.LastError.Message); | |
209 | } |
|
207 | } | |
210 |
|
208 | |||
211 | [TestMethod] |
|
209 | [TestMethod] | |
212 | public void StopCancelTest() { |
|
210 | public void StopCancelTest() { | |
213 | var comp = new MockRunnableComponent(true) { |
|
211 | var comp = new MockRunnableComponent(true) { | |
214 | MockStop = () => PromiseHelper.Sleep(100000, 0) |
|
212 | MockStop = () => PromiseHelper.Sleep(100000, 0) | |
215 | }; |
|
213 | }; | |
216 |
|
214 | |||
217 | comp.Start(); |
|
215 | comp.Start(); | |
218 | var p = comp.Stop(); |
|
216 | var p = comp.Stop(); | |
219 | Assert.AreEqual(ExecutionState.Stopping, comp.State); |
|
217 | Assert.AreEqual(ExecutionState.Stopping, comp.State); | |
220 | p.Cancel(); |
|
218 | p.Cancel(); | |
221 | ShouldThrow(() => p.Join(1000)); |
|
219 | ShouldThrow(() => p.Join(1000)); | |
222 | Assert.AreEqual(ExecutionState.Failed, comp.State); |
|
220 | Assert.AreEqual(ExecutionState.Failed, comp.State); | |
223 | Assert.IsTrue(comp.LastError is OperationCanceledException); |
|
221 | Assert.IsTrue(comp.LastError is OperationCanceledException); | |
224 |
|
222 | |||
225 | comp.Dispose(); |
|
223 | comp.Dispose(); | |
226 | } |
|
224 | } | |
227 |
|
225 | |||
228 | } |
|
226 | } | |
229 | } |
|
227 | } | |
230 |
|
228 |
@@ -1,103 +1,105 | |||||
1 | using Implab.Diagnostics; |
|
1 | using Implab.Diagnostics; | |
2 | using System; |
|
2 | using System; | |
|
3 | using System.Diagnostics.CodeAnalysis; | |||
3 | using System.Threading; |
|
4 | using System.Threading; | |
4 |
|
5 | |||
5 | namespace Implab.Components { |
|
6 | namespace Implab.Components { | |
6 | /// <summary> |
|
7 | /// <summary> | |
7 | /// Base class the objects which support disposing. |
|
8 | /// Base class the objects which support disposing. | |
8 | /// </summary> |
|
9 | /// </summary> | |
9 | public class Disposable : IDisposable { |
|
10 | public class Disposable : IDisposable { | |
10 |
|
11 | |||
11 | int m_disposed; |
|
12 | int m_disposed; | |
12 |
|
13 | |||
13 | public event EventHandler Disposed; |
|
14 | public event EventHandler Disposed; | |
14 |
|
15 | |||
15 | public bool IsDisposed { |
|
16 | public bool IsDisposed { | |
16 | get { |
|
17 | get { | |
17 | Thread.MemoryBarrier(); |
|
18 | Thread.MemoryBarrier(); | |
18 | return m_disposed != 0; |
|
19 | return m_disposed != 0; | |
19 | } |
|
20 | } | |
20 | } |
|
21 | } | |
21 |
|
22 | |||
22 | /// <summary> |
|
23 | /// <summary> | |
23 | /// Asserts the object is not disposed. |
|
24 | /// Asserts the object is not disposed. | |
24 | /// </summary> |
|
25 | /// </summary> | |
25 | /// <exception cref="ObjectDisposedException">The object is disposed</exception> |
|
26 | /// <exception cref="ObjectDisposedException">The object is disposed</exception> | |
26 | /// <remarks> |
|
27 | /// <remarks> | |
27 | /// Π£ΡΠΏΠ΅ΡΠ½Π°Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΎΠ³ΠΎ, ΡΡΠΎ ΠΎΠ±ΡΠ΅ΠΊΡ Π½Π΅ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ Π΅ΡΠ΅ Π½Π΅ Π³Π°ΡΠ°Π½ΡΠΈΡΡΠ΅Ρ, ΡΡΠΎ ΠΎΠ½ Π½Π΅ |
|
28 | /// Π£ΡΠΏΠ΅ΡΠ½Π°Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΎΠ³ΠΎ, ΡΡΠΎ ΠΎΠ±ΡΠ΅ΠΊΡ Π½Π΅ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ Π΅ΡΠ΅ Π½Π΅ Π³Π°ΡΠ°Π½ΡΠΈΡΡΠ΅Ρ, ΡΡΠΎ ΠΎΠ½ Π½Π΅ | |
28 | /// Π±ΡΠ΄Π΅Ρ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ ΡΡΠ°Π·Ρ ΠΏΠΎΡΠ»Π΅ Π½Π΅Π΅, ΠΏΠΎΡΡΠΎΠΌΡ ΠΌΠ΅ΡΠΎΠ΄Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡΡΠΈΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ Π΄ΠΎΠ»ΠΆΠ½Ρ |
|
29 | /// Π±ΡΠ΄Π΅Ρ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ ΡΡΠ°Π·Ρ ΠΏΠΎΡΠ»Π΅ Π½Π΅Π΅, ΠΏΠΎΡΡΠΎΠΌΡ ΠΌΠ΅ΡΠΎΠ΄Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡΡΠΈΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ Π΄ΠΎΠ»ΠΆΠ½Ρ | |
29 | /// ΡΡΠΈΡΡΠ²Π°ΡΡ, ΡΡΠΎ ΠΎΠ±ΡΠ΅ΠΊΡ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ ΠΈΠ· ΠΏΠ°ΡΠ°Π»Π»Π΅Π»ΡΠ½ΠΎΠ³ΠΎ ΠΏΠΎΡΠΎΠΊΠ°. |
|
30 | /// ΡΡΠΈΡΡΠ²Π°ΡΡ, ΡΡΠΎ ΠΎΠ±ΡΠ΅ΠΊΡ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ ΠΈΠ· ΠΏΠ°ΡΠ°Π»Π»Π΅Π»ΡΠ½ΠΎΠ³ΠΎ ΠΏΠΎΡΠΎΠΊΠ°. | |
30 | /// ΠΠ°Π½Π½Ρ ΠΌΠ΅ΡΠΎΠ΄ ΡΠ»ΡΠΆΠΈΡ Π΄Π»Ρ ΡΠΏΡΠΎΡΠ΅Π½ΠΈΡ ΠΎΡΠ»Π°Π΄ΠΊΠΈ ΠΎΡΠΈΠ±ΠΎΠΊ ΠΏΡΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ ΠΎΠ±ΡΠ΅ΠΊΡΠ° ΠΏΠΎΡΠ»Π΅ Π΅Π³ΠΎ |
|
31 | /// ΠΠ°Π½Π½Ρ ΠΌΠ΅ΡΠΎΠ΄ ΡΠ»ΡΠΆΠΈΡ Π΄Π»Ρ ΡΠΏΡΠΎΡΠ΅Π½ΠΈΡ ΠΎΡΠ»Π°Π΄ΠΊΠΈ ΠΎΡΠΈΠ±ΠΎΠΊ ΠΏΡΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ ΠΎΠ±ΡΠ΅ΠΊΡΠ° ΠΏΠΎΡΠ»Π΅ Π΅Π³ΠΎ | |
31 | /// ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ΠΈΡ. |
|
32 | /// ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ΠΈΡ. | |
32 | /// </remarks> |
|
33 | /// </remarks> | |
33 | /// <example> |
|
34 | /// <example> | |
34 | /// // ΠΏΡΠΈΠΌΠ΅Ρ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ΠΈΡ ΡΠ΅ΡΡΡΡΠΎΠ² |
|
35 | /// // ΠΏΡΠΈΠΌΠ΅Ρ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ΠΈΡ ΡΠ΅ΡΡΡΡΠΎΠ² | |
35 | /// class FileStore : Disposable { |
|
36 | /// class FileStore : Disposable { | |
36 | /// readonly TextWriter m_file; |
|
37 | /// readonly TextWriter m_file; | |
37 | /// readonly obejct m_sync = new object(); |
|
38 | /// readonly obejct m_sync = new object(); | |
38 | /// |
|
39 | /// | |
39 | /// public FileStore(string file) { |
|
40 | /// public FileStore(string file) { | |
40 | /// m_file = new TextWriter(File.OpenWrite(file)); |
|
41 | /// m_file = new TextWriter(File.OpenWrite(file)); | |
41 | /// } |
|
42 | /// } | |
42 | /// |
|
43 | /// | |
43 | /// public void Write(string text) { |
|
44 | /// public void Write(string text) { | |
44 | /// lock(m_sync) { |
|
45 | /// lock(m_sync) { | |
45 | /// AssertNotDisposed(); |
|
46 | /// AssertNotDisposed(); | |
46 | /// m_file.Write(text); |
|
47 | /// m_file.Write(text); | |
47 | /// } |
|
48 | /// } | |
48 | /// } |
|
49 | /// } | |
49 | /// |
|
50 | /// | |
50 | /// protected override void Dispose(bool disposing) { |
|
51 | /// protected override void Dispose(bool disposing) { | |
51 | /// if (disposing) |
|
52 | /// if (disposing) | |
52 | /// lock(m_sync) { |
|
53 | /// lock(m_sync) { | |
53 | /// m_file.Dipose(); |
|
54 | /// m_file.Dipose(); | |
54 | /// base.Dispose(true); |
|
55 | /// base.Dispose(true); | |
55 | /// } |
|
56 | /// } | |
56 | /// else |
|
57 | /// else | |
57 | /// base.Dispose(false); |
|
58 | /// base.Dispose(false); | |
58 | /// } |
|
59 | /// } | |
59 | /// } |
|
60 | /// } | |
60 | /// <example> |
|
61 | /// <example> | |
61 | protected void AssertNotDisposed() { |
|
62 | protected void AssertNotDisposed() { | |
62 | Thread.MemoryBarrier(); |
|
63 | Thread.MemoryBarrier(); | |
63 | if (m_disposed != 0) |
|
64 | if (m_disposed != 0) | |
64 | throw new ObjectDisposedException(ToString()); |
|
65 | throw new ObjectDisposedException(ToString()); | |
65 | } |
|
66 | } | |
66 | /// <summary> |
|
67 | /// <summary> | |
67 | /// ΠΡΠ·ΡΠ²Π°Π΅Ρ ΡΠΎΠ±ΡΡΠΈΠ΅ <see cref="Disposed"/> |
|
68 | /// ΠΡΠ·ΡΠ²Π°Π΅Ρ ΡΠΎΠ±ΡΡΠΈΠ΅ <see cref="Disposed"/> | |
68 | /// </summary> |
|
69 | /// </summary> | |
69 | /// <param name="disposing">ΠΡΠΈΠ·Π½Π°ΠΊ ΡΠΎΠ³ΠΎ, ΡΡΠΎ Π½ΡΠΆΠ½ΠΎ ΠΎΡΠ²ΠΎΠ±ΠΎΠ΄ΠΈΡΡ ΡΠ΅ΡΡΡΡΡ, ΠΈΠ½Π°ΡΠ΅ Π΄Π°Π½Π½ΡΠΉ ΠΌΠ΅ΡΠΎΠ΄ |
|
70 | /// <param name="disposing">ΠΡΠΈΠ·Π½Π°ΠΊ ΡΠΎΠ³ΠΎ, ΡΡΠΎ Π½ΡΠΆΠ½ΠΎ ΠΎΡΠ²ΠΎΠ±ΠΎΠ΄ΠΈΡΡ ΡΠ΅ΡΡΡΡΡ, ΠΈΠ½Π°ΡΠ΅ Π΄Π°Π½Π½ΡΠΉ ΠΌΠ΅ΡΠΎΠ΄ | |
70 | /// Π²ΡΠ·Π²Π°Π½ ΡΠ±ΠΎΡΡΠΈΠΊΠΎΠΌ ΠΌΡΡΠΎΡΠ° ΠΈ Π½ΡΠΆΠ½ΠΎ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π°ΡΡ Π’ΠΠΠ¬ΠΠ Π½Π΅ΡΠΏΡΠ°Π²Π»ΡΠ΅ΠΌΡΠ΅ ΡΠ΅ΡΡΡΡΡ Π’ΠΠΠ¬ΠΠ ΡΡΠΎΠ³ΠΎ |
|
71 | /// Π²ΡΠ·Π²Π°Π½ ΡΠ±ΠΎΡΡΠΈΠΊΠΎΠΌ ΠΌΡΡΠΎΡΠ° ΠΈ Π½ΡΠΆΠ½ΠΎ ΠΎΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π°ΡΡ Π’ΠΠΠ¬ΠΠ Π½Π΅ΡΠΏΡΠ°Π²Π»ΡΠ΅ΠΌΡΠ΅ ΡΠ΅ΡΡΡΡΡ Π’ΠΠΠ¬ΠΠ ΡΡΠΎΠ³ΠΎ | |
71 | /// ΠΎΠ±ΡΠ΅ΠΊΡΠ°.</param> |
|
72 | /// ΠΎΠ±ΡΠ΅ΠΊΡΠ°.</param> | |
72 | /// <remarks> |
|
73 | /// <remarks> | |
73 | /// ΠΠ°Π½Π½ΡΠΉ ΠΌΠ΅ΡΠΎΠ΄ Π²ΡΠ·ΡΠ²Π°Π΅ΡΡΡ Π³Π°ΡΠ°Π½ΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎ ΠΎΠ΄ΠΈΠ½ ΡΠ°Π· Π΄Π°ΠΆΠ΅ ΠΏΡΠΈ ΠΎΠ΄Π½ΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΌ Π²ΡΠ·ΠΎΠ²Π΅ <see cref="Dispose()"/> |
|
74 | /// ΠΠ°Π½Π½ΡΠΉ ΠΌΠ΅ΡΠΎΠ΄ Π²ΡΠ·ΡΠ²Π°Π΅ΡΡΡ Π³Π°ΡΠ°Π½ΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎ ΠΎΠ΄ΠΈΠ½ ΡΠ°Π· Π΄Π°ΠΆΠ΅ ΠΏΡΠΈ ΠΎΠ΄Π½ΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΌ Π²ΡΠ·ΠΎΠ²Π΅ <see cref="Dispose()"/> | |
74 | /// ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ ΠΏΠΎΡΠΎΠΊΠΎΠ². |
|
75 | /// ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ ΠΏΠΎΡΠΎΠΊΠΎΠ². | |
75 | /// </remarks> |
|
76 | /// </remarks> | |
76 | protected virtual void Dispose(bool disposing) { |
|
77 | protected virtual void Dispose(bool disposing) { | |
77 | if (disposing) { |
|
78 | if (disposing) { | |
78 | EventHandler temp = Disposed; |
|
79 | EventHandler temp = Disposed; | |
79 | if (temp != null) |
|
80 | if (temp != null) | |
80 | temp(this, EventArgs.Empty); |
|
81 | temp(this, EventArgs.Empty); | |
81 | } |
|
82 | } | |
82 | } |
|
83 | } | |
83 |
|
84 | |||
|
85 | [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")] | |||
84 | public void Dispose() { |
|
86 | public void Dispose() { | |
85 | if (Interlocked.Increment(ref m_disposed) == 1) { |
|
87 | if (Interlocked.Increment(ref m_disposed) == 1) { | |
86 | Dispose(true); |
|
88 | Dispose(true); | |
87 | GC.SuppressFinalize(this); |
|
89 | GC.SuppressFinalize(this); | |
88 | } |
|
90 | } | |
89 | } |
|
91 | } | |
90 |
|
92 | |||
91 | /// <summary> |
|
93 | /// <summary> | |
92 | /// ΠΠ°ΠΏΠΈΡΡΠ²Π°Π΅Ρ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎΠ± ΡΡΠ΅ΡΠΊΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΠ°. |
|
94 | /// ΠΠ°ΠΏΠΈΡΡΠ²Π°Π΅Ρ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ ΠΎΠ± ΡΡΠ΅ΡΠΊΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΠ°. | |
93 | /// </summary> |
|
95 | /// </summary> | |
94 | protected virtual void ReportObjectLeaks() { |
|
96 | protected virtual void ReportObjectLeaks() { | |
95 | TraceLog.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this); |
|
97 | TraceLog.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this); | |
96 | } |
|
98 | } | |
97 |
|
99 | |||
98 | ~Disposable() { |
|
100 | ~Disposable() { | |
99 | Dispose(false); |
|
101 | Dispose(false); | |
100 | ReportObjectLeaks(); |
|
102 | ReportObjectLeaks(); | |
101 | } |
|
103 | } | |
102 | } |
|
104 | } | |
103 | } No newline at end of file |
|
105 | } |
@@ -1,22 +1,30 | |||||
1 | using System; |
|
1 | using System; | |
2 |
|
2 | |||
3 | namespace Implab.Components { |
|
3 | namespace Implab.Components { | |
4 | public interface IRunnable { |
|
|||
5 |
|
|
4 | /// <summary> | |
6 | /// Starts this instance. |
|
5 | /// Interface for the component which performs a long running task. | |
|
6 | /// </summary> | |||
|
7 | /// <remarks> | |||
|
8 | /// <para>The component also should implement <see cref="IDisposable"/> interface to be able to release used resources.</para> | |||
|
9 | /// <para>All methods of this interface must be a thread safe. If the operation is not applicable in the current state the | |||
|
10 | /// method should throw an exception and keep the current state unchanged.</para> | |||
|
11 | /// </remarks> | |||
|
12 | public interface IRunnable : IDisposable { | |||
|
13 | /// <summary> | |||
|
14 | /// Starts this instance | |||
7 | /// </summary> |
|
15 | /// </summary> | |
8 | IPromise Start(); |
|
16 | IPromise Start(); | |
9 |
|
17 | |||
10 | /// <summary> |
|
18 | /// <summary> | |
11 | /// Stops this instance. After the instance is stopped it can't be started again, stopping should be treated as gracefull and async dispose. |
|
19 | /// Stops this instance, after the instance is stopped it can move to Failed, Ready or Disposed state, in case with the last it can't be reused. | |
12 | /// </summary> |
|
20 | /// </summary> | |
13 | IPromise Stop(); |
|
21 | IPromise Stop(); | |
14 |
|
22 | |||
15 | ExecutionState State { get; } |
|
23 | ExecutionState State { get; } | |
16 |
|
24 | |||
17 | event EventHandler<StateChangeEventArgs> StateChanged; |
|
25 | event EventHandler<StateChangeEventArgs> StateChanged; | |
18 |
|
26 | |||
19 | Exception LastError { get; } |
|
27 | Exception LastError { get; } | |
20 | } |
|
28 | } | |
21 | } |
|
29 | } | |
22 |
|
30 |
@@ -1,155 +1,155 | |||||
1 | using System; |
|
1 | using System; | |
2 | using System.Threading; |
|
2 | using System.Threading; | |
3 | using Implab.Diagnostics; |
|
3 | using Implab.Diagnostics; | |
4 |
|
4 | |||
5 | namespace Implab.Components { |
|
5 | namespace Implab.Components { | |
6 | public class PollingComponent : RunnableComponent { |
|
6 | public class PollingComponent : RunnableComponent { | |
7 | readonly Timer m_timer; |
|
7 | readonly Timer m_timer; | |
8 | readonly Func<Func<ICancellationToken, IPromise>, IPromise> m_dispatcher; |
|
8 | readonly Func<Func<ICancellationToken, IPromise>, IPromise> m_dispatcher; | |
9 | readonly TimeSpan m_interval; |
|
9 | readonly TimeSpan m_interval; | |
10 |
|
10 | |||
11 | readonly object m_lock = new object(); |
|
11 | readonly object m_lock = new object(); | |
12 |
|
12 | |||
13 | ActionTask m_pending; |
|
13 | ActionTask m_pending; | |
14 |
|
14 | |||
15 | protected PollingComponent(TimeSpan interval, Func<Func<ICancellationToken, IPromise>, IPromise> dispatcher, bool initialized) : base(initialized) { |
|
15 | protected PollingComponent(TimeSpan interval, Func<Func<ICancellationToken, IPromise>, IPromise> dispatcher, bool initialized) : base(initialized) { | |
16 | m_timer = new Timer(OnInternalTick); |
|
16 | m_timer = new Timer(OnInternalTick); | |
17 |
|
17 | |||
18 | m_interval = interval; |
|
18 | m_interval = interval; | |
19 | m_dispatcher = dispatcher; |
|
19 | m_dispatcher = dispatcher; | |
20 | } |
|
20 | } | |
21 |
|
21 | |||
22 | protected override IPromise OnStart() { |
|
22 | protected override IPromise OnStart() { | |
23 | m_timer.Change(TimeSpan.Zero, m_interval); |
|
23 | m_timer.Change(TimeSpan.Zero, m_interval); | |
24 |
|
24 | |||
25 | return base.OnStart(); |
|
25 | return base.OnStart(); | |
26 | } |
|
26 | } | |
27 |
|
27 | |||
28 | void OnInternalTick(object state) { |
|
28 | void OnInternalTick(object state) { | |
29 | if (StartTick()) { |
|
29 | if (StartTick()) { | |
30 | try { |
|
30 | try { | |
31 | if (m_dispatcher != null) { |
|
31 | if (m_dispatcher != null) { | |
32 | var result = m_dispatcher(OnTick); |
|
32 | var result = m_dispatcher(OnTick); | |
33 | m_pending.CancellationRequested(result.Cancel); |
|
33 | m_pending.CancellationRequested(result.Cancel); | |
34 | AwaitTick(result); |
|
34 | AwaitTick(result); | |
35 | } else { |
|
35 | } else { | |
36 | AwaitTick(OnTick(m_pending)); |
|
36 | AwaitTick(OnTick(m_pending)); | |
37 | } |
|
37 | } | |
38 | } catch (Exception error) { |
|
38 | } catch (Exception error) { | |
39 | HandleTickError(error); |
|
39 | HandleTickError(error); | |
40 | } |
|
40 | } | |
41 | } |
|
41 | } | |
42 | } |
|
42 | } | |
43 |
|
43 | |||
44 | /// <summary> |
|
44 | /// <summary> | |
45 | /// Checks wheather there is no running handler in the component and marks that the handler is starting. |
|
45 | /// Checks wheather there is no running handler in the component and marks that the handler is starting. | |
46 | /// </summary> |
|
46 | /// </summary> | |
47 | /// <returns>boolean value, true - the new tick handler may be invoked, false - a tick handler is already running or a component isn't running.</returns> |
|
47 | /// <returns>boolean value, true - the new tick handler may be invoked, false - a tick handler is already running or a component isn't running.</returns> | |
48 | /// <remarks> |
|
48 | /// <remarks> | |
49 | /// If the component is stopping no new handlers can be run. Every successful call to this method must be completed with either AwaitTick or HandleTickError handlers. |
|
49 | /// If the component is stopping no new handlers can be run. Every successful call to this method must be completed with either AwaitTick or HandleTickError handlers. | |
50 | /// </remarks> |
|
50 | /// </remarks> | |
51 | protected virtual bool StartTick() { |
|
51 | protected virtual bool StartTick() { | |
52 | lock (m_lock) { |
|
52 | lock (m_lock) { | |
53 | if (State != ExecutionState.Running || m_pending != null) |
|
53 | if (State != ExecutionState.Running || m_pending != null) | |
54 | return false; |
|
54 | return false; | |
55 | // actually the component may be in a not running state here (stopping, disposed or what ever), |
|
55 | // actually the component may be in a not running state here (stopping, disposed or what ever), | |
56 | // but OnStop method also locks on the same object and will handle operation in m_pending |
|
56 | // but OnStop method also locks on the same object and will handle operation in m_pending | |
57 | m_pending = new ActionTask( |
|
57 | m_pending = new ActionTask( | |
58 | () => { |
|
58 | () => { | |
59 | // only one operation is running, it's safe to assing m_pending from it |
|
59 | // only one operation is running, it's safe to assing m_pending from it | |
60 | m_pending = null; |
|
60 | m_pending = null; | |
61 | }, |
|
61 | }, | |
62 | ex => { |
|
62 | ex => { | |
63 | try { |
|
63 | try { | |
64 | OnTickError(ex); |
|
64 | OnTickError(ex); | |
65 | // Analysis disable once EmptyGeneralCatchClause |
|
65 | // Analysis disable once EmptyGeneralCatchClause | |
66 | } catch { |
|
66 | } catch { | |
67 | } finally { |
|
67 | } finally { | |
68 | m_pending = null; |
|
68 | m_pending = null; | |
69 | } |
|
69 | } | |
70 | // suppress error |
|
70 | // suppress error | |
71 | }, |
|
71 | }, | |
72 | ex => { |
|
72 | ex => { | |
73 | try { |
|
73 | try { | |
74 | OnTickCancel(ex); |
|
74 | OnTickCancel(ex); | |
75 | // Analysis disable once EmptyGeneralCatchClause |
|
75 | // Analysis disable once EmptyGeneralCatchClause | |
76 | } catch { |
|
76 | } catch { | |
77 | } finally { |
|
77 | } finally { | |
78 | m_pending = null; |
|
78 | m_pending = null; | |
79 | } |
|
79 | } | |
80 | // supress cancellation |
|
80 | // supress cancellation | |
81 | }, |
|
81 | }, | |
82 | false |
|
82 | false | |
83 | ); |
|
83 | ); | |
84 | return true; |
|
84 | return true; | |
85 | } |
|
85 | } | |
86 | } |
|
86 | } | |
87 |
|
87 | |||
88 | /// <summary> |
|
88 | /// <summary> | |
89 | /// Awaits the tick. |
|
89 | /// Awaits the tick. | |
90 | /// </summary> |
|
90 | /// </summary> | |
91 | /// <param name="tick">Tick.</param> |
|
91 | /// <param name="tick">Tick.</param> | |
92 | /// <remarks> |
|
92 | /// <remarks> | |
93 | /// This method is called only after StartTick method and m_pending will hold the promise which should be fulfilled. |
|
93 | /// This method is called only after StartTick method and m_pending will hold the promise which should be fulfilled. | |
94 | /// </remarks> |
|
94 | /// </remarks> | |
95 | void AwaitTick(IPromise tick) { |
|
95 | void AwaitTick(IPromise tick) { | |
96 | if (tick == null) { |
|
96 | if (tick == null) { | |
97 | m_pending.Resolve(); |
|
97 | m_pending.Resolve(); | |
98 | } else { |
|
98 | } else { | |
99 | tick.On( |
|
99 | tick.On( | |
100 | m_pending.Resolve, |
|
100 | m_pending.Resolve, | |
101 | m_pending.Reject, |
|
101 | m_pending.Reject, | |
102 | m_pending.CancelOperation |
|
102 | m_pending.CancelOperation | |
103 | ); |
|
103 | ); | |
104 | } |
|
104 | } | |
105 | } |
|
105 | } | |
106 |
|
106 | |||
107 | /// <summary> |
|
107 | /// <summary> | |
108 | /// Handles the tick error. |
|
108 | /// Handles the tick error. | |
109 | /// </summary> |
|
109 | /// </summary> | |
110 | /// <remarks> |
|
110 | /// <remarks> | |
111 | /// This method is called only after StartTick method and m_pending will hold the promise which should be fulfilled. |
|
111 | /// This method is called only after StartTick method and m_pending will hold the promise which should be fulfilled. | |
112 | /// </remarks> |
|
112 | /// </remarks> | |
113 | void HandleTickError(Exception error) { |
|
113 | void HandleTickError(Exception error) { | |
114 | m_pending.Reject(error); |
|
114 | m_pending.Reject(error); | |
115 | } |
|
115 | } | |
116 |
|
116 | |||
117 | protected virtual void OnTickError(Exception error) { |
|
117 | protected virtual void OnTickError(Exception error) { | |
118 | } |
|
118 | } | |
119 |
|
119 | |||
120 | protected virtual void OnTickCancel(Exception error) { |
|
120 | protected virtual void OnTickCancel(Exception error) { | |
121 | } |
|
121 | } | |
122 |
|
122 | |||
123 | /// <summary> |
|
123 | /// <summary> | |
124 | /// Invoked when the timer ticks, use this method to implement your logic |
|
124 | /// Invoked when the timer ticks, use this method to implement your logic | |
125 | /// </summary> |
|
125 | /// </summary> | |
126 | protected virtual IPromise OnTick(ICancellationToken cancellationToken) { |
|
126 | protected virtual IPromise OnTick(ICancellationToken cancellationToken) { | |
127 | return Promise.Success; |
|
127 | return Promise.Success; | |
128 | } |
|
128 | } | |
129 |
|
129 | |||
130 | protected override IPromise OnStop() { |
|
130 | protected override IPromise OnStop() { | |
131 | m_timer.Change(-1, -1); |
|
131 | m_timer.Change(-1, -1); | |
132 |
|
132 | |||
133 | // the component is in the stopping state |
|
133 | // the component is in the stopping state | |
134 | lock (m_lock) { |
|
134 | lock (m_lock) { | |
135 | // after this lock no more pending operations could be created |
|
135 | // after this lock no more pending operations could be created | |
136 | var pending = m_pending; |
|
136 | var pending = m_pending; | |
137 | // m_pending could be fulfilled and set to null already |
|
137 | // m_pending could be fulfilled and set to null already | |
138 | if (pending != null) { |
|
138 | if (pending != null) { | |
139 | pending.Cancel(); |
|
139 | pending.Cancel(); | |
140 | return pending.Then(base.OnStop); |
|
140 | return pending.Then(base.OnStop); | |
141 | } |
|
141 | } | |
142 | } |
|
142 | } | |
143 |
|
143 | |||
144 | return base.OnStop(); |
|
144 | return base.OnStop(); | |
145 | } |
|
145 | } | |
146 |
|
146 | |||
147 |
protected override void Dispose(bool disposing |
|
147 | protected override void Dispose(bool disposing) { | |
148 | if (disposing) |
|
148 | if (disposing) | |
149 |
|
|
149 | m_timer.Dispose(); | |
150 |
|
150 | |||
151 |
base.Dispose(disposing |
|
151 | base.Dispose(disposing); | |
152 | } |
|
152 | } | |
153 | } |
|
153 | } | |
154 | } |
|
154 | } | |
155 |
|
155 |
@@ -1,375 +1,368 | |||||
1 | using System; |
|
1 | using System; | |
|
2 | using System.Diagnostics.CodeAnalysis; | |||
2 |
|
3 | |||
3 | namespace Implab.Components { |
|
4 | namespace Implab.Components { | |
4 | public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable { |
|
5 | public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable { | |
5 | enum Commands { |
|
6 | enum Commands { | |
6 | Ok = 0, |
|
7 | Ok = 0, | |
7 | Fail, |
|
8 | Fail, | |
8 | Init, |
|
9 | Init, | |
9 | Start, |
|
10 | Start, | |
10 | Stop, |
|
11 | Stop, | |
11 | Dispose, |
|
12 | Dispose, | |
12 | Reset, |
|
13 | Reset, | |
13 | Last = Reset |
|
14 | Last = Reset | |
14 | } |
|
15 | } | |
15 |
|
16 | |||
16 | class StateMachine { |
|
17 | class StateMachine { | |
17 | static readonly ExecutionState[,] _transitions; |
|
18 | static readonly ExecutionState[,] _transitions; | |
18 |
|
19 | |||
19 | static StateMachine() { |
|
20 | static StateMachine() { | |
20 | _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; |
|
21 | _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; | |
21 |
|
22 | |||
22 | Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init); |
|
23 | Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init); | |
23 | Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose); |
|
24 | Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose); | |
24 |
|
25 | |||
25 | Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok); |
|
26 | Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok); | |
26 | Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail); |
|
27 | Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail); | |
27 |
|
28 | |||
28 | Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); |
|
29 | Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); | |
29 | Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); |
|
30 | Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); | |
30 |
|
31 | |||
31 | Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok); |
|
32 | Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok); | |
32 | Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail); |
|
33 | Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail); | |
33 | Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop); |
|
34 | Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop); | |
34 | Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose); |
|
35 | Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose); | |
35 |
|
36 | |||
36 | Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail); |
|
37 | Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail); | |
37 | Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop); |
|
38 | Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop); | |
38 | Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose); |
|
39 | Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose); | |
39 |
|
40 | |||
40 | Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); |
|
41 | Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); | |
41 | Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok); |
|
42 | Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok); | |
42 | Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose); |
|
43 | Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose); | |
43 |
|
44 | |||
44 | Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose); |
|
45 | Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose); | |
45 | Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset); |
|
46 | Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset); | |
46 | } |
|
47 | } | |
47 |
|
48 | |||
48 | static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { |
|
49 | static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { | |
49 | _transitions[(int)s1, (int)cmd] = s2; |
|
50 | _transitions[(int)s1, (int)cmd] = s2; | |
50 | } |
|
51 | } | |
51 |
|
52 | |||
52 | public ExecutionState State { |
|
53 | public ExecutionState State { | |
53 | get; |
|
54 | get; | |
54 | private set; |
|
55 | private set; | |
55 | } |
|
56 | } | |
56 |
|
57 | |||
57 | public StateMachine(ExecutionState initial) { |
|
58 | public StateMachine(ExecutionState initial) { | |
58 | State = initial; |
|
59 | State = initial; | |
59 | } |
|
60 | } | |
60 |
|
61 | |||
61 | public bool Move(Commands cmd) { |
|
62 | public bool Move(Commands cmd) { | |
62 | var next = _transitions[(int)State, (int)cmd]; |
|
63 | var next = _transitions[(int)State, (int)cmd]; | |
63 | if (next == ExecutionState.Undefined) |
|
64 | if (next == ExecutionState.Undefined) | |
64 | return false; |
|
65 | return false; | |
65 | State = next; |
|
66 | State = next; | |
66 | return true; |
|
67 | return true; | |
67 | } |
|
68 | } | |
68 | } |
|
69 | } | |
69 |
|
70 | |||
70 | IPromise m_pending; |
|
71 | IPromise m_pending; | |
71 | Exception m_lastError; |
|
72 | Exception m_lastError; | |
72 |
|
73 | |||
73 | readonly StateMachine m_stateMachine; |
|
74 | readonly StateMachine m_stateMachine; | |
74 | readonly bool m_reusable; |
|
75 | readonly bool m_reusable; | |
75 | public event EventHandler<StateChangeEventArgs> StateChanged; |
|
76 | public event EventHandler<StateChangeEventArgs> StateChanged; | |
76 |
|
77 | |||
77 | /// <summary> |
|
78 | /// <summary> | |
78 | /// Initializes component state. |
|
79 | /// Initializes component state. | |
79 | /// </summary> |
|
80 | /// </summary> | |
80 | /// <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> |
|
81 | /// <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> | |
81 | /// <param name="reusable">If set, the component may start after it has been stopped, otherwise the component is disposed after being stopped.</param> |
|
82 | /// <param name="reusable">If set, the component may start after it has been stopped, otherwise the component is disposed after being stopped.</param> | |
82 | protected RunnableComponent(bool initialized, bool reusable) { |
|
83 | protected RunnableComponent(bool initialized, bool reusable) { | |
83 | m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); |
|
84 | m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); | |
84 | m_reusable = reusable; |
|
85 | m_reusable = reusable; | |
85 | DisposeTimeout = 10000; |
|
86 | DisposeTimeout = 10000; | |
86 | } |
|
87 | } | |
87 |
|
88 | |||
88 | /// <summary> |
|
89 | /// <summary> | |
89 | /// Initializes component state. The component created with this constructor is not reusable, i.e. it will be disposed after stop. |
|
90 | /// Initializes component state. The component created with this constructor is not reusable, i.e. it will be disposed after stop. | |
90 | /// </summary> |
|
91 | /// </summary> | |
91 | /// <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> |
|
92 | /// <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> | |
92 | protected RunnableComponent(bool initialized) : this(initialized, false) { |
|
93 | protected RunnableComponent(bool initialized) : this(initialized, false) { | |
93 | } |
|
94 | } | |
94 |
|
95 | |||
95 | /// <summary> |
|
96 | /// <summary> | |
96 | /// Gets or sets the timeout to wait for the pending operation to complete. If the pending operation doesn't finish than the component will be disposed anyway. |
|
97 | /// Gets or sets the timeout to wait for the pending operation to complete. If the pending operation doesn't finish than the component will be disposed anyway. | |
97 | /// </summary> |
|
98 | /// </summary> | |
98 | protected int DisposeTimeout { |
|
99 | protected int DisposeTimeout { | |
99 | get; |
|
100 | get; | |
100 | set; |
|
101 | set; | |
101 | } |
|
102 | } | |
102 |
|
103 | |||
103 | void ThrowInvalidCommand(Commands cmd) { |
|
104 | void ThrowInvalidCommand(Commands cmd) { | |
104 | if (m_stateMachine.State == ExecutionState.Disposed) |
|
105 | if (m_stateMachine.State == ExecutionState.Disposed) | |
105 | throw new ObjectDisposedException(ToString()); |
|
106 | throw new ObjectDisposedException(ToString()); | |
106 |
|
107 | |||
107 | throw new InvalidOperationException(String.Format("Command {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); |
|
108 | throw new InvalidOperationException(String.Format("Command {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); | |
108 | } |
|
109 | } | |
109 |
|
110 | |||
110 | bool MoveIfInState(Commands cmd, IPromise pending, Exception error, ExecutionState state) { |
|
111 | bool MoveIfInState(Commands cmd, IPromise pending, Exception error, ExecutionState state) { | |
111 | ExecutionState prev, current; |
|
112 | ExecutionState prev, current; | |
112 | lock (m_stateMachine) { |
|
113 | lock (m_stateMachine) { | |
113 | if (m_stateMachine.State != state) |
|
114 | if (m_stateMachine.State != state) | |
114 | return false; |
|
115 | return false; | |
115 |
|
116 | |||
116 | prev = m_stateMachine.State; |
|
117 | prev = m_stateMachine.State; | |
117 | if (!m_stateMachine.Move(cmd)) |
|
118 | if (!m_stateMachine.Move(cmd)) | |
118 | ThrowInvalidCommand(cmd); |
|
119 | ThrowInvalidCommand(cmd); | |
119 | current = m_stateMachine.State; |
|
120 | current = m_stateMachine.State; | |
120 |
|
121 | |||
121 | m_pending = pending; |
|
122 | m_pending = pending; | |
122 | m_lastError = error; |
|
123 | m_lastError = error; | |
123 | } |
|
124 | } | |
124 | if (prev != current) |
|
125 | if (prev != current) | |
125 | OnStateChanged(prev, current, error); |
|
126 | OnStateChanged(prev, current, error); | |
126 | return true; |
|
127 | return true; | |
127 | } |
|
128 | } | |
128 |
|
129 | |||
129 | bool MoveIfPending(Commands cmd, IPromise pending, Exception error, IPromise expected) { |
|
130 | bool MoveIfPending(Commands cmd, IPromise pending, Exception error, IPromise expected) { | |
130 | ExecutionState prev, current; |
|
131 | ExecutionState prev, current; | |
131 | lock (m_stateMachine) { |
|
132 | lock (m_stateMachine) { | |
132 | if (m_pending != expected) |
|
133 | if (m_pending != expected) | |
133 | return false; |
|
134 | return false; | |
134 | prev = m_stateMachine.State; |
|
135 | prev = m_stateMachine.State; | |
135 | if (!m_stateMachine.Move(cmd)) |
|
136 | if (!m_stateMachine.Move(cmd)) | |
136 | ThrowInvalidCommand(cmd); |
|
137 | ThrowInvalidCommand(cmd); | |
137 | current = m_stateMachine.State; |
|
138 | current = m_stateMachine.State; | |
138 | m_pending = pending; |
|
139 | m_pending = pending; | |
139 | m_lastError = error; |
|
140 | m_lastError = error; | |
140 | } |
|
141 | } | |
141 | if (prev != current) |
|
142 | if (prev != current) | |
142 | OnStateChanged(prev, current, error); |
|
143 | OnStateChanged(prev, current, error); | |
143 | return true; |
|
144 | return true; | |
144 | } |
|
145 | } | |
145 |
|
146 | |||
146 | IPromise Move(Commands cmd, IPromise pending, Exception error) { |
|
147 | IPromise Move(Commands cmd, IPromise pending, Exception error) { | |
147 | ExecutionState prev, current; |
|
148 | ExecutionState prev, current; | |
148 | IPromise ret; |
|
149 | IPromise ret; | |
149 | lock (m_stateMachine) { |
|
150 | lock (m_stateMachine) { | |
150 | prev = m_stateMachine.State; |
|
151 | prev = m_stateMachine.State; | |
151 | if (!m_stateMachine.Move(cmd)) |
|
152 | if (!m_stateMachine.Move(cmd)) | |
152 | ThrowInvalidCommand(cmd); |
|
153 | ThrowInvalidCommand(cmd); | |
153 | current = m_stateMachine.State; |
|
154 | current = m_stateMachine.State; | |
154 |
|
155 | |||
155 | ret = m_pending; |
|
156 | ret = m_pending; | |
156 | m_pending = pending; |
|
157 | m_pending = pending; | |
157 | m_lastError = error; |
|
158 | m_lastError = error; | |
158 |
|
159 | |||
159 | } |
|
160 | } | |
160 | if(prev != current) |
|
161 | if(prev != current) | |
161 | OnStateChanged(prev, current, error); |
|
162 | OnStateChanged(prev, current, error); | |
162 | return ret; |
|
163 | return ret; | |
163 | } |
|
164 | } | |
164 |
|
165 | |||
165 | protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) { |
|
166 | protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) { | |
166 | var h = StateChanged; |
|
167 | var h = StateChanged; | |
167 | if (h != null) |
|
168 | if (h != null) | |
168 | h(this, new StateChangeEventArgs { |
|
169 | h(this, new StateChangeEventArgs { | |
169 | State = current, |
|
170 | State = current, | |
170 | LastError = error |
|
171 | LastError = error | |
171 | }); |
|
172 | }); | |
172 | } |
|
173 | } | |
173 |
|
174 | |||
174 | /// <summary> |
|
175 | /// <summary> | |
175 | /// Moves the component from running to failed state. |
|
176 | /// Moves the component from running to failed state. | |
176 | /// </summary> |
|
177 | /// </summary> | |
177 | /// <param name="error">The exception which is describing the error.</param> |
|
178 | /// <param name="error">The exception which is describing the error.</param> | |
178 | protected bool Fail(Exception error) { |
|
179 | protected bool Fail(Exception error) { | |
179 | return MoveIfInState(Commands.Fail, null, error, ExecutionState.Running); |
|
180 | return MoveIfInState(Commands.Fail, null, error, ExecutionState.Running); | |
180 | } |
|
181 | } | |
181 |
|
182 | |||
182 | /// <summary> |
|
183 | /// <summary> | |
183 | /// Tries to reset <see cref="ExecutionState.Failed"/> state to <see cref="ExecutionState.Ready"/>. |
|
184 | /// Tries to reset <see cref="ExecutionState.Failed"/> state to <see cref="ExecutionState.Ready"/>. | |
184 | /// </summary> |
|
185 | /// </summary> | |
185 | /// <returns>True if component is reset to <see cref="ExecutionState.Ready"/>, false if the componet wasn't |
|
186 | /// <returns>True if component is reset to <see cref="ExecutionState.Ready"/>, false if the componet wasn't | |
186 | /// in <see cref="ExecutionState.Failed"/> state.</returns> |
|
187 | /// in <see cref="ExecutionState.Failed"/> state.</returns> | |
187 | /// <remarks> |
|
188 | /// <remarks> | |
188 | /// This method checks the current state of the component and if it's in <see cref="ExecutionState.Failed"/> |
|
189 | /// This method checks the current state of the component and if it's in <see cref="ExecutionState.Failed"/> | |
189 | /// moves component to <see cref="ExecutionState.Initializing"/>. |
|
190 | /// moves component to <see cref="ExecutionState.Initializing"/>. | |
190 | /// The <see cref="OnResetState()"/> is called and if this method completes succesfully the component moved |
|
191 | /// The <see cref="OnResetState()"/> is called and if this method completes succesfully the component moved | |
191 | /// to <see cref="ExecutionState.Ready"/> state, otherwise the component is moved to <see cref="ExecutionState.Failed"/> |
|
192 | /// to <see cref="ExecutionState.Ready"/> state, otherwise the component is moved to <see cref="ExecutionState.Failed"/> | |
192 | /// state. If <see cref="OnResetState()"/> throws an exception it will be propagated by this method to the caller. |
|
193 | /// state. If <see cref="OnResetState()"/> throws an exception it will be propagated by this method to the caller. | |
193 | /// </remarks> |
|
194 | /// </remarks> | |
194 | protected bool ResetState() { |
|
195 | protected bool ResetState() { | |
195 | if (!MoveIfInState(Commands.Reset, null, null, ExecutionState.Failed)) |
|
196 | if (!MoveIfInState(Commands.Reset, null, null, ExecutionState.Failed)) | |
196 | return false; |
|
197 | return false; | |
197 |
|
198 | |||
198 | try { |
|
199 | try { | |
199 | OnResetState(); |
|
200 | OnResetState(); | |
200 | Move(Commands.Ok, null, null); |
|
201 | Move(Commands.Ok, null, null); | |
201 | return true; |
|
202 | return true; | |
202 | } catch (Exception err) { |
|
203 | } catch (Exception err) { | |
203 | Move(Commands.Fail, null, err); |
|
204 | Move(Commands.Fail, null, err); | |
204 | throw; |
|
205 | throw; | |
205 | } |
|
206 | } | |
206 | } |
|
207 | } | |
207 |
|
208 | |||
208 | /// <summary> |
|
209 | /// <summary> | |
209 | /// This method is called by <see cref="ResetState"/> to reinitialize component in the failed state. |
|
210 | /// This method is called by <see cref="ResetState"/> to reinitialize component in the failed state. | |
210 | /// </summary> |
|
211 | /// </summary> | |
211 | /// <remarks> |
|
212 | /// <remarks> | |
212 | /// Default implementation throws <see cref="NotImplementedException"/> which will cause the component |
|
213 | /// Default implementation throws <see cref="NotImplementedException"/> which will cause the component | |
213 | /// fail to reset it's state and it left in <see cref="ExecutionState.Failed"/> state. |
|
214 | /// fail to reset it's state and it left in <see cref="ExecutionState.Failed"/> state. | |
214 | /// If this method doesn't throw exceptions the component is moved to <see cref="ExecutionState.Ready"/> state. |
|
215 | /// If this method doesn't throw exceptions the component is moved to <see cref="ExecutionState.Ready"/> state. | |
215 | /// </remarks> |
|
216 | /// </remarks> | |
216 | protected virtual void OnResetState() { |
|
217 | protected virtual void OnResetState() { | |
217 | throw new NotImplementedException(); |
|
218 | throw new NotImplementedException(); | |
218 | } |
|
219 | } | |
219 |
|
220 | |||
220 | IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) { |
|
221 | IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) { | |
221 | IPromise promise = null; |
|
222 | IPromise promise = null; | |
222 | IPromise prev; |
|
223 | IPromise prev; | |
223 |
|
224 | |||
224 | var task = new ActionChainTask(action, null, null, true); |
|
225 | var task = new ActionChainTask(action, null, null, true); | |
225 |
|
226 | |||
226 | Action<Exception> errorOrCancel = e => { |
|
227 | Action<Exception> errorOrCancel = e => { | |
227 | if (e == null) |
|
228 | if (e == null) | |
228 | e = new OperationCanceledException(); |
|
229 | e = new OperationCanceledException(); | |
229 | MoveIfPending(Commands.Fail, null, e, promise); |
|
230 | MoveIfPending(Commands.Fail, null, e, promise); | |
230 | throw new PromiseTransientException(e); |
|
231 | throw new PromiseTransientException(e); | |
231 | }; |
|
232 | }; | |
232 |
|
233 | |||
233 | promise = task.Then( |
|
234 | promise = task.Then( | |
234 | () => MoveIfPending(Commands.Ok, null, null, promise), |
|
235 | () => MoveIfPending(Commands.Ok, null, null, promise), | |
235 | errorOrCancel, |
|
236 | errorOrCancel, | |
236 | errorOrCancel |
|
237 | errorOrCancel | |
237 | ); |
|
238 | ); | |
238 |
|
239 | |||
239 | prev = Move(cmd, promise, null); |
|
240 | prev = Move(cmd, promise, null); | |
240 |
|
241 | |||
241 | if (prev == null) |
|
242 | if (prev == null) | |
242 | task.Resolve(); |
|
243 | task.Resolve(); | |
243 | else |
|
244 | else | |
244 | chain(prev, task); |
|
245 | chain(prev, task); | |
245 |
|
246 | |||
246 | return promise; |
|
247 | return promise; | |
247 | } |
|
248 | } | |
248 |
|
249 | |||
249 |
|
250 | |||
250 | #region IInitializable implementation |
|
251 | #region IInitializable implementation | |
251 |
|
252 | |||
252 | public void Initialize() { |
|
253 | public void Initialize() { | |
253 | Move(Commands.Init, null, null); |
|
254 | Move(Commands.Init, null, null); | |
254 |
|
255 | |||
255 | try { |
|
256 | try { | |
256 | OnInitialize(); |
|
257 | OnInitialize(); | |
257 | Move(Commands.Ok, null, null); |
|
258 | Move(Commands.Ok, null, null); | |
258 | } catch (Exception err) { |
|
259 | } catch (Exception err) { | |
259 | Move(Commands.Fail, null, err); |
|
260 | Move(Commands.Fail, null, err); | |
260 | throw; |
|
261 | throw; | |
261 | } |
|
262 | } | |
262 | } |
|
263 | } | |
263 |
|
264 | |||
264 | protected virtual void OnInitialize() { |
|
265 | protected virtual void OnInitialize() { | |
265 | } |
|
266 | } | |
266 |
|
267 | |||
267 | #endregion |
|
268 | #endregion | |
268 |
|
269 | |||
269 | #region IRunnable implementation |
|
270 | #region IRunnable implementation | |
270 |
|
271 | |||
271 | public IPromise Start() { |
|
272 | public IPromise Start() { | |
272 | return InvokeAsync(Commands.Start, OnStart, null); |
|
273 | return InvokeAsync(Commands.Start, OnStart, null); | |
273 | } |
|
274 | } | |
274 |
|
275 | |||
275 | protected virtual IPromise OnStart() { |
|
276 | protected virtual IPromise OnStart() { | |
276 | return Promise.Success; |
|
277 | return Promise.Success; | |
277 | } |
|
278 | } | |
278 |
|
279 | |||
279 | public IPromise Stop() { |
|
280 | public IPromise Stop() { | |
280 | var pending = InvokeAsync(Commands.Stop, OnStop, StopPending); |
|
281 | var pending = InvokeAsync(Commands.Stop, OnStop, StopPending); | |
281 | return m_reusable ? pending : pending.Then(Dispose); |
|
282 | return m_reusable ? pending : pending.Then(Dispose); | |
282 | } |
|
283 | } | |
283 |
|
284 | |||
284 | protected virtual IPromise OnStop() { |
|
285 | protected virtual IPromise OnStop() { | |
285 | return Promise.Success; |
|
286 | return Promise.Success; | |
286 | } |
|
287 | } | |
287 |
|
288 | |||
288 | /// <summary> |
|
289 | /// <summary> | |
289 | /// Stops the current operation if one exists. |
|
290 | /// Stops the current operation if one exists. | |
290 | /// </summary> |
|
291 | /// </summary> | |
291 | /// <param name="current">Current.</param> |
|
292 | /// <param name="current">Current.</param> | |
292 | /// <param name="stop">Stop.</param> |
|
293 | /// <param name="stop">Stop.</param> | |
293 | protected virtual void StopPending(IPromise current, IDeferred stop) { |
|
294 | protected virtual void StopPending(IPromise current, IDeferred stop) { | |
294 | if (current == null) { |
|
295 | if (current == null) { | |
295 | stop.Resolve(); |
|
296 | stop.Resolve(); | |
296 | } else { |
|
297 | } else { | |
297 | // ΡΠ²ΡΠ·Π²Π°Π΅ΠΌ ΡΠ΅ΠΊΡΡΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ΅ΠΉ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ |
|
298 | // ΡΠ²ΡΠ·Π²Π°Π΅ΠΌ ΡΠ΅ΠΊΡΡΡΡ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ΅ΠΉ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ | |
298 | current.On( |
|
299 | current.On( | |
299 | stop.Resolve, // Π΅ΡΠ»ΠΈ ΡΠ΅ΠΊΡΡΠ°Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ Π·Π°Π²Π΅ΡΡΠΈΠ»Π°ΡΡ, ΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΠΈΠ½Π°ΡΡ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΡ |
|
300 | stop.Resolve, // Π΅ΡΠ»ΠΈ ΡΠ΅ΠΊΡΡΠ°Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ Π·Π°Π²Π΅ΡΡΠΈΠ»Π°ΡΡ, ΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΠΈΠ½Π°ΡΡ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΡ | |
300 | stop.Reject, // Π΅ΡΠ»ΠΈ ΡΠ΅ΠΊΡΡΠ°Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ Π΄Π°Π»Π° ΠΎΡΠΈΠ±ΠΊΡ - ΡΠΎ Π²ΡΠ΅ ΠΏΠ»ΠΎΡ ΠΎ, Π½Π΅Π»ΡΠ·Ρ ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°ΡΡ |
|
301 | stop.Reject, // Π΅ΡΠ»ΠΈ ΡΠ΅ΠΊΡΡΠ°Ρ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ Π΄Π°Π»Π° ΠΎΡΠΈΠ±ΠΊΡ - ΡΠΎ Π²ΡΠ΅ ΠΏΠ»ΠΎΡ ΠΎ, Π½Π΅Π»ΡΠ·Ρ ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°ΡΡ | |
301 | e => stop.Resolve() // Π΅ΡΠ»ΠΈ ΡΠ΅ΠΊΡΡΠ°Ρ ΠΎΡΠΌΠ΅Π½ΠΈΠ»Π°ΡΡ, ΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΠΈΠ½Π°ΡΡ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΡ |
|
302 | e => stop.Resolve() // Π΅ΡΠ»ΠΈ ΡΠ΅ΠΊΡΡΠ°Ρ ΠΎΡΠΌΠ΅Π½ΠΈΠ»Π°ΡΡ, ΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΠΈΠ½Π°ΡΡ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΡ | |
302 | ); |
|
303 | ); | |
303 | // ΠΏΠΎΡΡΠ»Π°Π΅ΠΌ ΡΠ΅ΠΊΡΡΠ΅ΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ ΡΠΈΠ³Π½Π°Π» ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ |
|
304 | // ΠΏΠΎΡΡΠ»Π°Π΅ΠΌ ΡΠ΅ΠΊΡΡΠ΅ΠΉ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ ΡΠΈΠ³Π½Π°Π» ΠΎΡΡΠ°Π½ΠΎΠ²ΠΊΠΈ | |
304 | current.Cancel(); |
|
305 | current.Cancel(); | |
305 | } |
|
306 | } | |
306 | } |
|
307 | } | |
307 |
|
308 | |||
308 | public ExecutionState State { |
|
309 | public ExecutionState State { | |
309 | get { |
|
310 | get { | |
310 | return m_stateMachine.State; |
|
311 | return m_stateMachine.State; | |
311 | } |
|
312 | } | |
312 | } |
|
313 | } | |
313 |
|
314 | |||
314 | public Exception LastError { |
|
315 | public Exception LastError { | |
315 | get { |
|
316 | get { | |
316 | return m_lastError; |
|
317 | return m_lastError; | |
317 | } |
|
318 | } | |
318 | } |
|
319 | } | |
319 |
|
320 | |||
320 | #endregion |
|
321 | #endregion | |
321 |
|
322 | |||
322 | #region IDisposable implementation |
|
323 | #region IDisposable implementation | |
323 |
|
324 | |||
324 | /// <summary> |
|
325 | /// <summary> | |
325 | /// Releases all resource used by the <see cref="Implab.Components.RunnableComponent"/> object. |
|
326 | /// Releases all resource used by the <see cref="Implab.Components.RunnableComponent"/> object. | |
326 | /// </summary> |
|
327 | /// </summary> | |
327 | /// <remarks> |
|
328 | /// <remarks> | |
328 | /// <para>Will not try to stop the component, it will just release all resources. |
|
329 | /// <para>Will not try to stop the component, it will just release all resources. | |
329 | /// To cleanup the component gracefully use <see cref="Stop()"/> method.</para> |
|
330 | /// To cleanup the component gracefully use <see cref="Stop()"/> method.</para> | |
330 | /// <para> |
|
331 | /// <para> | |
331 | /// In normal cases the <see cref="Dispose()"/> method shouldn't be called, the call to the <see cref="Stop()"/> |
|
332 | /// In normal cases the <see cref="Dispose()"/> method shouldn't be called, the call to the <see cref="Stop()"/> | |
332 | /// method is sufficient to cleanup the component. Call <see cref="Dispose()"/> only to cleanup after errors, |
|
333 | /// method is sufficient to cleanup the component. Call <see cref="Dispose()"/> only to cleanup after errors, | |
333 | /// especially if <see cref="Stop"/> method is failed. Using this method insted of <see cref="Stop()"/> may |
|
334 | /// especially if <see cref="Stop"/> method is failed. Using this method insted of <see cref="Stop()"/> may | |
334 | /// lead to the data loss by the component. |
|
335 | /// lead to the data loss by the component. | |
335 | /// </para></remarks> |
|
336 | /// </para></remarks> | |
|
337 | [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dipose(bool) and GC.SuppessFinalize are called")] | |||
336 | public void Dispose() { |
|
338 | public void Dispose() { | |
337 | IPromise pending; |
|
339 | IPromise pending; | |
338 |
|
340 | |||
339 | lock (m_stateMachine) { |
|
341 | lock (m_stateMachine) { | |
340 | if (m_stateMachine.State == ExecutionState.Disposed) |
|
342 | if (m_stateMachine.State == ExecutionState.Disposed) | |
341 | return; |
|
343 | return; | |
342 |
|
|
344 | Move(Commands.Dispose, null, null); | |
343 | } |
|
345 | } | |
344 |
|
346 | |||
345 | GC.SuppressFinalize(this); |
|
347 | GC.SuppressFinalize(this); | |
346 | if (pending != null) { |
|
348 | Dispose(true); | |
347 | pending.Cancel(); |
|
|||
348 | pending.Timeout(DisposeTimeout).On( |
|
|||
349 | () => Dispose(true, null), |
|
|||
350 | err => Dispose(true, err), |
|
|||
351 | reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason)) |
|
|||
352 | ); |
|
|||
353 | } else { |
|
|||
354 | Dispose(true, null); |
|
|||
355 | } |
|
|||
356 | } |
|
349 | } | |
357 |
|
350 | |||
358 | ~RunnableComponent() { |
|
351 | ~RunnableComponent() { | |
359 |
Dispose(false |
|
352 | Dispose(false); | |
360 | } |
|
353 | } | |
361 |
|
354 | |||
362 | #endregion |
|
355 | #endregion | |
363 |
|
356 | |||
364 | /// <summary> |
|
357 | /// <summary> | |
365 | /// Releases all resources used by the component, called automatically, override this method to implement your cleanup. |
|
358 | /// Releases all resources used by the component, called automatically, override this method to implement your cleanup. | |
366 | /// </summary> |
|
359 | /// </summary> | |
367 | /// <param name="disposing">true if this method is called during normal dispose process.</param> |
|
360 | /// <param name="disposing">true if this method is called during normal dispose process.</param> | |
368 |
/// <param name=" |
|
361 | /// <param name="pending">The operation which is currenty pending</param> | |
369 |
protected virtual void Dispose(bool disposing |
|
362 | protected virtual void Dispose(bool disposing) { | |
370 |
|
363 | |||
371 | } |
|
364 | } | |
372 |
|
365 | |||
373 | } |
|
366 | } | |
374 | } |
|
367 | } | |
375 |
|
368 |
@@ -1,293 +1,293 | |||||
1 | using System; |
|
1 | using System; | |
2 | using System.Diagnostics; |
|
2 | using System.Diagnostics; | |
3 | using System.IO; |
|
3 | using System.IO; | |
4 | using Implab.Automaton; |
|
4 | using Implab.Automaton; | |
5 | using Implab.Automaton.RegularExpressions; |
|
5 | using Implab.Automaton.RegularExpressions; | |
6 | using System.Linq; |
|
6 | using System.Linq; | |
7 | using Implab.Components; |
|
7 | using Implab.Components; | |
8 | using System.Collections.Generic; |
|
8 | using System.Collections.Generic; | |
9 |
|
9 | |||
10 | namespace Implab.Formats.JSON { |
|
10 | namespace Implab.Formats.JSON { | |
11 | /// <summary> |
|
11 | /// <summary> | |
12 | /// Pull ΠΏΠ°ΡΡΠ΅Ρ JSON Π΄Π°Π½Π½ΡΡ . |
|
12 | /// Pull ΠΏΠ°ΡΡΠ΅Ρ JSON Π΄Π°Π½Π½ΡΡ . | |
13 | /// </summary> |
|
13 | /// </summary> | |
14 | /// <remarks> |
|
14 | /// <remarks> | |
15 | /// Π‘Π»Π΅Π΄ΡΠ΅Ρ ΠΎΡΠΌΠ΅ΡΠΈΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΡ ΠΈΠ½ΡΠ΅ΡΠΏΡΠ΅ΡΠ°ΡΠΈΡ ΡΠ²ΠΎΠΉΡΡΠ²Π° <see cref="Level"/>, |
|
15 | /// Π‘Π»Π΅Π΄ΡΠ΅Ρ ΠΎΡΠΌΠ΅ΡΠΈΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΡ ΠΈΠ½ΡΠ΅ΡΠΏΡΠ΅ΡΠ°ΡΠΈΡ ΡΠ²ΠΎΠΉΡΡΠ²Π° <see cref="Level"/>, | |
16 | /// ΠΎΠ½ΠΎ ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ ΡΠ΅ΠΊΡΡΠΈΠΉ ΡΡΠΎΠ²Π΅Π½Ρ Π²Π»ΠΎΠΆΠ΅Π½Π½ΠΎΡΡΠΈ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ², ΠΎΠ΄Π½Π°ΠΊΠΎ Π·Π°ΠΊΡΡΠ²Π°ΡΡΠΈΠΉ |
|
16 | /// ΠΎΠ½ΠΎ ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ ΡΠ΅ΠΊΡΡΠΈΠΉ ΡΡΠΎΠ²Π΅Π½Ρ Π²Π»ΠΎΠΆΠ΅Π½Π½ΠΎΡΡΠΈ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ², ΠΎΠ΄Π½Π°ΠΊΠΎ Π·Π°ΠΊΡΡΠ²Π°ΡΡΠΈΠΉ | |
17 | /// ΡΠ»Π΅ΠΌΠ΅Π½Ρ ΠΎΠ±ΡΠ΅ΠΊΡΠ° ΠΈ ΠΌΠ°ΡΡΠΈΠ²Π° ΠΈΠΌΠ΅Π΅Ρ ΡΡΠΎΠ²Π΅Π½Ρ ΠΌΠ΅Π½ΡΡΠ΅, ΡΠ΅ΠΌ ΡΠ°ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ. |
|
17 | /// ΡΠ»Π΅ΠΌΠ΅Π½Ρ ΠΎΠ±ΡΠ΅ΠΊΡΠ° ΠΈ ΠΌΠ°ΡΡΠΈΠ²Π° ΠΈΠΌΠ΅Π΅Ρ ΡΡΠΎΠ²Π΅Π½Ρ ΠΌΠ΅Π½ΡΡΠ΅, ΡΠ΅ΠΌ ΡΠ°ΠΌ ΠΎΠ±ΡΠ΅ΠΊΡ. | |
18 | /// <code> |
|
18 | /// <code> | |
19 | /// { // Level = 1 |
|
19 | /// { // Level = 1 | |
20 | /// "name" : "Peter", // Level = 1 |
|
20 | /// "name" : "Peter", // Level = 1 | |
21 | /// "address" : { // Level = 2 |
|
21 | /// "address" : { // Level = 2 | |
22 | /// city : "Stern" // Level = 2 |
|
22 | /// city : "Stern" // Level = 2 | |
23 | /// } // Level = 1 |
|
23 | /// } // Level = 1 | |
24 | /// } // Level = 0 |
|
24 | /// } // Level = 0 | |
25 | /// </code> |
|
25 | /// </code> | |
26 | /// </remarks> |
|
26 | /// </remarks> | |
27 | public class JSONParser : Disposable { |
|
27 | public class JSONParser : Disposable { | |
28 |
|
28 | |||
29 | enum MemberContext { |
|
29 | enum MemberContext { | |
30 | MemberName, |
|
30 | MemberName, | |
31 | MemberValue |
|
31 | MemberValue | |
32 | } |
|
32 | } | |
33 |
|
33 | |||
34 | #region Parser rules |
|
34 | #region Parser rules | |
35 | struct ParserContext { |
|
35 | struct ParserContext { | |
36 | readonly int[,] m_dfa; |
|
36 | readonly int[,] m_dfa; | |
37 | int m_state; |
|
37 | int m_state; | |
38 |
|
38 | |||
39 | readonly JSONElementContext m_elementContext; |
|
39 | readonly JSONElementContext m_elementContext; | |
40 |
|
40 | |||
41 | public ParserContext(int[,] dfa, int state, JSONElementContext context) { |
|
41 | public ParserContext(int[,] dfa, int state, JSONElementContext context) { | |
42 | m_dfa = dfa; |
|
42 | m_dfa = dfa; | |
43 | m_state = state; |
|
43 | m_state = state; | |
44 | m_elementContext = context; |
|
44 | m_elementContext = context; | |
45 | } |
|
45 | } | |
46 |
|
46 | |||
47 | public bool Move(JsonTokenType token) { |
|
47 | public bool Move(JsonTokenType token) { | |
48 | var next = m_dfa[m_state, (int)token]; |
|
48 | var next = m_dfa[m_state, (int)token]; | |
49 | if (next == AutomatonConst.UNREACHABLE_STATE) |
|
49 | if (next == AutomatonConst.UNREACHABLE_STATE) | |
50 | return false; |
|
50 | return false; | |
51 | m_state = next; |
|
51 | m_state = next; | |
52 | return true; |
|
52 | return true; | |
53 | } |
|
53 | } | |
54 |
|
54 | |||
55 | public JSONElementContext ElementContext { |
|
55 | public JSONElementContext ElementContext { | |
56 | get { return m_elementContext; } |
|
56 | get { return m_elementContext; } | |
57 | } |
|
57 | } | |
58 | } |
|
58 | } | |
59 |
|
59 | |||
60 | static readonly ParserContext _jsonContext; |
|
60 | static readonly ParserContext _jsonContext; | |
61 | static readonly ParserContext _objectContext; |
|
61 | static readonly ParserContext _objectContext; | |
62 | static readonly ParserContext _arrayContext; |
|
62 | static readonly ParserContext _arrayContext; | |
63 |
|
63 | |||
64 | static JSONParser() { |
|
64 | static JSONParser() { | |
65 |
|
65 | |||
66 | var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); |
|
66 | var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); | |
67 | var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression); |
|
67 | var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression); | |
68 |
|
68 | |||
69 | var objectExpression = memberExpression |
|
69 | var objectExpression = memberExpression | |
70 | .Cat( |
|
70 | .Cat( | |
71 | MakeToken(JsonTokenType.ValueSeparator) |
|
71 | MakeToken(JsonTokenType.ValueSeparator) | |
72 | .Cat(memberExpression) |
|
72 | .Cat(memberExpression) | |
73 | .EClosure() |
|
73 | .EClosure() | |
74 | ) |
|
74 | ) | |
75 | .Optional() |
|
75 | .Optional() | |
76 | .Cat(MakeToken(JsonTokenType.EndObject)) |
|
76 | .Cat(MakeToken(JsonTokenType.EndObject)) | |
77 | .End(); |
|
77 | .End(); | |
78 |
|
78 | |||
79 | var arrayExpression = valueExpression |
|
79 | var arrayExpression = valueExpression | |
80 | .Cat( |
|
80 | .Cat( | |
81 | MakeToken(JsonTokenType.ValueSeparator) |
|
81 | MakeToken(JsonTokenType.ValueSeparator) | |
82 | .Cat(valueExpression) |
|
82 | .Cat(valueExpression) | |
83 | .EClosure() |
|
83 | .EClosure() | |
84 | ) |
|
84 | ) | |
85 | .Optional() |
|
85 | .Optional() | |
86 | .Cat(MakeToken(JsonTokenType.EndArray)) |
|
86 | .Cat(MakeToken(JsonTokenType.EndArray)) | |
87 | .End(); |
|
87 | .End(); | |
88 |
|
88 | |||
89 | var jsonExpression = valueExpression.End(); |
|
89 | var jsonExpression = valueExpression.End(); | |
90 |
|
90 | |||
91 | _jsonContext = CreateParserContext(jsonExpression, JSONElementContext.None); |
|
91 | _jsonContext = CreateParserContext(jsonExpression, JSONElementContext.None); | |
92 | _objectContext = CreateParserContext(objectExpression, JSONElementContext.Object); |
|
92 | _objectContext = CreateParserContext(objectExpression, JSONElementContext.Object); | |
93 | _arrayContext = CreateParserContext(arrayExpression, JSONElementContext.Array); |
|
93 | _arrayContext = CreateParserContext(arrayExpression, JSONElementContext.Array); | |
94 | } |
|
94 | } | |
95 |
|
95 | |||
96 | static Token MakeToken(params JsonTokenType[] input) { |
|
96 | static Token MakeToken(params JsonTokenType[] input) { | |
97 | return Token.New( input.Select(t => (int)t).ToArray() ); |
|
97 | return Token.New( input.Select(t => (int)t).ToArray() ); | |
98 | } |
|
98 | } | |
99 |
|
99 | |||
100 | static ParserContext CreateParserContext(Token expr, JSONElementContext context) { |
|
100 | static ParserContext CreateParserContext(Token expr, JSONElementContext context) { | |
101 |
|
101 | |||
102 | var dfa = new DFATable(); |
|
102 | var dfa = new DFATable(); | |
103 | var builder = new RegularExpressionVisitor(dfa); |
|
103 | var builder = new RegularExpressionVisitor(dfa); | |
104 | expr.Accept(builder); |
|
104 | expr.Accept(builder); | |
105 | builder.BuildDFA(); |
|
105 | builder.BuildDFA(); | |
106 |
|
106 | |||
107 | return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context); |
|
107 | return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context); | |
108 | } |
|
108 | } | |
109 |
|
109 | |||
110 | #endregion |
|
110 | #endregion | |
111 |
|
111 | |||
112 | readonly JSONScanner m_scanner; |
|
112 | readonly JSONScanner m_scanner; | |
113 | MemberContext m_memberContext; |
|
113 | MemberContext m_memberContext; | |
114 |
|
114 | |||
115 | JSONElementType m_elementType; |
|
115 | JSONElementType m_elementType; | |
116 | object m_elementValue; |
|
116 | object m_elementValue; | |
117 | string m_memberName = String.Empty; |
|
117 | string m_memberName = String.Empty; | |
118 |
|
118 | |||
119 | Stack<ParserContext> m_stack = new Stack<ParserContext>(); |
|
119 | Stack<ParserContext> m_stack = new Stack<ParserContext>(); | |
120 | ParserContext m_context = _jsonContext; |
|
120 | ParserContext m_context = _jsonContext; | |
121 |
|
121 | |||
122 | /// <summary> |
|
122 | /// <summary> | |
123 | /// Π‘ΠΎΠ·Π΄Π°Π΅Ρ Π½ΠΎΠ²ΡΠΉ ΠΏΠ°ΡΡΠ΅Ρ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΡΡΠΎΠΊΠΈ, ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΠ΅ΠΉ JSON |
|
123 | /// Π‘ΠΎΠ·Π΄Π°Π΅Ρ Π½ΠΎΠ²ΡΠΉ ΠΏΠ°ΡΡΠ΅Ρ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΡΡΠΎΠΊΠΈ, ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΠ΅ΠΉ JSON | |
124 | /// </summary> |
|
124 | /// </summary> | |
125 | /// <param name="text"></param> |
|
125 | /// <param name="text"></param> | |
126 | public JSONParser(string text) { |
|
126 | public JSONParser(string text) { | |
127 | Safe.ArgumentNotEmpty(text, "text"); |
|
127 | Safe.ArgumentNotEmpty(text, "text"); | |
128 | m_scanner = new JSONScanner(text); |
|
128 | m_scanner = new JSONScanner(text); | |
129 | } |
|
129 | } | |
130 |
|
130 | |||
131 | /// <summary> |
|
131 | /// <summary> | |
132 | /// Π‘ΠΎΠ·Π΄Π°Π΅Ρ Π½ΠΎΠ²ΡΠΉ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΠΏΠ°ΡΡΠ΅ΡΠ°, Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΠ΅ΠΊΡΡΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΡΠΎΠΊΠ°. |
|
132 | /// Π‘ΠΎΠ·Π΄Π°Π΅Ρ Π½ΠΎΠ²ΡΠΉ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΠΏΠ°ΡΡΠ΅ΡΠ°, Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΠ΅ΠΊΡΡΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΡΠΎΠΊΠ°. | |
133 | /// </summary> |
|
133 | /// </summary> | |
134 | /// <param name="reader">Π’Π΅ΠΊΡΡΠΎΠ²ΡΠΉ ΠΏΠΎΡΠΎΠΊ.</param> |
|
134 | /// <param name="reader">Π’Π΅ΠΊΡΡΠΎΠ²ΡΠΉ ΠΏΠΎΡΠΎΠΊ.</param> | |
135 | public JSONParser(TextReader reader) { |
|
135 | public JSONParser(TextReader reader) { | |
136 | Safe.ArgumentNotNull(reader, "reader"); |
|
136 | Safe.ArgumentNotNull(reader, "reader"); | |
137 | m_scanner = new JSONScanner(reader); |
|
137 | m_scanner = new JSONScanner(reader); | |
138 | } |
|
138 | } | |
139 |
|
139 | |||
140 | public int Level { |
|
140 | public int Level { | |
141 | get { return m_stack.Count; } |
|
141 | get { return m_stack.Count; } | |
142 | } |
|
142 | } | |
143 |
|
143 | |||
144 | /// <summary> |
|
144 | /// <summary> | |
145 | /// Π’ΠΈΠΏ ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° Π½Π° ΠΊΠΎΡΠΎΡΠΎΠΌ ΡΡΠΎΠΈΡ ΠΏΠ°ΡΡΠ΅Ρ. |
|
145 | /// Π’ΠΈΠΏ ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° Π½Π° ΠΊΠΎΡΠΎΡΠΎΠΌ ΡΡΠΎΠΈΡ ΠΏΠ°ΡΡΠ΅Ρ. | |
146 | /// </summary> |
|
146 | /// </summary> | |
147 | public JSONElementType ElementType { |
|
147 | public JSONElementType ElementType { | |
148 | get { return m_elementType; } |
|
148 | get { return m_elementType; } | |
149 | } |
|
149 | } | |
150 |
|
150 | |||
151 | /// <summary> |
|
151 | /// <summary> | |
152 | /// ΠΠΌΡ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° - ΠΈΠΌΡ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΎΠ³ΠΎ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ°. ΠΠ»Ρ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ² ΠΌΠ°ΡΡΠΈΠ²ΠΎΠ² ΠΈ ΠΊΠΎΡΠ½Π΅Π²ΠΎΠ³ΠΎ Π²ΡΠ΅Π³Π΄Π° |
|
152 | /// ΠΠΌΡ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° - ΠΈΠΌΡ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΎΠ³ΠΎ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ°. ΠΠ»Ρ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ² ΠΌΠ°ΡΡΠΈΠ²ΠΎΠ² ΠΈ ΠΊΠΎΡΠ½Π΅Π²ΠΎΠ³ΠΎ Π²ΡΠ΅Π³Π΄Π° | |
153 | /// ΠΏΡΡΡΠ°Ρ ΡΡΡΠΎΠΊΠ°. |
|
153 | /// ΠΏΡΡΡΠ°Ρ ΡΡΡΠΎΠΊΠ°. | |
154 | /// </summary> |
|
154 | /// </summary> | |
155 | public string ElementName { |
|
155 | public string ElementName { | |
156 | get { return m_memberName; } |
|
156 | get { return m_memberName; } | |
157 | } |
|
157 | } | |
158 |
|
158 | |||
159 | /// <summary> |
|
159 | /// <summary> | |
160 | /// ΠΠ½Π°ΡΠ΅Π½ΠΈΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°. Π’ΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ² ΡΠΈΠΏΠ° <see cref="JSONElementType.Value"/>, Π΄Π»Ρ ΠΎΡΡΠ°Π»ΡΠ½ΡΡ <c>null</c> |
|
160 | /// ΠΠ½Π°ΡΠ΅Π½ΠΈΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ°. Π’ΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ² ΡΠΈΠΏΠ° <see cref="JSONElementType.Value"/>, Π΄Π»Ρ ΠΎΡΡΠ°Π»ΡΠ½ΡΡ <c>null</c> | |
161 | /// </summary> |
|
161 | /// </summary> | |
162 | public object ElementValue { |
|
162 | public object ElementValue { | |
163 | get { return m_elementValue; } |
|
163 | get { return m_elementValue; } | |
164 | } |
|
164 | } | |
165 |
|
165 | |||
166 | /// <summary> |
|
166 | /// <summary> | |
167 | /// Π§ΠΈΡΠ°Π΅Ρ ΡΠ»Π΅ΡΡΠ΄ΡΡΠΈΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ ΠΈΠ· ΠΏΠΎΡΠΎΠΊΠ° |
|
167 | /// Π§ΠΈΡΠ°Π΅Ρ ΡΠ»Π΅ΡΡΠ΄ΡΡΠΈΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ ΠΈΠ· ΠΏΠΎΡΠΎΠΊΠ° | |
168 | /// </summary> |
|
168 | /// </summary> | |
169 | /// <returns><c>true</c> - ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ ΡΡΠ΅Π½ΠΈΡ ΠΏΡΠΎΡΠ»Π° ΡΡΠΏΠ΅ΡΠ½ΠΎ, <c>false</c> - ΠΊΠΎΠ½Π΅Ρ Π΄Π°Π½Π½ΡΡ </returns> |
|
169 | /// <returns><c>true</c> - ΠΎΠΏΠ΅ΡΠ°ΡΠΈΡ ΡΡΠ΅Π½ΠΈΡ ΠΏΡΠΎΡΠ»Π° ΡΡΠΏΠ΅ΡΠ½ΠΎ, <c>false</c> - ΠΊΠΎΠ½Π΅Ρ Π΄Π°Π½Π½ΡΡ </returns> | |
170 | public bool Read() { |
|
170 | public bool Read() { | |
171 | object tokenValue; |
|
171 | object tokenValue; | |
172 | JsonTokenType tokenType; |
|
172 | JsonTokenType tokenType; | |
173 |
|
173 | |||
174 | m_memberName = String.Empty; |
|
174 | m_memberName = String.Empty; | |
175 |
|
175 | |||
176 | while (m_scanner.ReadToken(out tokenValue, out tokenType)) { |
|
176 | while (m_scanner.ReadToken(out tokenValue, out tokenType)) { | |
177 | if(!m_context.Move(tokenType)) |
|
177 | if(!m_context.Move(tokenType)) | |
178 | UnexpectedToken(tokenValue, tokenType); |
|
178 | UnexpectedToken(tokenValue, tokenType); | |
179 |
|
179 | |||
180 | switch (tokenType) { |
|
180 | switch (tokenType) { | |
181 | case JsonTokenType.BeginObject: |
|
181 | case JsonTokenType.BeginObject: | |
182 | m_stack.Push(m_context); |
|
182 | m_stack.Push(m_context); | |
183 | m_context = _objectContext; |
|
183 | m_context = _objectContext; | |
184 |
|
184 | |||
185 | m_elementValue = null; |
|
185 | m_elementValue = null; | |
186 | m_memberContext = MemberContext.MemberName; |
|
186 | m_memberContext = MemberContext.MemberName; | |
187 | m_elementType = JSONElementType.BeginObject; |
|
187 | m_elementType = JSONElementType.BeginObject; | |
188 | return true; |
|
188 | return true; | |
189 | case JsonTokenType.EndObject: |
|
189 | case JsonTokenType.EndObject: | |
190 | if (m_stack.Count == 0) |
|
190 | if (m_stack.Count == 0) | |
191 | UnexpectedToken(tokenValue, tokenType); |
|
191 | UnexpectedToken(tokenValue, tokenType); | |
192 | m_context = m_stack.Pop(); |
|
192 | m_context = m_stack.Pop(); | |
193 |
|
193 | |||
194 | m_elementValue = null; |
|
194 | m_elementValue = null; | |
195 | m_elementType = JSONElementType.EndObject; |
|
195 | m_elementType = JSONElementType.EndObject; | |
196 | return true; |
|
196 | return true; | |
197 | case JsonTokenType.BeginArray: |
|
197 | case JsonTokenType.BeginArray: | |
198 | m_stack.Push(m_context); |
|
198 | m_stack.Push(m_context); | |
199 | m_context = _arrayContext; |
|
199 | m_context = _arrayContext; | |
200 |
|
200 | |||
201 | m_elementValue = null; |
|
201 | m_elementValue = null; | |
202 | m_memberContext = MemberContext.MemberValue; |
|
202 | m_memberContext = MemberContext.MemberValue; | |
203 | m_elementType = JSONElementType.BeginArray; |
|
203 | m_elementType = JSONElementType.BeginArray; | |
204 | return true; |
|
204 | return true; | |
205 | case JsonTokenType.EndArray: |
|
205 | case JsonTokenType.EndArray: | |
206 | if (m_stack.Count == 0) |
|
206 | if (m_stack.Count == 0) | |
207 | UnexpectedToken(tokenValue, tokenType); |
|
207 | UnexpectedToken(tokenValue, tokenType); | |
208 | m_context = m_stack.Pop(); |
|
208 | m_context = m_stack.Pop(); | |
209 |
|
209 | |||
210 | m_elementValue = null; |
|
210 | m_elementValue = null; | |
211 | m_elementType = JSONElementType.EndArray; |
|
211 | m_elementType = JSONElementType.EndArray; | |
212 | return true; |
|
212 | return true; | |
213 | case JsonTokenType.String: |
|
213 | case JsonTokenType.String: | |
214 | if (m_memberContext == MemberContext.MemberName) { |
|
214 | if (m_memberContext == MemberContext.MemberName) { | |
215 | m_memberName = (string)tokenValue; |
|
215 | m_memberName = (string)tokenValue; | |
216 | break; |
|
216 | break; | |
217 | } |
|
217 | } | |
218 | m_elementType = JSONElementType.Value; |
|
218 | m_elementType = JSONElementType.Value; | |
219 | m_elementValue = tokenValue; |
|
219 | m_elementValue = tokenValue; | |
220 | return true; |
|
220 | return true; | |
221 | case JsonTokenType.Number: |
|
221 | case JsonTokenType.Number: | |
222 | m_elementType = JSONElementType.Value; |
|
222 | m_elementType = JSONElementType.Value; | |
223 | m_elementValue = tokenValue; |
|
223 | m_elementValue = tokenValue; | |
224 | return true; |
|
224 | return true; | |
225 | case JsonTokenType.Literal: |
|
225 | case JsonTokenType.Literal: | |
226 | m_elementType = JSONElementType.Value; |
|
226 | m_elementType = JSONElementType.Value; | |
227 | m_elementValue = ParseLiteral((string)tokenValue); |
|
227 | m_elementValue = ParseLiteral((string)tokenValue); | |
228 | return true; |
|
228 | return true; | |
229 | case JsonTokenType.NameSeparator: |
|
229 | case JsonTokenType.NameSeparator: | |
230 | m_memberContext = MemberContext.MemberValue; |
|
230 | m_memberContext = MemberContext.MemberValue; | |
231 | break; |
|
231 | break; | |
232 | case JsonTokenType.ValueSeparator: |
|
232 | case JsonTokenType.ValueSeparator: | |
233 | m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue; |
|
233 | m_memberContext = m_context.ElementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue; | |
234 | break; |
|
234 | break; | |
235 | default: |
|
235 | default: | |
236 | UnexpectedToken(tokenValue, tokenType); |
|
236 | UnexpectedToken(tokenValue, tokenType); | |
237 | break; |
|
237 | break; | |
238 | } |
|
238 | } | |
239 | } |
|
239 | } | |
240 | if (m_context.ElementContext != JSONElementContext.None) |
|
240 | if (m_context.ElementContext != JSONElementContext.None) | |
241 | throw new ParserException("Unexpedted end of data"); |
|
241 | throw new ParserException("Unexpedted end of data"); | |
242 |
|
242 | |||
243 | EOF = true; |
|
243 | EOF = true; | |
244 |
|
244 | |||
245 | return false; |
|
245 | return false; | |
246 | } |
|
246 | } | |
247 |
|
247 | |||
248 | object ParseLiteral(string literal) { |
|
248 | object ParseLiteral(string literal) { | |
249 | switch (literal) { |
|
249 | switch (literal) { | |
250 | case "null": |
|
250 | case "null": | |
251 | return null; |
|
251 | return null; | |
252 | case "false": |
|
252 | case "false": | |
253 | return false; |
|
253 | return false; | |
254 | case "true": |
|
254 | case "true": | |
255 | return true; |
|
255 | return true; | |
256 | default: |
|
256 | default: | |
257 | UnexpectedToken(literal, JsonTokenType.Literal); |
|
257 | UnexpectedToken(literal, JsonTokenType.Literal); | |
258 | return null; // avoid compliler error |
|
258 | return null; // avoid compliler error | |
259 | } |
|
259 | } | |
260 | } |
|
260 | } | |
261 |
|
261 | |||
262 | void UnexpectedToken(object value, JsonTokenType tokenType) { |
|
262 | void UnexpectedToken(object value, JsonTokenType tokenType) { | |
263 | throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value)); |
|
263 | throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value)); | |
264 | } |
|
264 | } | |
265 |
|
265 | |||
266 |
|
266 | |||
267 | /// <summary> |
|
267 | /// <summary> | |
268 | /// ΠΡΠΈΠ·Π½Π°ΠΊ ΠΊΠΎΠ½ΡΠ° ΠΏΠΎΡΠΎΠΊΠ° |
|
268 | /// ΠΡΠΈΠ·Π½Π°ΠΊ ΠΊΠΎΠ½ΡΠ° ΠΏΠΎΡΠΎΠΊΠ° | |
269 | /// </summary> |
|
269 | /// </summary> | |
270 | public bool EOF { |
|
270 | public bool EOF { | |
271 | get; |
|
271 | get; | |
272 | private set; |
|
272 | private set; | |
273 | } |
|
273 | } | |
274 |
|
274 | |||
275 | protected override void Dispose(bool disposing) { |
|
275 | protected override void Dispose(bool disposing) { | |
276 | if (disposing) |
|
276 | if (disposing) | |
277 |
|
|
277 | m_scanner.Dispose(); | |
278 | } |
|
278 | } | |
279 |
|
279 | |||
280 | /// <summary> |
|
280 | /// <summary> | |
281 | /// ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΡ Π² ΠΊΠΎΠ½Π΅Ρ ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ°. |
|
281 | /// ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΡ Π² ΠΊΠΎΠ½Π΅Ρ ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ ΠΎΠ±ΡΠ΅ΠΊΡΠ°. | |
282 | /// </summary> |
|
282 | /// </summary> | |
283 | public void SeekElementEnd() { |
|
283 | public void SeekElementEnd() { | |
284 | var level = Level - 1; |
|
284 | var level = Level - 1; | |
285 |
|
285 | |||
286 | Debug.Assert(level >= 0); |
|
286 | Debug.Assert(level >= 0); | |
287 |
|
287 | |||
288 | while (Level != level) |
|
288 | while (Level != level) | |
289 | Read(); |
|
289 | Read(); | |
290 | } |
|
290 | } | |
291 | } |
|
291 | } | |
292 |
|
292 | |||
293 | } |
|
293 | } |
@@ -1,109 +1,109 | |||||
1 | using System; |
|
1 | using System; | |
2 | using System.Globalization; |
|
2 | using System.Globalization; | |
3 | using Implab.Automaton; |
|
3 | using Implab.Automaton; | |
4 | using System.Text; |
|
4 | using System.Text; | |
5 | using Implab.Components; |
|
5 | using Implab.Components; | |
6 | using System.IO; |
|
6 | using System.IO; | |
7 |
|
7 | |||
8 | namespace Implab.Formats.JSON { |
|
8 | namespace Implab.Formats.JSON { | |
9 | /// <summary> |
|
9 | /// <summary> | |
10 | /// Π‘ΠΊΠ°Π½Π½Π΅Ρ (Π»Π΅ΠΊΡΠ΅Ρ), ΡΠ°Π·Π±ΠΈΠ²Π°ΡΡΠΈΠΉ ΠΏΠΎΡΠΎΠΊ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ² Π½Π° ΡΠΎΠΊΠ΅Π½Ρ JSON. |
|
10 | /// Π‘ΠΊΠ°Π½Π½Π΅Ρ (Π»Π΅ΠΊΡΠ΅Ρ), ΡΠ°Π·Π±ΠΈΠ²Π°ΡΡΠΈΠΉ ΠΏΠΎΡΠΎΠΊ ΡΠΈΠΌΠ²ΠΎΠ»ΠΎΠ² Π½Π° ΡΠΎΠΊΠ΅Π½Ρ JSON. | |
11 | /// </summary> |
|
11 | /// </summary> | |
12 | public class JSONScanner : Disposable { |
|
12 | public class JSONScanner : Disposable { | |
13 | readonly StringBuilder m_builder = new StringBuilder(); |
|
13 | readonly StringBuilder m_builder = new StringBuilder(); | |
14 |
|
14 | |||
15 | readonly ScannerContext<JSONGrammar.TokenType> m_jsonContext = JSONGrammar.Instance.JsonExpression; |
|
15 | readonly ScannerContext<JSONGrammar.TokenType> m_jsonContext = JSONGrammar.Instance.JsonExpression; | |
16 | readonly ScannerContext<JSONGrammar.TokenType> m_stringContext = JSONGrammar.Instance.JsonStringExpression; |
|
16 | readonly ScannerContext<JSONGrammar.TokenType> m_stringContext = JSONGrammar.Instance.JsonStringExpression; | |
17 |
|
17 | |||
18 |
|
18 | |||
19 | readonly TextScanner m_scanner; |
|
19 | readonly TextScanner m_scanner; | |
20 |
|
20 | |||
21 | /// <summary> |
|
21 | /// <summary> | |
22 | /// Π‘ΠΎΠ·Π΄Π°Π΅Ρ Π½ΠΎΠ²ΡΠΉ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΡΠΊΠ°Π½Π½Π΅ΡΠ° |
|
22 | /// Π‘ΠΎΠ·Π΄Π°Π΅Ρ Π½ΠΎΠ²ΡΠΉ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΡΠΊΠ°Π½Π½Π΅ΡΠ° | |
23 | /// </summary> |
|
23 | /// </summary> | |
24 | public JSONScanner(string text) { |
|
24 | public JSONScanner(string text) { | |
25 | Safe.ArgumentNotEmpty(text, "text"); |
|
25 | Safe.ArgumentNotEmpty(text, "text"); | |
26 |
|
26 | |||
27 | m_scanner = new StringScanner(text); |
|
27 | m_scanner = new StringScanner(text); | |
28 | } |
|
28 | } | |
29 |
|
29 | |||
30 | public JSONScanner(TextReader reader, int bufferMax, int chunkSize) { |
|
30 | public JSONScanner(TextReader reader, int bufferMax, int chunkSize) { | |
31 | Safe.ArgumentNotNull(reader, "reader"); |
|
31 | Safe.ArgumentNotNull(reader, "reader"); | |
32 |
|
32 | |||
33 | m_scanner = new ReaderScanner(reader, bufferMax, chunkSize); |
|
33 | m_scanner = new ReaderScanner(reader, bufferMax, chunkSize); | |
34 | } |
|
34 | } | |
35 |
|
35 | |||
36 | public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){ |
|
36 | public JSONScanner(TextReader reader) : this(reader, 1024*1024, 1024){ | |
37 | } |
|
37 | } | |
38 |
|
38 | |||
39 | /// <summary> |
|
39 | /// <summary> | |
40 | /// Π§ΠΈΡΠ°Π΅Ρ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΉ Π»Π΅ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΠ»Π΅ΠΌΠ΅Π½Ρ ΠΈΠ· Π²Ρ ΠΎΠ΄Π½ΡΡ Π΄Π°Π½Π½ΡΡ . |
|
40 | /// Π§ΠΈΡΠ°Π΅Ρ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΉ Π»Π΅ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΠ»Π΅ΠΌΠ΅Π½Ρ ΠΈΠ· Π²Ρ ΠΎΠ΄Π½ΡΡ Π΄Π°Π½Π½ΡΡ . | |
41 | /// </summary> |
|
41 | /// </summary> | |
42 | /// <param name="tokenValue">ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΎΡΠΈΡΠ°Π½Π½ΠΎΠ³ΠΎ ΡΠΎΠΊΠ΅Π½Π°.</param> |
|
42 | /// <param name="tokenValue">ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΎΡΠΈΡΠ°Π½Π½ΠΎΠ³ΠΎ ΡΠΎΠΊΠ΅Π½Π°.</param> | |
43 | /// <param name="tokenType">ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ ΡΠΈΠΏ ΠΏΡΠΎΡΠΈΡΠ°Π½Π½ΠΎΠ³ΠΎ ΡΠΎΠΊΠ΅Π½Π°.</param> |
|
43 | /// <param name="tokenType">ΠΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ ΡΠΈΠΏ ΠΏΡΠΎΡΠΈΡΠ°Π½Π½ΠΎΠ³ΠΎ ΡΠΎΠΊΠ΅Π½Π°.</param> | |
44 | /// <returns><c>true</c> - ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΎΠΈΠ·Π²Π΅Π΄Π΅Π½ΠΎ ΡΡΠΏΠ΅ΡΠ½ΠΎ. <c>false</c> - Π΄ΠΎΡΡΠΈΠ³Π½ΡΡ ΠΊΠΎΠ½Π΅Ρ Π²Ρ ΠΎΠ΄Π½ΡΡ Π΄Π°Π½Π½ΡΡ </returns> |
|
44 | /// <returns><c>true</c> - ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΎΠΈΠ·Π²Π΅Π΄Π΅Π½ΠΎ ΡΡΠΏΠ΅ΡΠ½ΠΎ. <c>false</c> - Π΄ΠΎΡΡΠΈΠ³Π½ΡΡ ΠΊΠΎΠ½Π΅Ρ Π²Ρ ΠΎΠ΄Π½ΡΡ Π΄Π°Π½Π½ΡΡ </returns> | |
45 | /// <remarks>Π ΡΠ»ΡΡΠ΅ Π΅ΡΠ»ΠΈ ΡΠΎΠΊΠ΅Π½ Π½Π΅ ΡΠ°ΡΠΏΠΎΠ·Π½Π°Π΅ΡΡΡ, Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅. ΠΠ½Π°ΡΠ΅Π½ΠΈΡ ΡΠΎΠΊΠ΅Π½ΠΎΠ² ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡΡΡ, Ρ.Π΅. |
|
45 | /// <remarks>Π ΡΠ»ΡΡΠ΅ Π΅ΡΠ»ΠΈ ΡΠΎΠΊΠ΅Π½ Π½Π΅ ΡΠ°ΡΠΏΠΎΠ·Π½Π°Π΅ΡΡΡ, Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅. ΠΠ½Π°ΡΠ΅Π½ΠΈΡ ΡΠΎΠΊΠ΅Π½ΠΎΠ² ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡΡΡ, Ρ.Π΅. | |
46 | /// Π² ΡΡΡΠΎΠΊΠ°Ρ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡΡΡ ΡΠΊΡΠ°Π½ΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ ΡΠΈΠΌΠ²ΠΎΠ»Ρ, ΡΠΈΡΠ»Π° ΡΡΠ°Π½ΠΎΠ²ΡΡΡ ΡΠΈΠΏΠ° double.</remarks> |
|
46 | /// Π² ΡΡΡΠΎΠΊΠ°Ρ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡΡΡ ΡΠΊΡΠ°Π½ΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ ΡΠΈΠΌΠ²ΠΎΠ»Ρ, ΡΠΈΡΠ»Π° ΡΡΠ°Π½ΠΎΠ²ΡΡΡ ΡΠΈΠΏΠ° double.</remarks> | |
47 | public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) { |
|
47 | public bool ReadToken(out object tokenValue, out JsonTokenType tokenType) { | |
48 | JSONGrammar.TokenType[] tag; |
|
48 | JSONGrammar.TokenType[] tag; | |
49 | while (m_jsonContext.Execute(m_scanner, out tag)) { |
|
49 | while (m_jsonContext.Execute(m_scanner, out tag)) { | |
50 | switch (tag[0]) { |
|
50 | switch (tag[0]) { | |
51 | case JSONGrammar.TokenType.StringBound: |
|
51 | case JSONGrammar.TokenType.StringBound: | |
52 | tokenValue = ReadString(); |
|
52 | tokenValue = ReadString(); | |
53 | tokenType = JsonTokenType.String; |
|
53 | tokenType = JsonTokenType.String; | |
54 | break; |
|
54 | break; | |
55 | case JSONGrammar.TokenType.Number: |
|
55 | case JSONGrammar.TokenType.Number: | |
56 | tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture); |
|
56 | tokenValue = Double.Parse(m_scanner.GetTokenValue(), CultureInfo.InvariantCulture); | |
57 | tokenType = JsonTokenType.Number; |
|
57 | tokenType = JsonTokenType.Number; | |
58 | break; |
|
58 | break; | |
59 | case JSONGrammar.TokenType.Whitespace: |
|
59 | case JSONGrammar.TokenType.Whitespace: | |
60 | continue; |
|
60 | continue; | |
61 | default: |
|
61 | default: | |
62 | tokenType = (JsonTokenType)tag[0]; |
|
62 | tokenType = (JsonTokenType)tag[0]; | |
63 | tokenValue = m_scanner.GetTokenValue(); |
|
63 | tokenValue = m_scanner.GetTokenValue(); | |
64 | break; |
|
64 | break; | |
65 | } |
|
65 | } | |
66 | return true; |
|
66 | return true; | |
67 | } |
|
67 | } | |
68 | tokenValue = null; |
|
68 | tokenValue = null; | |
69 | tokenType = JsonTokenType.None; |
|
69 | tokenType = JsonTokenType.None; | |
70 | return false; |
|
70 | return false; | |
71 | } |
|
71 | } | |
72 |
|
72 | |||
73 | string ReadString() { |
|
73 | string ReadString() { | |
74 | int pos = 0; |
|
74 | int pos = 0; | |
75 | var buf = new char[6]; // the buffer for unescaping chars |
|
75 | var buf = new char[6]; // the buffer for unescaping chars | |
76 |
|
76 | |||
77 | JSONGrammar.TokenType[] tag; |
|
77 | JSONGrammar.TokenType[] tag; | |
78 | m_builder.Clear(); |
|
78 | m_builder.Clear(); | |
79 |
|
79 | |||
80 | while (m_stringContext.Execute(m_scanner, out tag)) { |
|
80 | while (m_stringContext.Execute(m_scanner, out tag)) { | |
81 | switch (tag[0]) { |
|
81 | switch (tag[0]) { | |
82 | case JSONGrammar.TokenType.StringBound: |
|
82 | case JSONGrammar.TokenType.StringBound: | |
83 | return m_builder.ToString(); |
|
83 | return m_builder.ToString(); | |
84 | case JSONGrammar.TokenType.UnescapedChar: |
|
84 | case JSONGrammar.TokenType.UnescapedChar: | |
85 | m_scanner.CopyTokenTo(m_builder); |
|
85 | m_scanner.CopyTokenTo(m_builder); | |
86 | break; |
|
86 | break; | |
87 | case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence |
|
87 | case JSONGrammar.TokenType.EscapedUnicode: // \xXXXX - unicode escape sequence | |
88 | m_scanner.CopyTokenTo(buf, 0); |
|
88 | m_scanner.CopyTokenTo(buf, 0); | |
89 | m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2)); |
|
89 | m_builder.Append(StringTranslator.TranslateHexUnicode(buf, 2)); | |
90 | pos++; |
|
90 | pos++; | |
91 | break; |
|
91 | break; | |
92 | case JSONGrammar.TokenType.EscapedChar: // \t - escape sequence |
|
92 | case JSONGrammar.TokenType.EscapedChar: // \t - escape sequence | |
93 | m_scanner.CopyTokenTo(buf, 0); |
|
93 | m_scanner.CopyTokenTo(buf, 0); | |
94 | m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1])); |
|
94 | m_builder.Append(StringTranslator.TranslateEscapedChar(buf[1])); | |
95 | break; |
|
95 | break; | |
96 | } |
|
96 | } | |
97 |
|
97 | |||
98 | } |
|
98 | } | |
99 |
|
99 | |||
100 | throw new ParserException("Unexpected end of data"); |
|
100 | throw new ParserException("Unexpected end of data"); | |
101 | } |
|
101 | } | |
102 |
|
102 | |||
103 | protected override void Dispose(bool disposing) { |
|
103 | protected override void Dispose(bool disposing) { | |
104 | if (disposing) |
|
104 | if (disposing) | |
105 |
|
|
105 | m_scanner.Dispose(); | |
106 | base.Dispose(disposing); |
|
106 | base.Dispose(disposing); | |
107 | } |
|
107 | } | |
108 | } |
|
108 | } | |
109 | } |
|
109 | } |
@@ -1,420 +1,448 | |||||
1 | using System.Threading; |
|
1 | using System.Threading; | |
2 | using System; |
|
2 | using System; | |
3 | using Implab.Diagnostics; |
|
3 | using Implab.Diagnostics; | |
4 | using System.Collections.Generic; |
|
4 | using System.Collections.Generic; | |
5 | using System.Linq; |
|
5 | using System.Linq; | |
6 |
|
6 | |||
7 | namespace Implab { |
|
7 | namespace Implab { | |
8 | public static class PromiseExtensions { |
|
8 | public static class PromiseExtensions { | |
9 | public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) { |
|
9 | public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) { | |
10 | Safe.ArgumentNotNull(that, "that"); |
|
10 | Safe.ArgumentNotNull(that, "that"); | |
11 | var context = SynchronizationContext.Current; |
|
11 | var context = SynchronizationContext.Current; | |
12 | if (context == null) |
|
12 | if (context == null) | |
13 | return that; |
|
13 | return that; | |
14 |
|
14 | |||
15 | var p = new SyncContextPromise<T>(context); |
|
15 | var p = new SyncContextPromise<T>(context); | |
16 | p.CancellationRequested(that.Cancel); |
|
16 | p.CancellationRequested(that.Cancel); | |
17 |
|
17 | |||
18 | that.On( |
|
18 | that.On( | |
19 | p.Resolve, |
|
19 | p.Resolve, | |
20 | p.Reject, |
|
20 | p.Reject, | |
21 | p.CancelOperation |
|
21 | p.CancelOperation | |
22 | ); |
|
22 | ); | |
23 | return p; |
|
23 | return p; | |
24 | } |
|
24 | } | |
25 |
|
25 | |||
26 | public static IPromise<T> DispatchToContext<T>(this IPromise<T> that, SynchronizationContext context) { |
|
26 | public static IPromise<T> DispatchToContext<T>(this IPromise<T> that, SynchronizationContext context) { | |
27 | Safe.ArgumentNotNull(that, "that"); |
|
27 | Safe.ArgumentNotNull(that, "that"); | |
28 | Safe.ArgumentNotNull(context, "context"); |
|
28 | Safe.ArgumentNotNull(context, "context"); | |
29 |
|
29 | |||
30 | var p = new SyncContextPromise<T>(context); |
|
30 | var p = new SyncContextPromise<T>(context); | |
31 | p.CancellationRequested(that.Cancel); |
|
31 | p.CancellationRequested(that.Cancel); | |
32 |
|
32 | |||
33 | that.On( |
|
33 | that.On( | |
34 | p.Resolve, |
|
34 | p.Resolve, | |
35 | p.Reject, |
|
35 | p.Reject, | |
36 | p.CancelOperation |
|
36 | p.CancelOperation | |
37 | ); |
|
37 | ); | |
38 | return p; |
|
38 | return p; | |
39 | } |
|
39 | } | |
40 |
|
40 | |||
41 | /// <summary> |
|
41 | /// <summary> | |
42 | /// Ensures the dispatched. |
|
42 | /// Ensures the dispatched. | |
43 | /// </summary> |
|
43 | /// </summary> | |
44 | /// <returns>The dispatched.</returns> |
|
44 | /// <returns>The dispatched.</returns> | |
45 | /// <param name="that">That.</param> |
|
45 | /// <param name="that">That.</param> | |
46 | /// <param name="head">Head.</param> |
|
46 | /// <param name="head">Head.</param> | |
47 | /// <param name="cleanup">Cleanup.</param> |
|
47 | /// <param name="cleanup">Cleanup.</param> | |
48 | /// <typeparam name="TPromise">The 1st type parameter.</typeparam> |
|
48 | /// <typeparam name="TPromise">The 1st type parameter.</typeparam> | |
49 | /// <typeparam name="T">The 2nd type parameter.</typeparam> |
|
49 | /// <typeparam name="T">The 2nd type parameter.</typeparam> | |
50 | public static TPromise EnsureDispatched<TPromise, T>(this TPromise that, IPromise<T> head, Action<T> cleanup) where TPromise : IPromise { |
|
50 | public static TPromise EnsureDispatched<TPromise, T>(this TPromise that, IPromise<T> head, Action<T> cleanup) where TPromise : IPromise { | |
51 | Safe.ArgumentNotNull(that, "that"); |
|
51 | Safe.ArgumentNotNull(that, "that"); | |
52 | Safe.ArgumentNotNull(head, "head"); |
|
52 | Safe.ArgumentNotNull(head, "head"); | |
53 |
|
53 | |||
54 | that.On(() => head.On(cleanup), PromiseEventType.Cancelled); |
|
54 | that.On(() => head.On(cleanup), PromiseEventType.Cancelled); | |
55 |
|
55 | |||
56 | return that; |
|
56 | return that; | |
57 | } |
|
57 | } | |
58 |
|
58 | |||
59 | /// <summary> |
|
59 | /// <summary> | |
60 | /// Adds a cancellation point to the chain of promises. When a cancellation request reaches the cancellation point the operation is |
|
60 | /// Adds a cancellation point to the chain of promises. When a cancellation request reaches the cancellation point the operation is | |
61 | /// cancelled immediatelly, and the request is passed towards. If the operation at the higher level can not be cancelled is't result |
|
61 | /// cancelled immediatelly, and the request is passed towards. If the operation at the higher level can not be cancelled is't result | |
62 | /// will be collected with <paramref name="cleanup"/> callback. |
|
62 | /// will be collected with <paramref name="cleanup"/> callback. | |
63 | /// </summary> |
|
63 | /// </summary> | |
64 | /// <typeparam name="T">The type of the promise result.</typeparam> |
|
64 | /// <typeparam name="T">The type of the promise result.</typeparam> | |
65 | /// <param name="that">The promise to which the cancellation point should be attached.</param> |
|
65 | /// <param name="that">The promise to which the cancellation point should be attached.</param> | |
66 | /// <param name="cleanup">The callback which is used to cleanup the result of the operation if the cancellation point is cancelled already.</param> |
|
66 | /// <param name="cleanup">The callback which is used to cleanup the result of the operation if the cancellation point is cancelled already.</param> | |
67 | /// <returns>The promise</returns> |
|
67 | /// <returns>The promise</returns> | |
68 | public static IPromise<T> CancellationPoint<T>(this IPromise<T> that, Action<T> cleanup) { |
|
68 | public static IPromise<T> CancellationPoint<T>(this IPromise<T> that, Action<T> cleanup) { | |
69 | var meduim = new Promise<T>(); |
|
69 | var meduim = new Promise<T>(); | |
70 |
|
70 | |||
71 | that.On(meduim.Resolve, meduim.Reject, meduim.CancelOperation); |
|
71 | that.On(meduim.Resolve, meduim.Reject, meduim.CancelOperation); | |
72 |
|
72 | |||
73 | meduim.CancellationRequested(that.Cancel); |
|
73 | meduim.CancellationRequested(that.Cancel); | |
74 | meduim.CancellationRequested(meduim.CancelOperation); |
|
74 | meduim.CancellationRequested(meduim.CancelOperation); | |
75 |
|
75 | |||
76 | if (cleanup != null) |
|
76 | if (cleanup != null) | |
77 | meduim.On((Action<T>)null, null, (e) => { |
|
77 | meduim.On((Action<T>)null, null, (e) => { | |
78 | that.On(cleanup); |
|
78 | that.On(cleanup); | |
79 | }); |
|
79 | }); | |
80 |
|
80 | |||
81 | return meduim; |
|
81 | return meduim; | |
82 | } |
|
82 | } | |
83 |
|
83 | |||
84 | public static AsyncCallback AsyncCallback<T>(this Promise<T> that, Func<IAsyncResult, T> callback) { |
|
84 | public static AsyncCallback AsyncCallback<T>(this Promise<T> that, Func<IAsyncResult, T> callback) { | |
85 | Safe.ArgumentNotNull(that, "that"); |
|
85 | Safe.ArgumentNotNull(that, "that"); | |
86 | Safe.ArgumentNotNull(callback, "callback"); |
|
86 | Safe.ArgumentNotNull(callback, "callback"); | |
87 | var op = TraceContext.Instance.CurrentOperation; |
|
87 | var op = TraceContext.Instance.CurrentOperation; | |
88 | return ar => { |
|
88 | return ar => { | |
89 | TraceContext.Instance.EnterLogicalOperation(op, false); |
|
89 | TraceContext.Instance.EnterLogicalOperation(op, false); | |
90 | try { |
|
90 | try { | |
91 | that.Resolve(callback(ar)); |
|
91 | that.Resolve(callback(ar)); | |
92 | } catch (Exception err) { |
|
92 | } catch (Exception err) { | |
93 | that.Reject(err); |
|
93 | that.Reject(err); | |
94 | } finally { |
|
94 | } finally { | |
95 | TraceContext.Instance.Leave(); |
|
95 | TraceContext.Instance.Leave(); | |
96 | } |
|
96 | } | |
97 | }; |
|
97 | }; | |
98 | } |
|
98 | } | |
99 |
|
99 | |||
100 | static void CancelByTimeoutCallback(object cookie) { |
|
100 | static void CancelByTimeoutCallback(object cookie) { | |
101 | ((ICancellable)cookie).Cancel(new TimeoutException()); |
|
101 | ((ICancellable)cookie).Cancel(new TimeoutException()); | |
102 | } |
|
102 | } | |
103 |
|
103 | |||
104 | /// <summary> |
|
104 | /// <summary> | |
105 | /// Cancells promise after the specified timeout is elapsed. |
|
105 | /// Cancells promise after the specified timeout is elapsed. | |
106 | /// </summary> |
|
106 | /// </summary> | |
107 | /// <param name="that">The promise to cancel on timeout.</param> |
|
107 | /// <param name="that">The promise to cancel on timeout.</param> | |
108 | /// <param name="milliseconds">The timeout in milliseconds.</param> |
|
108 | /// <param name="milliseconds">The timeout in milliseconds.</param> | |
109 | /// <typeparam name="TPromise">The 1st type parameter.</typeparam> |
|
109 | /// <typeparam name="TPromise">The 1st type parameter.</typeparam> | |
110 | public static TPromise Timeout<TPromise>(this TPromise that, int milliseconds) where TPromise : IPromise { |
|
110 | public static TPromise Timeout<TPromise>(this TPromise that, int milliseconds) where TPromise : IPromise { | |
111 | Safe.ArgumentNotNull(that, "that"); |
|
111 | Safe.ArgumentNotNull(that, "that"); | |
112 | var timer = new Timer(CancelByTimeoutCallback, that, milliseconds, -1); |
|
112 | var timer = new Timer(CancelByTimeoutCallback, that, milliseconds, -1); | |
113 | that.On(timer.Dispose, PromiseEventType.All); |
|
113 | that.On(timer.Dispose, PromiseEventType.All); | |
114 | return that; |
|
114 | return that; | |
115 | } |
|
115 | } | |
116 |
|
116 | |||
117 | public static IPromise PromiseAll(this IEnumerable<IPromise> that) { |
|
117 | public static IPromise PromiseAll(this IEnumerable<IPromise> that) { | |
118 | Safe.ArgumentNotNull(that, "that"); |
|
118 | Safe.ArgumentNotNull(that, "that"); | |
119 | return PromiseAll(that.ToList()); |
|
119 | return PromiseAll(that.ToList()); | |
120 | } |
|
120 | } | |
121 |
|
121 | |||
122 | public static IPromise<T[]> PromiseAll<T>(this IEnumerable<IPromise<T>> that) { |
|
122 | public static IPromise<T[]> PromiseAll<T>(this IEnumerable<IPromise<T>> that) { | |
|
123 | return PromiseAll(that, null); | |||
|
124 | } | |||
|
125 | ||||
|
126 | public static IPromise<T[]> PromiseAll<T>(this IEnumerable<IPromise<T>> that, Action<T> cleanup) { | |||
123 | Safe.ArgumentNotNull(that, "that"); |
|
127 | Safe.ArgumentNotNull(that, "that"); | |
124 | return PromiseAll(that.ToList()); |
|
128 | return PromiseAll(that.ToList(), cleanup); | |
125 | } |
|
129 | } | |
126 |
|
130 | |||
127 | public static IPromise PromiseAll(this ICollection<IPromise> that) { |
|
131 | public static IPromise PromiseAll(this ICollection<IPromise> that) { | |
128 | Safe.ArgumentNotNull(that, "that"); |
|
132 | Safe.ArgumentNotNull(that, "that"); | |
129 |
|
133 | |||
130 | int count = that.Count; |
|
134 | int count = that.Count; | |
131 | int errors = 0; |
|
135 | int errors = 0; | |
132 | var medium = new Promise(); |
|
136 | var medium = new Promise(); | |
133 |
|
137 | |||
134 | if (count == 0) { |
|
138 | if (count == 0) { | |
135 | medium.Resolve(); |
|
139 | medium.Resolve(); | |
136 | return medium; |
|
140 | return medium; | |
137 | } |
|
141 | } | |
138 |
|
142 | |||
139 | medium.On(() => { |
|
143 | medium.On(() => { | |
140 | foreach (var p2 in that) |
|
144 | foreach (var p2 in that) | |
141 | p2.Cancel(); |
|
145 | p2.Cancel(); | |
142 | }, PromiseEventType.ErrorOrCancel); |
|
146 | }, PromiseEventType.ErrorOrCancel); | |
143 |
|
147 | |||
144 | foreach (var p in that) |
|
148 | foreach (var p in that) | |
145 | p.On( |
|
149 | p.On( | |
146 | () => { |
|
150 | () => { | |
147 | if (Interlocked.Decrement(ref count) == 0) |
|
151 | if (Interlocked.Decrement(ref count) == 0) | |
148 | medium.Resolve(); |
|
152 | medium.Resolve(); | |
149 | }, |
|
153 | }, | |
150 | error => { |
|
154 | error => { | |
151 | if (Interlocked.Increment(ref errors) == 1) |
|
155 | if (Interlocked.Increment(ref errors) == 1) | |
152 | medium.Reject( |
|
156 | medium.Reject( | |
153 | new Exception("The dependency promise is failed", error) |
|
157 | new Exception("The dependency promise is failed", error) | |
154 | ); |
|
158 | ); | |
155 | }, |
|
159 | }, | |
156 | reason => { |
|
160 | reason => { | |
157 | if (Interlocked.Increment(ref errors) == 1) |
|
161 | if (Interlocked.Increment(ref errors) == 1) | |
158 | medium.Cancel( |
|
162 | medium.Cancel( | |
159 | new Exception("The dependency promise is cancelled") |
|
163 | new Exception("The dependency promise is cancelled") | |
160 | ); |
|
164 | ); | |
161 | } |
|
165 | } | |
162 | ); |
|
166 | ); | |
163 |
|
167 | |||
164 | return medium; |
|
168 | return medium; | |
165 | } |
|
169 | } | |
166 |
|
170 | |||
167 | public static IPromise<T[]> PromiseAll<T>(this ICollection<IPromise<T>> that) { |
|
171 | public static IPromise<T[]> PromiseAll<T>(this ICollection<IPromise<T>> that) { | |
|
172 | return PromiseAll(that, null); | |||
|
173 | } | |||
|
174 | ||||
|
175 | /// <summary> | |||
|
176 | /// Creates a new promise which will be satisfied when all promises are satisfied. | |||
|
177 | /// </summary> | |||
|
178 | /// <typeparam name="T"></typeparam> | |||
|
179 | /// <param name="that"></param> | |||
|
180 | /// <param name="cleanup">A callback used to cleanup already resolved promises in case of an error</param> | |||
|
181 | /// <returns></returns> | |||
|
182 | public static IPromise<T[]> PromiseAll<T>(this ICollection<IPromise<T>> that, Action<T> cleanup) { | |||
168 | Safe.ArgumentNotNull(that, "that"); |
|
183 | Safe.ArgumentNotNull(that, "that"); | |
169 |
|
184 | |||
170 | int count = that.Count; |
|
185 | int count = that.Count; | |
|
186 | ||||
|
187 | if (count == 0) | |||
|
188 | return Promise<T[]>.FromResult(new T[0]); | |||
|
189 | ||||
171 | int errors = 0; |
|
190 | int errors = 0; | |
172 | var medium = new Promise<T[]>(); |
|
191 | var medium = new Promise<T[]>(); | |
173 | var results = new T[that.Count]; |
|
192 | var results = new T[that.Count]; | |
174 |
|
193 | |||
175 | medium.On(() => { |
|
194 | medium.On(() => { | |
176 | foreach (var p2 in that) |
|
195 | foreach (var p2 in that) { | |
177 | p2.Cancel(); |
|
196 | p2.Cancel(); | |
|
197 | if (cleanup != null) | |||
|
198 | p2.On(cleanup); | |||
|
199 | } | |||
178 | }, PromiseEventType.ErrorOrCancel); |
|
200 | }, PromiseEventType.ErrorOrCancel); | |
179 |
|
201 | |||
180 | int i = 0; |
|
202 | int i = 0; | |
181 | foreach (var p in that) { |
|
203 | foreach (var p in that) { | |
182 | var idx = i; |
|
204 | var idx = i; | |
183 | p.On( |
|
205 | p.On( | |
184 | x => { |
|
206 | x => { | |
185 | results[idx] = x; |
|
207 | results[idx] = x; | |
186 | if (Interlocked.Decrement(ref count) == 0) |
|
208 | if (Interlocked.Decrement(ref count) == 0) | |
187 | medium.Resolve(results); |
|
209 | medium.Resolve(results); | |
188 | }, |
|
210 | }, | |
189 | error => { |
|
211 | error => { | |
190 | if (Interlocked.Increment(ref errors) == 1) |
|
212 | if (Interlocked.Increment(ref errors) == 1) | |
191 | medium.Reject( |
|
213 | medium.Reject( | |
192 | new Exception("The dependency promise is failed", error) |
|
214 | new Exception("The dependency promise is failed", error) | |
193 | ); |
|
215 | ); | |
194 | }, |
|
216 | }, | |
195 | reason => { |
|
217 | reason => { | |
196 | if (Interlocked.Increment(ref errors) == 1) |
|
218 | if (Interlocked.Increment(ref errors) == 1) | |
197 | medium.Cancel( |
|
219 | medium.Cancel( | |
198 | new Exception("The dependency promise is cancelled", reason) |
|
220 | new Exception("The dependency promise is cancelled", reason) | |
199 | ); |
|
221 | ); | |
200 | } |
|
222 | } | |
201 | ); |
|
223 | ); | |
202 | i++; |
|
224 | i++; | |
203 | } |
|
225 | } | |
204 |
|
226 | |||
205 | return medium; |
|
227 | return medium; | |
206 | } |
|
228 | } | |
207 |
|
229 | |||
208 | public static IPromise Then(this IPromise that, Action success, Action<Exception> error, Action<Exception> cancel) { |
|
230 | public static IPromise Then(this IPromise that, Action success, Action<Exception> error, Action<Exception> cancel) { | |
209 | Safe.ArgumentNotNull(that, "that"); |
|
231 | Safe.ArgumentNotNull(that, "that"); | |
210 |
|
232 | |||
211 | var d = new ActionTask(success, error, cancel, false); |
|
233 | var d = new ActionTask(success, error, cancel, false); | |
212 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
234 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
213 | d.CancellationRequested(that.Cancel); |
|
235 | d.CancellationRequested(that.Cancel); | |
214 | return d; |
|
236 | return d; | |
215 | } |
|
237 | } | |
216 |
|
238 | |||
217 | public static IPromise Then(this IPromise that, Action success, Action<Exception> error) { |
|
239 | public static IPromise Then(this IPromise that, Action success, Action<Exception> error) { | |
218 | return Then(that, success, error, null); |
|
240 | return Then(that, success, error, null); | |
219 | } |
|
241 | } | |
220 |
|
242 | |||
221 | public static IPromise Then(this IPromise that, Action success) { |
|
243 | public static IPromise Then(this IPromise that, Action success) { | |
222 | return Then(that, success, null, null); |
|
244 | return Then(that, success, null, null); | |
223 | } |
|
245 | } | |
224 |
|
246 | |||
225 | public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error, Func<Exception, T> cancel) { |
|
247 | public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error, Func<Exception, T> cancel) { | |
226 | Safe.ArgumentNotNull(that, "that"); |
|
248 | Safe.ArgumentNotNull(that, "that"); | |
227 |
|
249 | |||
228 | var d = new FuncTask<T>(success, error, cancel, false); |
|
250 | var d = new FuncTask<T>(success, error, cancel, false); | |
229 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
251 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
230 | d.CancellationRequested(that.Cancel); |
|
252 | d.CancellationRequested(that.Cancel); | |
231 | return d; |
|
253 | return d; | |
232 | } |
|
254 | } | |
233 |
|
255 | |||
234 | public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error) { |
|
256 | public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error) { | |
235 | return Then(that, success, error, null); |
|
257 | return Then(that, success, error, null); | |
236 | } |
|
258 | } | |
237 |
|
259 | |||
238 | public static IPromise<T> Then<T>(this IPromise that, Func<T> success) { |
|
260 | public static IPromise<T> Then<T>(this IPromise that, Func<T> success) { | |
239 | return Then(that, success, null, null); |
|
261 | return Then(that, success, null, null); | |
240 | } |
|
262 | } | |
241 |
|
263 | |||
242 | public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error, Func<Exception, T2> cancel) { |
|
264 | public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error, Func<Exception, T2> cancel) { | |
243 | Safe.ArgumentNotNull(that, "that"); |
|
265 | Safe.ArgumentNotNull(that, "that"); | |
244 | var d = new FuncTask<T, T2>(success, error, cancel, false); |
|
266 | var d = new FuncTask<T, T2>(success, error, cancel, false); | |
245 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
267 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
246 | d.CancellationRequested(that.Cancel); |
|
268 | d.CancellationRequested(that.Cancel); | |
247 | return d; |
|
269 | return d; | |
248 | } |
|
270 | } | |
249 |
|
271 | |||
250 | public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success, Func<Exception, T> error, Func<Exception, T> cancel) { |
|
272 | public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success, Func<Exception, T> error, Func<Exception, T> cancel) { | |
251 | Safe.ArgumentNotNull(that, "that"); |
|
273 | Safe.ArgumentNotNull(that, "that"); | |
252 | var d = new FuncTask<T, T>( |
|
274 | var d = new FuncTask<T, T>( | |
253 | x => { |
|
275 | x => { | |
254 | success(x); |
|
276 | success(x); | |
255 | return x; |
|
277 | return x; | |
256 | }, |
|
278 | }, | |
257 | error, |
|
279 | error, | |
258 | cancel, |
|
280 | cancel, | |
259 | false |
|
281 | false | |
260 | ); |
|
282 | ); | |
261 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
283 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
262 | d.CancellationRequested(that.Cancel); |
|
284 | d.CancellationRequested(that.Cancel); | |
263 | return d; |
|
285 | return d; | |
264 | } |
|
286 | } | |
265 |
|
287 | |||
266 | public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success, Func<Exception, T> error) { |
|
288 | public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success, Func<Exception, T> error) { | |
267 | return Then(that, success, error, null); |
|
289 | return Then(that, success, error, null); | |
268 | } |
|
290 | } | |
269 |
|
291 | |||
270 | public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success) { |
|
292 | public static IPromise<T> Then<T>(this IPromise<T> that, Action<T> success) { | |
271 | return Then(that, success, null, null); |
|
293 | return Then(that, success, null, null); | |
272 | } |
|
294 | } | |
273 |
|
295 | |||
274 | public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error) { |
|
296 | public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error) { | |
275 | return Then(that, success, error, null); |
|
297 | return Then(that, success, error, null); | |
276 | } |
|
298 | } | |
277 |
|
299 | |||
278 | public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success) { |
|
300 | public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success) { | |
279 | return Then(that, success, null, null); |
|
301 | return Then(that, success, null, null); | |
280 | } |
|
302 | } | |
281 |
|
303 | |||
282 | public static IPromise<T> Always<T>(this IPromise<T> that, Action handler) { |
|
304 | public static IPromise<T> Always<T>(this IPromise<T> that, Action handler) { | |
283 | Func<Exception, T> errorOrCancel; |
|
305 | Func<Exception, T> errorOrCancel; | |
284 | if (handler != null) |
|
306 | if (handler != null) | |
285 | errorOrCancel = e => { |
|
307 | errorOrCancel = e => { | |
286 | handler(); |
|
308 | handler(); | |
287 | throw new PromiseTransientException(e); |
|
309 | throw new PromiseTransientException(e); | |
288 | }; |
|
310 | }; | |
289 | else |
|
311 | else | |
290 | errorOrCancel = null; |
|
312 | errorOrCancel = null; | |
291 |
|
313 | |||
292 | return Then( |
|
314 | return Then( | |
293 | that, |
|
315 | that, | |
294 | x => { |
|
316 | x => { | |
295 | handler(); |
|
317 | handler(); | |
296 | return x; |
|
318 | return x; | |
297 | }, |
|
319 | }, | |
298 | errorOrCancel, |
|
320 | errorOrCancel, | |
299 | errorOrCancel); |
|
321 | errorOrCancel); | |
300 | } |
|
322 | } | |
301 |
|
323 | |||
302 | public static IPromise Always(this IPromise that, Action handler) { |
|
324 | public static IPromise Always(this IPromise that, Action handler) { | |
303 | Action<Exception> errorOrCancel; |
|
325 | Action<Exception> errorOrCancel; | |
304 | if (handler != null) |
|
326 | if (handler != null) | |
305 | errorOrCancel = e => { |
|
327 | errorOrCancel = e => { | |
306 | handler(); |
|
328 | handler(); | |
307 | throw new PromiseTransientException(e); |
|
329 | throw new PromiseTransientException(e); | |
308 | }; |
|
330 | }; | |
309 | else |
|
331 | else | |
310 | errorOrCancel = null; |
|
332 | errorOrCancel = null; | |
311 |
|
333 | |||
312 | return Then( |
|
334 | return Then( | |
313 | that, |
|
335 | that, | |
314 | handler, |
|
336 | handler, | |
315 | errorOrCancel, |
|
337 | errorOrCancel, | |
316 | errorOrCancel); |
|
338 | errorOrCancel); | |
317 | } |
|
339 | } | |
318 |
|
340 | |||
319 | public static IPromise Error(this IPromise that, Action<Exception> handler, bool handleCancellation) { |
|
341 | public static IPromise Error(this IPromise that, Action<Exception> handler, bool handleCancellation) { | |
320 | Action<Exception> errorOrCancel; |
|
342 | Action<Exception> errorOrCancel; | |
321 | if (handler != null) |
|
343 | if (handler != null) | |
322 | errorOrCancel = e => { |
|
344 | errorOrCancel = e => { | |
323 | handler(e); |
|
345 | handler(e); | |
324 | throw new PromiseTransientException(e); |
|
346 | throw new PromiseTransientException(e); | |
325 | }; |
|
347 | }; | |
326 | else |
|
348 | else | |
327 | errorOrCancel = null; |
|
349 | errorOrCancel = null; | |
328 |
|
350 | |||
329 | return Then(that, null, errorOrCancel, handleCancellation ? errorOrCancel : null); |
|
351 | return Then(that, null, errorOrCancel, handleCancellation ? errorOrCancel : null); | |
330 | } |
|
352 | } | |
331 |
|
353 | |||
332 | public static IPromise Error(this IPromise that, Action<Exception> handler) { |
|
354 | public static IPromise Error(this IPromise that, Action<Exception> handler) { | |
333 | return Error(that, handler, false); |
|
355 | return Error(that, handler, false); | |
334 | } |
|
356 | } | |
335 |
|
357 | |||
336 | public static IPromise<T> Error<T>(this IPromise<T> that, Action<Exception> handler, bool handleCancellation) { |
|
358 | public static IPromise<T> Error<T>(this IPromise<T> that, Action<Exception> handler, bool handleCancellation) { | |
337 | Func<Exception, T> errorOrCancel; |
|
359 | Func<Exception, T> errorOrCancel; | |
338 | if (handler != null) |
|
360 | if (handler != null) | |
339 | errorOrCancel = e => { |
|
361 | errorOrCancel = e => { | |
340 | handler(e); |
|
362 | handler(e); | |
341 | throw new PromiseTransientException(e); |
|
363 | throw new PromiseTransientException(e); | |
342 | }; |
|
364 | }; | |
343 | else |
|
365 | else | |
344 | errorOrCancel = null; |
|
366 | errorOrCancel = null; | |
345 |
|
367 | |||
346 | return Then(that, null, errorOrCancel, handleCancellation ? errorOrCancel : null); |
|
368 | return Then(that, null, errorOrCancel, handleCancellation ? errorOrCancel : null); | |
347 | } |
|
369 | } | |
348 |
|
370 | |||
349 | public static IPromise<T> Error<T>(this IPromise<T> that, Action<Exception> handler) { |
|
371 | public static IPromise<T> Error<T>(this IPromise<T> that, Action<Exception> handler) { | |
350 | return Error(that, handler, false); |
|
372 | return Error(that, handler, false); | |
351 | } |
|
373 | } | |
352 |
|
374 | |||
353 | #region chain traits |
|
375 | #region chain traits | |
354 | public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) { |
|
376 | public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception, IPromise> error, Func<Exception, IPromise> cancel) { | |
355 | Safe.ArgumentNotNull(that, "that"); |
|
377 | Safe.ArgumentNotNull(that, "that"); | |
356 |
|
378 | |||
357 | var d = new ActionChainTask(success, error, cancel, false); |
|
379 | var d = new ActionChainTask(success, error, cancel, false); | |
358 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
380 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
359 | d.CancellationRequested(that.Cancel); |
|
381 | d.CancellationRequested(that.Cancel); | |
360 | return d; |
|
382 | return d; | |
361 | } |
|
383 | } | |
362 |
|
384 | |||
363 | public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception, IPromise> error) { |
|
385 | public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception, IPromise> error) { | |
364 | return Chain(that, success, error, null); |
|
386 | return Chain(that, success, error, null); | |
365 | } |
|
387 | } | |
366 |
|
388 | |||
367 | public static IPromise Chain(this IPromise that, Func<IPromise> success) { |
|
389 | public static IPromise Chain(this IPromise that, Func<IPromise> success) { | |
368 | return Chain(that, success, null, null); |
|
390 | return Chain(that, success, null, null); | |
369 | } |
|
391 | } | |
370 |
|
392 | |||
371 | public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error, Func<Exception, IPromise<T>> cancel) { |
|
393 | public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error, Func<Exception, IPromise<T>> cancel) { | |
372 | Safe.ArgumentNotNull(that, "that"); |
|
394 | Safe.ArgumentNotNull(that, "that"); | |
373 |
|
395 | |||
374 | var d = new FuncChainTask<T>(success, error, cancel, false); |
|
396 | var d = new FuncChainTask<T>(success, error, cancel, false); | |
375 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
397 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
376 | if (success != null) |
|
398 | if (success != null) | |
377 | d.CancellationRequested(that.Cancel); |
|
399 | d.CancellationRequested(that.Cancel); | |
378 | return d; |
|
400 | return d; | |
379 | } |
|
401 | } | |
380 |
|
402 | |||
381 | public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error) { |
|
403 | public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error) { | |
382 | return Chain(that, success, error, null); |
|
404 | return Chain(that, success, error, null); | |
383 | } |
|
405 | } | |
384 |
|
406 | |||
385 | public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success) { |
|
407 | public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success) { | |
386 | return Chain(that, success, null, null); |
|
408 | return Chain(that, success, null, null); | |
387 | } |
|
409 | } | |
388 |
|
410 | |||
389 | public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel) { |
|
411 | public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel) { | |
390 | Safe.ArgumentNotNull(that, "that"); |
|
412 | Safe.ArgumentNotNull(that, "that"); | |
391 | var d = new FuncChainTask<T, T2>(success, error, cancel, false); |
|
413 | var d = new FuncChainTask<T, T2>(success, error, cancel, false); | |
392 | that.On(d.Resolve, d.Reject, d.CancelOperation); |
|
414 | that.On(d.Resolve, d.Reject, d.CancelOperation); | |
393 | if (success != null) |
|
415 | if (success != null) | |
394 | d.CancellationRequested(that.Cancel); |
|
416 | d.CancellationRequested(that.Cancel); | |
395 | return d; |
|
417 | return d; | |
396 | } |
|
418 | } | |
397 |
|
419 | |||
398 | public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error) { |
|
420 | public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error) { | |
399 | return Chain(that, success, error, null); |
|
421 | return Chain(that, success, error, null); | |
400 | } |
|
422 | } | |
401 |
|
423 | |||
402 | public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success) { |
|
424 | public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success) { | |
403 | return Chain(that, success, null, null); |
|
425 | return Chain(that, success, null, null); | |
404 | } |
|
426 | } | |
405 |
|
427 | |||
406 | #endregion |
|
428 | #endregion | |
407 |
|
429 | |||
408 |
|
430 | |||
409 | #if NET_4_5 |
|
431 | #if NET_4_5 | |
410 |
|
432 | |||
411 | public static PromiseAwaiter<T> GetAwaiter<T>(this IPromise<T> that) { |
|
433 | public static PromiseAwaiter<T> GetAwaiter<T>(this IPromise<T> that) { | |
412 | Safe.ArgumentNotNull(that, "that"); |
|
434 | Safe.ArgumentNotNull(that, "that"); | |
413 |
|
435 | |||
414 | return new PromiseAwaiter<T>(that); |
|
436 | return new PromiseAwaiter<T>(that); | |
415 | } |
|
437 | } | |
416 |
|
438 | |||
|
439 | public static PromiseAwaiter GetAwaiter(this IPromise that) { | |||
|
440 | Safe.ArgumentNotNull(that, "that"); | |||
|
441 | ||||
|
442 | return new PromiseAwaiter(that); | |||
|
443 | } | |||
|
444 | ||||
417 | #endif |
|
445 | #endif | |
418 | } |
|
446 | } | |
419 | } |
|
447 | } | |
420 |
|
448 |
General Comments 3
ok, latest stable version should be in default
You need to be logged in to leave comments.
Login now