##// END OF EJS Templates
small refactoring, cleanup.
cin -
r30:2fad2d1f4b03 default
parent child
Show More
@@ -1,89 +1,89
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5 using System.Windows.Forms;
5 using System.Windows.Forms;
6 using System.Diagnostics;
6 using System.Diagnostics;
7
7
8 namespace Implab.Fx
8 namespace Implab.Fx
9 {
9 {
10 public static class AnimationHelpers
10 public static class AnimationHelpers
11 {
11 {
12 public static Animation<TTarget> AnimateProperty<TTarget, TVal>(this Animation<TTarget> animation, Action<TTarget, TVal> setter, Func<TTarget, TVal> getter, TVal newValue, Func<TVal, TVal, int, int, TVal> fx) where TTarget : class
12 public static Animation<TTarget> AnimateProperty<TTarget, TVal>(this Animation<TTarget> animation, Action<TTarget, TVal> setter, Func<TTarget, TVal> getter, TVal newValue, Func<TVal, TVal, int, int, TVal> fx) where TTarget : class
13 {
13 {
14 if (animation == null)
14 if (animation == null)
15 throw new ArgumentNullException("animation");
15 throw new ArgumentNullException("animation");
16
16
17 TVal oldValue = getter(animation.Traget);
17 TVal oldValue = getter(animation.Traget);
18
18
19 animation.Step += (target, elaped, duration) =>
19 animation.Step += (target, elaped, duration) =>
20 {
20 {
21 var value = fx(oldValue, newValue, elaped, duration);
21 var value = fx(oldValue, newValue, elaped, duration);
22 setter(target, value);
22 setter(target, value);
23 };
23 };
24
24
25 return animation;
25 return animation;
26 }
26 }
27
27
28 public static Animation<T> AnimateTransparency<T>(this T ctl, float newValue) where T : Form
28 public static Animation<T> AnimateTransparency<T>(this T ctl, float newValue) where T : Form
29 {
29 {
30 var anim = new Animation<T>(ctl);
30 var anim = new Animation<T>(ctl);
31
31
32 anim.AnimateProperty(
32 anim.AnimateProperty(
33 (target, value) => target.Opacity = value,
33 (target, value) => target.Opacity = value,
34 target => target.Opacity,
34 target => target.Opacity,
35 newValue,
35 newValue,
36 (ov, nv, el, du) => ov + ((float)el / du) * (nv - ov)
36 (ov, nv, el, du) => ov + ((float)el / du) * (nv - ov)
37 );
37 );
38 return anim;
38 return anim;
39 }
39 }
40
40
41 public static Promise<T> CloseFadeOut<T>(this T ctl) where T : Form
41 public static IPromise<T> CloseFadeOut<T>(this T ctl) where T : Form
42 {
42 {
43 var anim = ctl.AnimateTransparency(0);
43 var anim = ctl.AnimateTransparency(0);
44
44
45 return anim.Play().DispatchToControl(ctl).Then(frm => frm.Close());
45 return anim.Play().DispatchToControl(ctl).Then(frm => frm.Close());
46 }
46 }
47
47
48 public static Promise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
48 public static IPromise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
49 {
49 {
50 if (that == null)
50 if (that == null)
51 throw new ArgumentNullException("that");
51 throw new ArgumentNullException("that");
52 if (overlay == null)
52 if (overlay == null)
53 throw new ArgumentNullException("overlay");
53 throw new ArgumentNullException("overlay");
54
54
55 // setup overlay
55 // setup overlay
56 overlay.Opacity = 0;
56 overlay.Opacity = 0;
57 overlay.FormBorderStyle = FormBorderStyle.None;
57 overlay.FormBorderStyle = FormBorderStyle.None;
58 overlay.ShowInTaskbar = false;
58 overlay.ShowInTaskbar = false;
59
59
60 that.AddOwnedForm(overlay);
60 that.AddOwnedForm(overlay);
61
61
62 EventHandler handler = (object sender, EventArgs args) =>
62 EventHandler handler = (object sender, EventArgs args) =>
63 {
63 {
64 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
64 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
65 };
65 };
66
66
67 // attach handlers
67 // attach handlers
68 that.Move += handler;
68 that.Move += handler;
69 that.Resize += handler;
69 that.Resize += handler;
70 that.Shown += handler;
70 that.Shown += handler;
71
71
72 // remove handlers to release overlay
72 // remove handlers to release overlay
73 overlay.FormClosed += (sender, args) =>
73 overlay.FormClosed += (sender, args) =>
74 {
74 {
75 that.Move -= handler;
75 that.Move -= handler;
76 that.Resize -= handler;
76 that.Resize -= handler;
77 that.Shown -= handler;
77 that.Shown -= handler;
78 };
78 };
79
79
80 overlay.Show(that);
80 overlay.Show(that);
81 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
81 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
82
82
83 return overlay
83 return overlay
84 .AnimateTransparency(1)
84 .AnimateTransparency(1)
85 .Play()
85 .Play()
86 .DispatchToControl(overlay);
86 .DispatchToControl(overlay);
87 }
87 }
88 }
88 }
89 }
89 }
@@ -1,333 +1,333
1 using System;
1 using System;
2 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using Microsoft.VisualStudio.TestTools.UnitTesting;
3 using System.Reflection;
3 using System.Reflection;
4 using System.Threading;
4 using System.Threading;
5 using Implab.Parallels;
5 using Implab.Parallels;
6
6
7 namespace Implab.Test {
7 namespace Implab.Test {
8 [TestClass]
8 [TestClass]
9 public class AsyncTests {
9 public class AsyncTests {
10 [TestMethod]
10 [TestMethod]
11 public void ResolveTest() {
11 public void ResolveTest() {
12 int res = -1;
12 int res = -1;
13 var p = new Promise<int>();
13 var p = new Promise<int>();
14 p.Then(x => res = x);
14 p.Then(x => res = x);
15 p.Resolve(100);
15 p.Resolve(100);
16
16
17 Assert.AreEqual(100, res);
17 Assert.AreEqual(100, res);
18 }
18 }
19
19
20 [TestMethod]
20 [TestMethod]
21 public void RejectTest() {
21 public void RejectTest() {
22 int res = -1;
22 int res = -1;
23 Exception err = null;
23 Exception err = null;
24
24
25 var p = new Promise<int>();
25 var p = new Promise<int>();
26 p.Then(x => res = x, e => err = e);
26 p.Then(x => res = x, e => err = e);
27 p.Reject(new ApplicationException("error"));
27 p.Reject(new ApplicationException("error"));
28
28
29 Assert.AreEqual(res, -1);
29 Assert.AreEqual(res, -1);
30 Assert.AreEqual(err.Message, "error");
30 Assert.AreEqual(err.Message, "error");
31
31
32 }
32 }
33
33
34 [TestMethod]
34 [TestMethod]
35 public void JoinSuccessTest() {
35 public void JoinSuccessTest() {
36 var p = new Promise<int>();
36 var p = new Promise<int>();
37 p.Resolve(100);
37 p.Resolve(100);
38 Assert.AreEqual(p.Join(), 100);
38 Assert.AreEqual(p.Join(), 100);
39 }
39 }
40
40
41 [TestMethod]
41 [TestMethod]
42 public void JoinFailTest() {
42 public void JoinFailTest() {
43 var p = new Promise<int>();
43 var p = new Promise<int>();
44 p.Reject(new ApplicationException("failed"));
44 p.Reject(new ApplicationException("failed"));
45
45
46 try {
46 try {
47 p.Join();
47 p.Join();
48 throw new ApplicationException("WRONG!");
48 throw new ApplicationException("WRONG!");
49 } catch (TargetInvocationException err) {
49 } catch (TargetInvocationException err) {
50 Assert.AreEqual(err.InnerException.Message, "failed");
50 Assert.AreEqual(err.InnerException.Message, "failed");
51 } catch {
51 } catch {
52 Assert.Fail("Got wrong excaption");
52 Assert.Fail("Got wrong excaption");
53 }
53 }
54 }
54 }
55
55
56 [TestMethod]
56 [TestMethod]
57 public void MapTest() {
57 public void MapTest() {
58 var p = new Promise<int>();
58 var p = new Promise<int>();
59
59
60 var p2 = p.Map(x => x.ToString());
60 var p2 = p.Map(x => x.ToString());
61 p.Resolve(100);
61 p.Resolve(100);
62
62
63 Assert.AreEqual(p2.Join(), "100");
63 Assert.AreEqual(p2.Join(), "100");
64 }
64 }
65
65
66 [TestMethod]
66 [TestMethod]
67 public void FixErrorTest() {
67 public void FixErrorTest() {
68 var p = new Promise<int>();
68 var p = new Promise<int>();
69
69
70 var p2 = p.Error(e => 101);
70 var p2 = p.Error(e => 101);
71
71
72 p.Reject(new Exception());
72 p.Reject(new Exception());
73
73
74 Assert.AreEqual(p2.Join(), 101);
74 Assert.AreEqual(p2.Join(), 101);
75 }
75 }
76
76
77 [TestMethod]
77 [TestMethod]
78 public void ChainTest() {
78 public void ChainTest() {
79 var p1 = new Promise<int>();
79 var p1 = new Promise<int>();
80
80
81 var p3 = p1.Chain(x => {
81 var p3 = p1.Chain(x => {
82 var p2 = new Promise<string>();
82 var p2 = new Promise<string>();
83 p2.Resolve(x.ToString());
83 p2.Resolve(x.ToString());
84 return p2;
84 return p2;
85 });
85 });
86
86
87 p1.Resolve(100);
87 p1.Resolve(100);
88
88
89 Assert.AreEqual(p3.Join(), "100");
89 Assert.AreEqual(p3.Join(), "100");
90 }
90 }
91
91
92 [TestMethod]
92 [TestMethod]
93 public void PoolTest() {
93 public void PoolTest() {
94 var pid = Thread.CurrentThread.ManagedThreadId;
94 var pid = Thread.CurrentThread.ManagedThreadId;
95 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
95 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
96
96
97 Assert.AreNotEqual(pid, p.Join());
97 Assert.AreNotEqual(pid, p.Join());
98 }
98 }
99
99
100 [TestMethod]
100 [TestMethod]
101 public void WorkerPoolSizeTest() {
101 public void WorkerPoolSizeTest() {
102 var pool = new WorkerPool(5, 10, 0);
102 var pool = new WorkerPool(5, 10, 0);
103
103
104 Assert.AreEqual(5, pool.PoolSize);
104 Assert.AreEqual(5, pool.PoolSize);
105
105
106 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
106 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
107 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
107 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
108 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
108 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
109
109
110 Assert.AreEqual(5, pool.PoolSize);
110 Assert.AreEqual(5, pool.PoolSize);
111
111
112 for (int i = 0; i < 100; i++)
112 for (int i = 0; i < 100; i++)
113 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
113 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
114 Thread.Sleep(200);
114 Thread.Sleep(200);
115 Assert.AreEqual(10, pool.PoolSize);
115 Assert.AreEqual(10, pool.PoolSize);
116
116
117 pool.Dispose();
117 pool.Dispose();
118 }
118 }
119
119
120 [TestMethod]
120 [TestMethod]
121 public void WorkerPoolCorrectTest() {
121 public void WorkerPoolCorrectTest() {
122 var pool = new WorkerPool(0,1000,100);
122 var pool = new WorkerPool(0,1000,100);
123
123
124 int iterations = 1000;
124 int iterations = 1000;
125 int pending = iterations;
125 int pending = iterations;
126 var stop = new ManualResetEvent(false);
126 var stop = new ManualResetEvent(false);
127
127
128 var count = 0;
128 var count = 0;
129 for (int i = 0; i < iterations; i++) {
129 for (int i = 0; i < iterations; i++) {
130 pool
130 pool
131 .Invoke(() => 1)
131 .Invoke(() => 1)
132 .Then(x => Interlocked.Add(ref count, x))
132 .Then(x => Interlocked.Add(ref count, x))
133 .Then(x => Math.Log10(x))
133 .Then(x => Math.Log10(x))
134 .Anyway(() => {
134 .Anyway(() => {
135 Interlocked.Decrement(ref pending);
135 Interlocked.Decrement(ref pending);
136 if (pending == 0)
136 if (pending == 0)
137 stop.Set();
137 stop.Set();
138 });
138 });
139 }
139 }
140
140
141 stop.WaitOne();
141 stop.WaitOne();
142
142
143 Assert.AreEqual(iterations, count);
143 Assert.AreEqual(iterations, count);
144 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
144 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
145 pool.Dispose();
145 pool.Dispose();
146
146
147 }
147 }
148
148
149 [TestMethod]
149 [TestMethod]
150 public void WorkerPoolDisposeTest() {
150 public void WorkerPoolDisposeTest() {
151 var pool = new WorkerPool(5, 20);
151 var pool = new WorkerPool(5, 20);
152 Assert.AreEqual(5, pool.PoolSize);
152 Assert.AreEqual(5, pool.PoolSize);
153 pool.Dispose();
153 pool.Dispose();
154 Thread.Sleep(500);
154 Thread.Sleep(500);
155 Assert.AreEqual(0, pool.PoolSize);
155 Assert.AreEqual(0, pool.PoolSize);
156 pool.Dispose();
156 pool.Dispose();
157 }
157 }
158
158
159 [TestMethod]
159 [TestMethod]
160 public void MTQueueTest() {
160 public void MTQueueTest() {
161 var queue = new MTQueue<int>();
161 var queue = new MTQueue<int>();
162 int res;
162 int res;
163
163
164 queue.Enqueue(10);
164 queue.Enqueue(10);
165 Assert.IsTrue(queue.TryDequeue(out res));
165 Assert.IsTrue(queue.TryDequeue(out res));
166 Assert.AreEqual(10, res);
166 Assert.AreEqual(10, res);
167 Assert.IsFalse(queue.TryDequeue(out res));
167 Assert.IsFalse(queue.TryDequeue(out res));
168
168
169 for (int i = 0; i < 1000; i++)
169 for (int i = 0; i < 1000; i++)
170 queue.Enqueue(i);
170 queue.Enqueue(i);
171
171
172 for (int i = 0; i < 1000; i++) {
172 for (int i = 0; i < 1000; i++) {
173 queue.TryDequeue(out res);
173 queue.TryDequeue(out res);
174 Assert.AreEqual(i, res);
174 Assert.AreEqual(i, res);
175 }
175 }
176
176
177 int writers = 0;
177 int writers = 0;
178 int readers = 0;
178 int readers = 0;
179 var stop = new ManualResetEvent(false);
179 var stop = new ManualResetEvent(false);
180 int total = 0;
180 int total = 0;
181
181
182 int itemsPerWriter = 1000;
182 int itemsPerWriter = 1000;
183 int writersCount = 3;
183 int writersCount = 3;
184
184
185 for (int i = 0; i < writersCount; i++) {
185 for (int i = 0; i < writersCount; i++) {
186 Interlocked.Increment(ref writers);
186 Interlocked.Increment(ref writers);
187 var wn = i;
187 var wn = i;
188 AsyncPool
188 AsyncPool
189 .InvokeNewThread(() => {
189 .InvokeNewThread(() => {
190 for (int ii = 0; ii < itemsPerWriter; ii++) {
190 for (int ii = 0; ii < itemsPerWriter; ii++) {
191 queue.Enqueue(1);
191 queue.Enqueue(1);
192 }
192 }
193 return 1;
193 return 1;
194 })
194 })
195 .Anyway(() => Interlocked.Decrement(ref writers));
195 .Anyway(() => Interlocked.Decrement(ref writers));
196 }
196 }
197
197
198 for (int i = 0; i < 10; i++) {
198 for (int i = 0; i < 10; i++) {
199 Interlocked.Increment(ref readers);
199 Interlocked.Increment(ref readers);
200 var wn = i;
200 var wn = i;
201 AsyncPool
201 AsyncPool
202 .InvokeNewThread(() => {
202 .InvokeNewThread(() => {
203 int t;
203 int t;
204 do {
204 do {
205 while (queue.TryDequeue(out t))
205 while (queue.TryDequeue(out t))
206 Interlocked.Add(ref total, t);
206 Interlocked.Add(ref total, t);
207 } while (writers > 0);
207 } while (writers > 0);
208 return 1;
208 return 1;
209 })
209 })
210 .Anyway(() => {
210 .Anyway(() => {
211 Interlocked.Decrement(ref readers);
211 Interlocked.Decrement(ref readers);
212 if (readers == 0)
212 if (readers == 0)
213 stop.Set();
213 stop.Set();
214 });
214 });
215 }
215 }
216
216
217 stop.WaitOne();
217 stop.WaitOne();
218
218
219 Assert.AreEqual(itemsPerWriter * writersCount, total);
219 Assert.AreEqual(itemsPerWriter * writersCount, total);
220 }
220 }
221
221
222 [TestMethod]
222 [TestMethod]
223 public void ParallelMapTest() {
223 public void ParallelMapTest() {
224
224
225 int count = 100000;
225 int count = 100000;
226
226
227 double[] args = new double[count];
227 double[] args = new double[count];
228 var rand = new Random();
228 var rand = new Random();
229
229
230 for (int i = 0; i < count; i++)
230 for (int i = 0; i < count; i++)
231 args[i] = rand.NextDouble();
231 args[i] = rand.NextDouble();
232
232
233 var t = Environment.TickCount;
233 var t = Environment.TickCount;
234 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
234 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
235
235
236 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
236 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
237
237
238 t = Environment.TickCount;
238 t = Environment.TickCount;
239 for (int i = 0; i < count; i++)
239 for (int i = 0; i < count; i++)
240 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
240 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
241 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
241 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
242 }
242 }
243
243
244 [TestMethod]
244 [TestMethod]
245 public void ChainedMapTest() {
245 public void ChainedMapTest() {
246
246
247 using (var pool = new WorkerPool(0,100,0)) {
247 using (var pool = new WorkerPool(0,100,0)) {
248 int count = 10000;
248 int count = 10000;
249
249
250 double[] args = new double[count];
250 double[] args = new double[count];
251 var rand = new Random();
251 var rand = new Random();
252
252
253 for (int i = 0; i < count; i++)
253 for (int i = 0; i < count; i++)
254 args[i] = rand.NextDouble();
254 args[i] = rand.NextDouble();
255
255
256 var t = Environment.TickCount;
256 var t = Environment.TickCount;
257 var res = args
257 var res = args
258 .ChainedMap2(
258 .ChainedMap(
259 x => pool.Invoke(
259 x => pool.Invoke(
260 () => Math.Sin(x * x)
260 () => Math.Sin(x * x)
261 ),
261 ),
262 4
262 4
263 )
263 )
264 .Join();
264 .Join();
265
265
266 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
266 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
267
267
268 t = Environment.TickCount;
268 t = Environment.TickCount;
269 for (int i = 0; i < count; i++)
269 for (int i = 0; i < count; i++)
270 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
270 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
271 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
271 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
272 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
272 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
273 }
273 }
274 }
274 }
275
275
276 [TestMethod]
276 [TestMethod]
277 public void ParallelForEachTest() {
277 public void ParallelForEachTest() {
278
278
279 int count = 100000;
279 int count = 100000;
280
280
281 int[] args = new int[count];
281 int[] args = new int[count];
282 var rand = new Random();
282 var rand = new Random();
283
283
284 for (int i = 0; i < count; i++)
284 for (int i = 0; i < count; i++)
285 args[i] = (int)(rand.NextDouble() * 100);
285 args[i] = (int)(rand.NextDouble() * 100);
286
286
287 int result = 0;
287 int result = 0;
288
288
289 var t = Environment.TickCount;
289 var t = Environment.TickCount;
290 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
290 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
291
291
292 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
292 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
293
293
294 int result2 = 0;
294 int result2 = 0;
295
295
296 t = Environment.TickCount;
296 t = Environment.TickCount;
297 for (int i = 0; i < count; i++)
297 for (int i = 0; i < count; i++)
298 result2 += args[i];
298 result2 += args[i];
299 Assert.AreEqual(result2, result);
299 Assert.AreEqual(result2, result);
300 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
300 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
301 }
301 }
302
302
303 [TestMethod]
303 [TestMethod]
304 public void ComplexCase1Test() {
304 public void ComplexCase1Test() {
305 var flags = new bool[3];
305 var flags = new bool[3];
306
306
307 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
307 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
308
308
309 var p = PromiseHelper
309 var p = PromiseHelper
310 .Sleep(200, "Alan")
310 .Sleep(200, "Alan")
311 .Cancelled(() => flags[0] = true)
311 .Cancelled(() => flags[0] = true)
312 .Chain(x =>
312 .Chain(x =>
313 PromiseHelper
313 PromiseHelper
314 .Sleep(200, "Hi, " + x)
314 .Sleep(200, "Hi, " + x)
315 .Map(y => y)
315 .Map(y => y)
316 .Cancelled(() => flags[1] = true)
316 .Cancelled(() => flags[1] = true)
317 )
317 )
318 .Cancelled(() => flags[2] = true);
318 .Cancelled(() => flags[2] = true);
319 Thread.Sleep(300);
319 Thread.Sleep(300);
320 p.Cancel();
320 p.Cancel();
321 try {
321 try {
322 Assert.AreEqual(p.Join(), "Hi, Alan");
322 Assert.AreEqual(p.Join(), "Hi, Alan");
323 Assert.Fail("Shouldn't get here");
323 Assert.Fail("Shouldn't get here");
324 } catch (OperationCanceledException) {
324 } catch (OperationCanceledException) {
325 }
325 }
326
326
327 Assert.IsFalse(flags[0]);
327 Assert.IsFalse(flags[0]);
328 Assert.IsTrue(flags[1]);
328 Assert.IsTrue(flags[1]);
329 Assert.IsTrue(flags[2]);
329 Assert.IsTrue(flags[2]);
330 }
330 }
331 }
331 }
332 }
332 }
333
333
@@ -1,172 +1,173
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Diagnostics;
3 using System.Diagnostics;
4 using System.Linq;
4 using System.Linq;
5 using System.Text;
5 using System.Text;
6 using System.Threading;
6 using System.Threading;
7
7
8 namespace Implab.Parallels {
8 namespace Implab.Parallels {
9 public static class ArrayTraits {
9 public static class ArrayTraits {
10 class ArrayIterator<TSrc> : DispatchPool<int> {
10 class ArrayIterator<TSrc> : DispatchPool<int> {
11 readonly Action<TSrc> m_action;
11 readonly Action<TSrc> m_action;
12 readonly TSrc[] m_source;
12 readonly TSrc[] m_source;
13 readonly Promise<int> m_promise = new Promise<int>();
13 readonly Promise<int> m_promise = new Promise<int>();
14
14
15 int m_pending;
15 int m_pending;
16 int m_next;
16 int m_next;
17
17
18 public ArrayIterator(TSrc[] source, Action<TSrc> action, int threads)
18 public ArrayIterator(TSrc[] source, Action<TSrc> action, int threads)
19 : base(threads) {
19 : base(threads) {
20
20
21 Debug.Assert(source != null);
21 Debug.Assert(source != null);
22 Debug.Assert(action != null);
22 Debug.Assert(action != null);
23
23
24 m_next = 0;
24 m_next = 0;
25 m_source = source;
25 m_source = source;
26 m_pending = source.Length;
26 m_pending = source.Length;
27 m_action = action;
27 m_action = action;
28
28
29 m_promise.Anyway(() => Dispose());
29 m_promise.Anyway(() => Dispose());
30 m_promise.Cancelled(() => Dispose());
30 m_promise.Cancelled(() => Dispose());
31
31
32 InitPool();
32 InitPool();
33 }
33 }
34
34
35 public Promise<int> Promise {
35 public Promise<int> Promise {
36 get {
36 get {
37 return m_promise;
37 return m_promise;
38 }
38 }
39 }
39 }
40
40
41 protected override bool TryDequeue(out int unit) {
41 protected override bool TryDequeue(out int unit) {
42 unit = Interlocked.Increment(ref m_next) - 1;
42 unit = Interlocked.Increment(ref m_next) - 1;
43 return unit >= m_source.Length ? false : true;
43 return unit >= m_source.Length ? false : true;
44 }
44 }
45
45
46 protected override void InvokeUnit(int unit) {
46 protected override void InvokeUnit(int unit) {
47 try {
47 try {
48 m_action(m_source[unit]);
48 m_action(m_source[unit]);
49 var pending = Interlocked.Decrement(ref m_pending);
49 var pending = Interlocked.Decrement(ref m_pending);
50 if (pending == 0)
50 if (pending == 0)
51 m_promise.Resolve(m_source.Length);
51 m_promise.Resolve(m_source.Length);
52 } catch (Exception e) {
52 } catch (Exception e) {
53 m_promise.Reject(e);
53 m_promise.Reject(e);
54 }
54 }
55 }
55 }
56 }
56 }
57
57
58 class ArrayMapper<TSrc, TDst>: DispatchPool<int> {
58 class ArrayMapper<TSrc, TDst>: DispatchPool<int> {
59 readonly Func<TSrc, TDst> m_transform;
59 readonly Func<TSrc, TDst> m_transform;
60 readonly TSrc[] m_source;
60 readonly TSrc[] m_source;
61 readonly TDst[] m_dest;
61 readonly TDst[] m_dest;
62 readonly Promise<TDst[]> m_promise = new Promise<TDst[]>();
62 readonly Promise<TDst[]> m_promise = new Promise<TDst[]>();
63
63
64 int m_pending;
64 int m_pending;
65 int m_next;
65 int m_next;
66
66
67 public ArrayMapper(TSrc[] source, Func<TSrc, TDst> transform, int threads)
67 public ArrayMapper(TSrc[] source, Func<TSrc, TDst> transform, int threads)
68 : base(threads) {
68 : base(threads) {
69
69
70 Debug.Assert (source != null);
70 Debug.Assert (source != null);
71 Debug.Assert( transform != null);
71 Debug.Assert( transform != null);
72
72
73 m_next = 0;
73 m_next = 0;
74 m_source = source;
74 m_source = source;
75 m_dest = new TDst[source.Length];
75 m_dest = new TDst[source.Length];
76 m_pending = source.Length;
76 m_pending = source.Length;
77 m_transform = transform;
77 m_transform = transform;
78
78
79 m_promise.Anyway(() => Dispose());
79 m_promise.Anyway(() => Dispose());
80 m_promise.Cancelled(() => Dispose());
80 m_promise.Cancelled(() => Dispose());
81
81
82 InitPool();
82 InitPool();
83 }
83 }
84
84
85 public Promise<TDst[]> Promise {
85 public Promise<TDst[]> Promise {
86 get {
86 get {
87 return m_promise;
87 return m_promise;
88 }
88 }
89 }
89 }
90
90
91 protected override bool TryDequeue(out int unit) {
91 protected override bool TryDequeue(out int unit) {
92 unit = Interlocked.Increment(ref m_next) - 1;
92 unit = Interlocked.Increment(ref m_next) - 1;
93 return unit >= m_source.Length ? false : true;
93 return unit >= m_source.Length ? false : true;
94 }
94 }
95
95
96 protected override void InvokeUnit(int unit) {
96 protected override void InvokeUnit(int unit) {
97 try {
97 try {
98 m_dest[unit] = m_transform(m_source[unit]);
98 m_dest[unit] = m_transform(m_source[unit]);
99 var pending = Interlocked.Decrement(ref m_pending);
99 var pending = Interlocked.Decrement(ref m_pending);
100 if (pending == 0)
100 if (pending == 0)
101 m_promise.Resolve(m_dest);
101 m_promise.Resolve(m_dest);
102 } catch (Exception e) {
102 } catch (Exception e) {
103 m_promise.Reject(e);
103 m_promise.Reject(e);
104 }
104 }
105 }
105 }
106 }
106 }
107
107
108 public static Promise<TDst[]> ParallelMap<TSrc, TDst> (this TSrc[] source, Func<TSrc,TDst> transform, int threads) {
108 public static IPromise<TDst[]> ParallelMap<TSrc, TDst> (this TSrc[] source, Func<TSrc,TDst> transform, int threads) {
109 if (source == null)
109 if (source == null)
110 throw new ArgumentNullException("source");
110 throw new ArgumentNullException("source");
111 if (transform == null)
111 if (transform == null)
112 throw new ArgumentNullException("transform");
112 throw new ArgumentNullException("transform");
113
113
114 var mapper = new ArrayMapper<TSrc, TDst>(source, transform, threads);
114 var mapper = new ArrayMapper<TSrc, TDst>(source, transform, threads);
115 return mapper.Promise;
115 return mapper.Promise;
116 }
116 }
117
117
118 public static Promise<int> ParallelForEach<TSrc>(this TSrc[] source, Action<TSrc> action, int threads) {
118 public static IPromise<int> ParallelForEach<TSrc>(this TSrc[] source, Action<TSrc> action, int threads) {
119 if (source == null)
119 if (source == null)
120 throw new ArgumentNullException("source");
120 throw new ArgumentNullException("source");
121 if (action == null)
121 if (action == null)
122 throw new ArgumentNullException("action");
122 throw new ArgumentNullException("action");
123
123
124 var iter = new ArrayIterator<TSrc>(source, action, threads);
124 var iter = new ArrayIterator<TSrc>(source, action, threads);
125 return iter.Promise;
125 return iter.Promise;
126 }
126 }
127
127
128 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, ChainedOperation<TSrc, TDst> transform, int threads) {
128 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, ChainedOperation<TSrc, TDst> transform, int threads) {
129 if (source == null)
129 if (source == null)
130 throw new ArgumentNullException("source");
130 throw new ArgumentNullException("source");
131 if (transform == null)
131 if (transform == null)
132 throw new ArgumentNullException("transform");
132 throw new ArgumentNullException("transform");
133 if (threads <= 0)
133 if (threads <= 0)
134 throw new ArgumentOutOfRangeException("Threads number must be greater then zero");
134 throw new ArgumentOutOfRangeException("Threads number must be greater then zero");
135
135
136 var promise = new Promise<TDst[]>();
136 var promise = new Promise<TDst[]>();
137 var res = new TDst[source.Length];
137 var res = new TDst[source.Length];
138 var pending = source.Length;
138 var pending = source.Length;
139
139 var semaphore = new Semaphore(threads, threads);
140 var semaphore = new Semaphore(threads, threads);
140
141
141 AsyncPool.InvokeNewThread(() => {
142 AsyncPool.InvokeNewThread(() => {
142 for (int i = 0; i < source.Length; i++) {
143 for (int i = 0; i < source.Length; i++) {
143 if(promise.IsResolved)
144 if(promise.IsResolved)
144 break; // stop processing in case of error or cancellation
145 break; // stop processing in case of error or cancellation
145 var idx = i;
146 var idx = i;
146 semaphore.WaitOne();
147 semaphore.WaitOne();
147 try {
148 try {
148 var p1 = transform(source[i]);
149 var p1 = transform(source[i]);
149 p1.Anyway(() => semaphore.Release());
150 p1.Anyway(() => semaphore.Release());
150 p1.Cancelled(() => semaphore.Release());
151 p1.Cancelled(() => semaphore.Release());
151 p1.Then(
152 p1.Then(
152 x => {
153 x => {
153 res[idx] = x;
154 res[idx] = x;
154 var left = Interlocked.Decrement(ref pending);
155 var left = Interlocked.Decrement(ref pending);
155 if (left == 0)
156 if (left == 0)
156 promise.Resolve(res);
157 promise.Resolve(res);
157 },
158 },
158 e => promise.Reject(e)
159 e => promise.Reject(e)
159 );
160 );
160
161
161 } catch (Exception e) {
162 } catch (Exception e) {
162 promise.Reject(e);
163 promise.Reject(e);
163 }
164 }
164 }
165 }
165 return 0;
166 return 0;
166 });
167 });
167
168
168 return promise.Anyway(() => semaphore.Dispose());
169 return promise.Anyway(() => semaphore.Dispose());
169 }
170 }
170
171
171 }
172 }
172 }
173 }
@@ -1,332 +1,335
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Linq;
3 using System.Linq;
4 using System.Text;
4 using System.Text;
5 using System.Threading;
5 using System.Threading;
6 using System.Diagnostics;
6 using System.Diagnostics;
7
7
8 namespace Implab.Parallels {
8 namespace Implab.Parallels {
9 public abstract class DispatchPool<TUnit> : IDisposable {
9 public abstract class DispatchPool<TUnit> : IDisposable {
10 readonly int m_minThreads;
10 readonly int m_minThreads;
11 readonly int m_maxThreads;
11 readonly int m_maxThreads;
12
12
13 int m_createdThreads = 0; // the current size of the pool
13 int m_createdThreads = 0; // the current size of the pool
14 int m_activeThreads = 0; // the count of threads which are active
14 int m_activeThreads = 0; // the count of threads which are active
15 int m_sleepingThreads = 0; // the count of currently inactive threads
15 int m_sleepingThreads = 0; // the count of currently inactive threads
16 int m_maxRunningThreads = 0; // the meximum reached size of the pool
16 int m_maxRunningThreads = 0; // the meximum reached size of the pool
17 int m_exitRequired = 0; // the pool is going to shutdown, all unused workers are released
17 int m_exitRequired = 0; // the pool is going to shutdown, all unused workers are released
18 int m_releaseTimeout = 100; // the timeout while the working thread will wait for the new tasks before exit
18 int m_releaseTimeout = 100; // the timeout while the working thread will wait for the new tasks before exit
19 int m_wakeEvents = 0; // the count of wake events
19 int m_wakeEvents = 0; // the count of wake events
20
20
21 AutoResetEvent m_hasTasks = new AutoResetEvent(false);
21 AutoResetEvent m_hasTasks = new AutoResetEvent(false);
22
22
23 protected DispatchPool(int min, int max) {
23 protected DispatchPool(int min, int max) {
24 if (min < 0)
24 if (min < 0)
25 throw new ArgumentOutOfRangeException("min");
25 throw new ArgumentOutOfRangeException("min");
26 if (max <= 0)
26 if (max <= 0)
27 throw new ArgumentOutOfRangeException("max");
27 throw new ArgumentOutOfRangeException("max");
28
28
29 if (min > max)
29 if (min > max)
30 min = max;
30 min = max;
31 m_minThreads = min;
31 m_minThreads = min;
32 m_maxThreads = max;
32 m_maxThreads = max;
33 }
33 }
34
34
35 protected DispatchPool(int threads)
35 protected DispatchPool(int threads)
36 : this(threads, threads) {
36 : this(threads, threads) {
37 }
37 }
38
38
39 protected DispatchPool() {
39 protected DispatchPool() {
40 int maxThreads, maxCP;
40 int maxThreads, maxCP;
41 ThreadPool.GetMaxThreads(out maxThreads, out maxCP);
41 ThreadPool.GetMaxThreads(out maxThreads, out maxCP);
42
42
43 m_minThreads = 0;
43 m_minThreads = 0;
44 m_maxThreads = maxThreads;
44 m_maxThreads = maxThreads;
45 }
45 }
46
46
47 protected void InitPool() {
47 protected void InitPool() {
48 for (int i = 0; i < m_minThreads; i++)
48 for (int i = 0; i < m_minThreads; i++)
49 StartWorker();
49 StartWorker();
50 }
50 }
51
51
52 public int PoolSize {
52 public int PoolSize {
53 get {
53 get {
54 return m_createdThreads;
54 return m_createdThreads;
55 }
55 }
56 }
56 }
57
57
58 public int ActiveThreads {
58 public int ActiveThreads {
59 get {
59 get {
60 return m_activeThreads;
60 return m_activeThreads;
61 }
61 }
62 }
62 }
63
63
64 public int MaxRunningThreads {
64 public int MaxRunningThreads {
65 get {
65 get {
66 return m_maxRunningThreads;
66 return m_maxRunningThreads;
67 }
67 }
68 }
68 }
69
69
70 protected bool IsDisposed {
70 protected bool IsDisposed {
71 get {
71 get {
72 return m_exitRequired != 0;
72 return m_exitRequired != 0;
73 }
73 }
74 }
74 }
75
75
76 protected abstract bool TryDequeue(out TUnit unit);
76 protected abstract bool TryDequeue(out TUnit unit);
77
77
78 #region thread execution traits
78 #region thread execution traits
79 int SignalThread() {
79 int SignalThread() {
80 var signals = Interlocked.Increment(ref m_wakeEvents);
80 var signals = Interlocked.Increment(ref m_wakeEvents);
81 if(signals == 1)
81 if(signals == 1)
82 m_hasTasks.Set();
82 m_hasTasks.Set();
83 return signals;
83 return signals;
84 }
84 }
85
85
86 bool FetchSignalOrWait(int timeout) {
86 bool FetchSignalOrWait(int timeout) {
87 var start = Environment.TickCount;
87 var start = Environment.TickCount;
88
88
89 // ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΏΠΎΡ‚ΠΎΠΊ Π²Π»Π°Π΄Π΅Π΅Ρ‚ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΎΠΉ ΠΈ ΠΏΡ€ΠΈ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ сигнала Π΄ΠΎΠ»ΠΆΠ΅Π½
89 // ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΏΠΎΡ‚ΠΎΠΊ Π²Π»Π°Π΄Π΅Π΅Ρ‚ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΎΠΉ ΠΈ ΠΏΡ€ΠΈ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ сигнала Π΄ΠΎΠ»ΠΆΠ΅Π½
90 // Π΅Π΅ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΎΠΆΠΈΠ΄Π°ΡŽΡ‰ΠΈΠΉ ΠΏΠΎΡ‚ΠΎΠΊ смог
90 // Π΅Π΅ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΎΠΆΠΈΠ΄Π°ΡŽΡ‰ΠΈΠΉ ΠΏΠΎΡ‚ΠΎΠΊ смог
91 bool hasLock = false;
91 bool hasLock = false;
92 do {
92 do {
93 int signals;
93 int signals;
94 do {
94 do {
95 signals = m_wakeEvents;
95 signals = m_wakeEvents;
96 if (signals == 0)
96 if (signals == 0)
97 break;
97 break;
98 } while (Interlocked.CompareExchange(ref m_wakeEvents, signals - 1, signals) != signals);
98 } while (Interlocked.CompareExchange(ref m_wakeEvents, signals - 1, signals) != signals);
99
99
100 if (signals >= 1) {
100 if (signals >= 1) {
101 if (signals > 1 && hasLock)
101 if (signals > 1 && hasLock)
102 m_hasTasks.Set();
102 m_hasTasks.Set();
103 return true;
103 return true;
104 }
104 }
105
105
106 if (timeout != -1)
106 if (timeout != -1)
107 timeout = Math.Max(0, timeout - (Environment.TickCount - start));
107 timeout = Math.Max(0, timeout - (Environment.TickCount - start));
108
108
109 // Ссли сигналов большС Π½Π΅ ΠΎΡΡ‚Π°Π»ΠΎΡΡŒ, Ρ‚ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ дошСл сюда сбросит событиС
109 // Ссли сигналов большС Π½Π΅ ΠΎΡΡ‚Π°Π»ΠΎΡΡŒ, Ρ‚ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ дошСл сюда сбросит событиС
110 // ΠΈ ΡƒΠΉΠ΄Π΅Ρ‚ Π½Π° пустой Ρ†ΠΈΠΊΠ», послС Ρ‡Π΅Π³ΠΎ заблокируСтся
110 // ΠΈ ΡƒΠΉΠ΄Π΅Ρ‚ Π½Π° пустой Ρ†ΠΈΠΊΠ», послС Ρ‡Π΅Π³ΠΎ заблокируСтся
111
111
112 hasLock = true;
112 hasLock = true;
113 } while (m_hasTasks.WaitOne(timeout));
113 } while (m_hasTasks.WaitOne(timeout));
114
114
115 return false;
115 return false;
116 }
116 }
117
117
118 bool Sleep(int timeout) {
118 bool Sleep(int timeout) {
119 Interlocked.Increment(ref m_sleepingThreads);
119 Interlocked.Increment(ref m_sleepingThreads);
120 if (FetchSignalOrWait(timeout)) {
120 if (FetchSignalOrWait(timeout)) {
121 Interlocked.Decrement(ref m_sleepingThreads);
121 Interlocked.Decrement(ref m_sleepingThreads);
122 return true;
122 return true;
123 } else {
123 } else {
124 Interlocked.Decrement(ref m_sleepingThreads);
124 Interlocked.Decrement(ref m_sleepingThreads);
125 return false;
125 return false;
126 }
126 }
127 }
127 }
128 #endregion
128 #endregion
129
129
130 /// <summary>
130 /// <summary>
131 /// ЗапускаСт Π»ΠΈΠ±ΠΎ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ, Ссли Ρ€Π°Π½ΡŒΡˆΠ΅ Π½Π΅ Π±Ρ‹Π»ΠΎ Π½ΠΈ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ°, Π»ΠΈΠ±ΠΎ устанавливаСт событиС ΠΏΡ€ΠΎΠ±ΡƒΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ спящСго ΠΏΠΎΡ‚ΠΎΠΊΠ°
131 /// ЗапускаСт Π»ΠΈΠ±ΠΎ Π½ΠΎΠ²Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ, Ссли Ρ€Π°Π½ΡŒΡˆΠ΅ Π½Π΅ Π±Ρ‹Π»ΠΎ Π½ΠΈ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ°, Π»ΠΈΠ±ΠΎ устанавливаСт событиС ΠΏΡ€ΠΎΠ±ΡƒΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ спящСго ΠΏΠΎΡ‚ΠΎΠΊΠ°
132 /// </summary>
132 /// </summary>
133 protected void GrowPool() {
133 protected void GrowPool() {
134 if (m_exitRequired != 0)
134 if (m_exitRequired != 0)
135 return;
135 return;
136 if (m_sleepingThreads > m_wakeEvents) {
136 if (m_sleepingThreads > m_wakeEvents) {
137 //Console.WriteLine("Waking threads (sleeps {0}, pending {1})", m_sleepingThreads, m_wakeEvents);
137 //Console.WriteLine("Waking threads (sleeps {0}, pending {1})", m_sleepingThreads, m_wakeEvents);
138
138
139 // all sleeping threads may gone
139 // all sleeping threads may gone
140 SignalThread(); // wake a sleeping thread;
140 SignalThread(); // wake a sleeping thread;
141
141
142 // we can't check whether signal has been processed
142 // we can't check whether signal has been processed
143 // anyway it may take some time for the thread to start
143 // anyway it may take some time for the thread to start
144 // we will ensure that at least one thread is running
144 // we will ensure that at least one thread is running
145
145
146 EnsurePoolIsAlive();
147 } else {
148 // if there is no sleeping threads in the pool
149 if (!StartWorker()) {
150 // we haven't started a new thread, but the current can be on the way to terminate and it can't process the queue
151 // send it a signal to spin again
152 SignalThread();
153 EnsurePoolIsAlive();
154 }
155 }
156 }
157
158 private void EnsurePoolIsAlive() {
146 if (AllocateThreadSlot(1)) {
159 if (AllocateThreadSlot(1)) {
147 // if there were no threads in the pool
160 // if there were no threads in the pool
148 var worker = new Thread(this.Worker);
161 var worker = new Thread(this.Worker);
149 worker.IsBackground = true;
162 worker.IsBackground = true;
150 worker.Start();
163 worker.Start();
151 }
164 }
152 } else {
153 // if there is no sleeping threads in the pool
154 if (!StartWorker())
155 // we haven't started a new thread, but the current can be on the way and it can't process the queue
156 // send it a signal to spin again
157 SignalThread();
158 }
159 }
165 }
160
166
161 private bool Suspend() {
167 private bool Suspend() {
162 //no tasks left, exit if the thread is no longer needed
168 //no tasks left, exit if the thread is no longer needed
163 bool last;
169 bool last;
164 bool requestExit;
170 bool requestExit;
165
171
166 // if threads have a timeout before releasing
172 // if threads have a timeout before releasing
167 if (m_releaseTimeout > 0)
173 if (m_releaseTimeout > 0)
168 requestExit = !Sleep(m_releaseTimeout);
174 requestExit = !Sleep(m_releaseTimeout);
169 else
175 else
170 requestExit = true;
176 requestExit = true;
171
177
172 if (!requestExit)
178 if (!requestExit)
173 return true;
179 return true;
174
180
175 // release unsused thread
181 // release unsused thread
176 if (requestExit && ReleaseThreadSlot(out last)) {
182 if (requestExit && ReleaseThreadSlot(out last)) {
177 // in case at the moment the last thread was being released
183 // in case at the moment the last thread was being released
178 // a new task was added to the queue, we need to try
184 // a new task was added to the queue, we need to try
179 // to revoke the thread to avoid the situation when the task is left unprocessed
185 // to revoke the thread to avoid the situation when the task is left unprocessed
180 if (last && Sleep(0)) { // Sleep(0) will fetch pending task or will return false
186 if (last && FetchSignalOrWait(0)) { // FetchSignalOrWait(0) will fetch pending task or will return false
181 if (AllocateThreadSlot(1))
187 SignalThread(); // since FetchSignalOrWait(0) has fetched the signal we need to reschedule it
182 return true; // spin again...
188 return AllocateThreadSlot(1); // ensure that at least one thread is alive
183 else
184 SignalThread(); // since Sleep(0) has fetched the signal we neet to reschedule it
185
186 }
189 }
187
190
188 return false;
191 return false;
189 }
192 }
190
193
191 // wait till infinity
194 // wait till infinity
192 Sleep(-1);
195 Sleep(-1);
193
196
194 return true;
197 return true;
195 }
198 }
196
199
197 #region thread slots traits
200 #region thread slots traits
198
201
199 bool AllocateThreadSlot() {
202 bool AllocateThreadSlot() {
200 int current;
203 int current;
201 // use spins to allocate slot for the new thread
204 // use spins to allocate slot for the new thread
202 do {
205 do {
203 current = m_createdThreads;
206 current = m_createdThreads;
204 if (current >= m_maxThreads || m_exitRequired != 0)
207 if (current >= m_maxThreads || m_exitRequired != 0)
205 // no more slots left or the pool has been disposed
208 // no more slots left or the pool has been disposed
206 return false;
209 return false;
207 } while (current != Interlocked.CompareExchange(ref m_createdThreads, current + 1, current));
210 } while (current != Interlocked.CompareExchange(ref m_createdThreads, current + 1, current));
208
211
209 UpdateMaxThreads(current + 1);
212 UpdateMaxThreads(current + 1);
210
213
211 return true;
214 return true;
212 }
215 }
213
216
214 bool AllocateThreadSlot(int desired) {
217 bool AllocateThreadSlot(int desired) {
215 if (desired - 1 != Interlocked.CompareExchange(ref m_createdThreads, desired, desired - 1))
218 if (desired - 1 != Interlocked.CompareExchange(ref m_createdThreads, desired, desired - 1))
216 return false;
219 return false;
217
220
218 UpdateMaxThreads(desired);
221 UpdateMaxThreads(desired);
219
222
220 return true;
223 return true;
221 }
224 }
222
225
223 bool ReleaseThreadSlot(out bool last) {
226 bool ReleaseThreadSlot(out bool last) {
224 last = false;
227 last = false;
225 int current;
228 int current;
226 // use spins to release slot for the new thread
229 // use spins to release slot for the new thread
227 do {
230 do {
228 current = m_createdThreads;
231 current = m_createdThreads;
229 if (current <= m_minThreads && m_exitRequired == 0)
232 if (current <= m_minThreads && m_exitRequired == 0)
230 // the thread is reserved
233 // the thread is reserved
231 return false;
234 return false;
232 } while (current != Interlocked.CompareExchange(ref m_createdThreads, current - 1, current));
235 } while (current != Interlocked.CompareExchange(ref m_createdThreads, current - 1, current));
233
236
234 last = (current == 1);
237 last = (current == 1);
235
238
236 return true;
239 return true;
237 }
240 }
238
241
239 /// <summary>
242 /// <summary>
240 /// releases thread slot unconditionally, used during cleanup
243 /// releases thread slot unconditionally, used during cleanup
241 /// </summary>
244 /// </summary>
242 /// <returns>true - no more threads left</returns>
245 /// <returns>true - no more threads left</returns>
243 bool ReleaseThreadSlotAnyway() {
246 bool ReleaseThreadSlotAnyway() {
244 var left = Interlocked.Decrement(ref m_createdThreads);
247 var left = Interlocked.Decrement(ref m_createdThreads);
245 return left == 0;
248 return left == 0;
246 }
249 }
247
250
248 void UpdateMaxThreads(int count) {
251 void UpdateMaxThreads(int count) {
249 int max;
252 int max;
250 do {
253 do {
251 max = m_maxRunningThreads;
254 max = m_maxRunningThreads;
252 if (max >= count)
255 if (max >= count)
253 break;
256 break;
254 } while(max != Interlocked.CompareExchange(ref m_maxRunningThreads, count, max));
257 } while(max != Interlocked.CompareExchange(ref m_maxRunningThreads, count, max));
255 }
258 }
256
259
257 #endregion
260 #endregion
258
261
259 bool StartWorker() {
262 bool StartWorker() {
260 if (AllocateThreadSlot()) {
263 if (AllocateThreadSlot()) {
261 // slot successfully allocated
264 // slot successfully allocated
262 var worker = new Thread(this.Worker);
265 var worker = new Thread(this.Worker);
263 worker.IsBackground = true;
266 worker.IsBackground = true;
264 worker.Start();
267 worker.Start();
265
268
266 return true;
269 return true;
267 } else {
270 } else {
268 return false;
271 return false;
269 }
272 }
270 }
273 }
271
274
272 protected abstract void InvokeUnit(TUnit unit);
275 protected abstract void InvokeUnit(TUnit unit);
273
276
274 void Worker() {
277 void Worker() {
275 TUnit unit;
278 TUnit unit;
276 //Console.WriteLine("{0}: Active", Thread.CurrentThread.ManagedThreadId);
279 //Console.WriteLine("{0}: Active", Thread.CurrentThread.ManagedThreadId);
277 Interlocked.Increment(ref m_activeThreads);
280 Interlocked.Increment(ref m_activeThreads);
278 do {
281 do {
279 // exit if requested
282 // exit if requested
280 if (m_exitRequired != 0) {
283 if (m_exitRequired != 0) {
281 // release the thread slot
284 // release the thread slot
282 Interlocked.Decrement(ref m_activeThreads);
285 Interlocked.Decrement(ref m_activeThreads);
283 if (ReleaseThreadSlotAnyway()) // it was the last worker
286 if (ReleaseThreadSlotAnyway()) // it was the last worker
284 m_hasTasks.Dispose();
287 m_hasTasks.Dispose();
285 else
288 else
286 SignalThread(); // wake next worker
289 SignalThread(); // wake next worker
287 break;
290 break;
288 }
291 }
289
292
290 // fetch task
293 // fetch task
291 if (TryDequeue(out unit)) {
294 if (TryDequeue(out unit)) {
292 InvokeUnit(unit);
295 InvokeUnit(unit);
293 continue;
296 continue;
294 }
297 }
295
298
296 Interlocked.Decrement(ref m_activeThreads);
299 Interlocked.Decrement(ref m_activeThreads);
297
300
298 // entering suspend state
301 // entering suspend state
299 // keep this thread and wait
302 // keep this thread and wait
300 if (!Suspend())
303 if (!Suspend())
301 break;
304 break;
302 //Console.WriteLine("{0}: Awake", Thread.CurrentThread.ManagedThreadId);
305 //Console.WriteLine("{0}: Awake", Thread.CurrentThread.ManagedThreadId);
303 Interlocked.Increment(ref m_activeThreads);
306 Interlocked.Increment(ref m_activeThreads);
304 } while (true);
307 } while (true);
305 //Console.WriteLine("{0}: Exited", Thread.CurrentThread.ManagedThreadId);
308 //Console.WriteLine("{0}: Exited", Thread.CurrentThread.ManagedThreadId);
306 }
309 }
307
310
308 protected virtual void Dispose(bool disposing) {
311 protected virtual void Dispose(bool disposing) {
309 if (disposing) {
312 if (disposing) {
310 if (m_exitRequired == 0) {
313 if (m_exitRequired == 0) {
311 if (Interlocked.CompareExchange(ref m_exitRequired, 1, 0) != 0)
314 if (Interlocked.CompareExchange(ref m_exitRequired, 1, 0) != 0)
312 return;
315 return;
313
316
314 // wake sleeping threads
317 // wake sleeping threads
315 if (m_createdThreads > 0)
318 if (m_createdThreads > 0)
316 SignalThread();
319 SignalThread();
317 else
320 else
318 m_hasTasks.Dispose();
321 m_hasTasks.Dispose();
319 GC.SuppressFinalize(this);
322 GC.SuppressFinalize(this);
320 }
323 }
321 }
324 }
322 }
325 }
323
326
324 public void Dispose() {
327 public void Dispose() {
325 Dispose(true);
328 Dispose(true);
326 }
329 }
327
330
328 ~DispatchPool() {
331 ~DispatchPool() {
329 Dispose(false);
332 Dispose(false);
330 }
333 }
331 }
334 }
332 }
335 }
@@ -1,669 +1,669
1 using System;
1 using System;
2 using System.Collections.Generic;
2 using System.Collections.Generic;
3 using System.Reflection;
3 using System.Reflection;
4 using System.Diagnostics;
4 using System.Diagnostics;
5 using System.Threading;
5 using System.Threading;
6 using Implab.Parallels;
6 using Implab.Parallels;
7
7
8 namespace Implab {
8 namespace Implab {
9
9
10 public delegate void ErrorHandler(Exception e);
10 public delegate void ErrorHandler(Exception e);
11 public delegate T ErrorHandler<out T>(Exception e);
11 public delegate T ErrorHandler<out T>(Exception e);
12 public delegate void ResultHandler<in T>(T result);
12 public delegate void ResultHandler<in T>(T result);
13 public delegate TNew ResultMapper<in TSrc, out TNew>(TSrc result);
13 public delegate TNew ResultMapper<in TSrc, out TNew>(TSrc result);
14 public delegate Promise<TNew> ChainedOperation<in TSrc,TNew>(TSrc result);
14 public delegate IPromise<TNew> ChainedOperation<in TSrc,TNew>(TSrc result);
15
15
16 /// <summary>
16 /// <summary>
17 /// Класс для асинхронного получСния Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ². Π’Π°ΠΊ Π½Π°Π·Ρ‹Π²Π°Π΅ΠΌΠΎΠ΅ "ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅".
17 /// Класс для асинхронного получСния Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ². Π’Π°ΠΊ Π½Π°Π·Ρ‹Π²Π°Π΅ΠΌΠΎΠ΅ "ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅".
18 /// </summary>
18 /// </summary>
19 /// <typeparam name="T">Π’ΠΈΠΏ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌΠΎΠ³ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°</typeparam>
19 /// <typeparam name="T">Π’ΠΈΠΏ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌΠΎΠ³ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°</typeparam>
20 /// <remarks>
20 /// <remarks>
21 /// <para>БСрвис ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠΈ ΠΊ Π΅Π³ΠΎ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρƒ Π΄Π°Π΅Ρ‚ ΠΎΠ±Π΅Ρ‰Π°ΠΈΠ½ΠΈΠ΅ ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ,
21 /// <para>БСрвис ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠΈ ΠΊ Π΅Π³ΠΎ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρƒ Π΄Π°Π΅Ρ‚ ΠΎΠ±Π΅Ρ‰Π°ΠΈΠ½ΠΈΠ΅ ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ,
22 /// ΠΊΠ»ΠΈΠ΅Π½Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ² Ρ‚Π°ΠΊΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ ряд ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹Ρ… Π²Ρ‹Π·ΠΎΠ²ΠΎ для получСния
22 /// ΠΊΠ»ΠΈΠ΅Π½Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ² Ρ‚Π°ΠΊΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ ряд ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹Ρ… Π²Ρ‹Π·ΠΎΠ²ΠΎ для получСния
23 /// событий выполнСния обСщания, Ρ‚ΠΎΠ΅ΡΡ‚ΡŒ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΈ прСдоставлСнии Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ².</para>
23 /// событий выполнСния обСщания, Ρ‚ΠΎΠ΅ΡΡ‚ΡŒ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΈ прСдоставлСнии Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ².</para>
24 /// <para>
24 /// <para>
25 /// ΠžΠ±Π΅Ρ‰Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΊΠ°ΠΊ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ, Ρ‚Π°ΠΊ ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ с ошибкой. Для подписки Π½Π°
25 /// ΠžΠ±Π΅Ρ‰Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΊΠ°ΠΊ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ, Ρ‚Π°ΠΊ ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ с ошибкой. Для подписки Π½Π°
26 /// Π΄Π°Π½Π½Ρ‹Π΅ события ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ <c>Then</c>.
26 /// Π΄Π°Π½Π½Ρ‹Π΅ события ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ <c>Then</c>.
27 /// </para>
27 /// </para>
28 /// <para>
28 /// <para>
29 /// БСрвис, Π² свою ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ, ΠΏΠΎ ΠΎΠΊΠΎΠ½Ρ‡Π°Π½ΠΈΡŽ выполнСния ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ (Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ с ошибкой),
29 /// БСрвис, Π² свою ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ, ΠΏΠΎ ΠΎΠΊΠΎΠ½Ρ‡Π°Π½ΠΈΡŽ выполнСния ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ (Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ с ошибкой),
30 /// ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ <c>Resolve</c> Π»ΠΈΠ±ΠΎ <c>Reject</c> для оповСщСния ΠΊΠ»ΠΈΠ΅Ρ‚Π½Π° ΠΎ
30 /// ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ <c>Resolve</c> Π»ΠΈΠ±ΠΎ <c>Reject</c> для оповСщСния ΠΊΠ»ΠΈΠ΅Ρ‚Π½Π° ΠΎ
31 /// Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ обСщания.
31 /// Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ обСщания.
32 /// </para>
32 /// </para>
33 /// <para>
33 /// <para>
34 /// Если сСрвСр успСл Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ Π΅Ρ‰Π΅ Π΄ΠΎ Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Π½Π° Π½Π΅Π³ΠΎ подписался,
34 /// Если сСрвСр успСл Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ Π΅Ρ‰Π΅ Π΄ΠΎ Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Π½Π° Π½Π΅Π³ΠΎ подписался,
35 /// Ρ‚ΠΎ Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚ подписки ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹Π·Π²Π°Π½Ρ‹ ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΠ²ΡƒΡŽΡ‰ΠΈΠ΅ события Π² синхронном
35 /// Ρ‚ΠΎ Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚ подписки ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹Π·Π²Π°Π½Ρ‹ ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΠ²ΡƒΡŽΡ‰ΠΈΠ΅ события Π² синхронном
36 /// Ρ€Π΅ΠΆΠΈΠΌΠ΅ ΠΈ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠΏΠΎΠ²Π΅Ρ‰Π΅Π½ Π² любом случаС. Π˜Π½Π°Ρ‡Π΅, ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ΡΡ Π²
36 /// Ρ€Π΅ΠΆΠΈΠΌΠ΅ ΠΈ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠΏΠΎΠ²Π΅Ρ‰Π΅Π½ Π² любом случаС. Π˜Π½Π°Ρ‡Π΅, ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ΡΡ Π²
37 /// список Π² порядкС подписания ΠΈ Π² этом ΠΆΠ΅ порядкС ΠΎΠ½ΠΈ Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹Π·Π²Π°Π½Ρ‹ ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ
37 /// список Π² порядкС подписания ΠΈ Π² этом ΠΆΠ΅ порядкС ΠΎΠ½ΠΈ Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹Π·Π²Π°Π½Ρ‹ ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ
38 /// обСщания.
38 /// обСщания.
39 /// </para>
39 /// </para>
40 /// <para>
40 /// <para>
41 /// ΠžΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ обСщания ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Ρ‹Π²Π°Ρ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Π»ΠΈΠ±ΠΎ ΠΈΠ½ΠΈΡ†ΠΈΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ
41 /// ΠžΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ обСщания ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Ρ‹Π²Π°Ρ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Π»ΠΈΠ±ΠΎ ΠΈΠ½ΠΈΡ†ΠΈΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ
42 /// связанныС асинхронныС ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Ρ‚Π°ΠΊΠΆΠ΅ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‚ обСщания. Для этого слСдуСт
42 /// связанныС асинхронныС ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Ρ‚Π°ΠΊΠΆΠ΅ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‚ обСщания. Для этого слСдуСт
43 /// ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΡƒΡŽ Ρ„ΠΎΡ€ΠΌΡƒ ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ <c>Then</c>.
43 /// ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΡƒΡŽ Ρ„ΠΎΡ€ΠΌΡƒ ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ <c>Then</c>.
44 /// </para>
44 /// </para>
45 /// <para>
45 /// <para>
46 /// Π’Π°ΠΊΠΆΠ΅ Ρ…ΠΎΡ€ΠΎΡˆΠΈΠΌ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎΠΌ являСтся Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ <c>Resolve</c> ΠΈ <c>Reject</c> Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ
46 /// Π’Π°ΠΊΠΆΠ΅ Ρ…ΠΎΡ€ΠΎΡˆΠΈΠΌ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎΠΌ являСтся Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ <c>Resolve</c> ΠΈ <c>Reject</c> Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ
47 /// Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΈΠ½ΠΈΡ†ΠΈΠ°Ρ‚ΠΎΡ€ обСщания ΠΈΠ½Π°Ρ‡Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΡƒΡ‚ΡŒ противорСчия.
47 /// Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΈΠ½ΠΈΡ†ΠΈΠ°Ρ‚ΠΎΡ€ обСщания ΠΈΠ½Π°Ρ‡Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΡƒΡ‚ΡŒ противорСчия.
48 /// </para>
48 /// </para>
49 /// </remarks>
49 /// </remarks>
50 public class Promise<T> : IPromise<T> {
50 public class Promise<T> : IPromise<T> {
51
51
52 protected struct HandlerDescriptor {
52 protected struct HandlerDescriptor {
53 public ResultHandler<T> resultHandler;
53 public ResultHandler<T> resultHandler;
54 public ErrorHandler errorHandler;
54 public ErrorHandler errorHandler;
55 public Action cancellHandler;
55 public Action cancellHandler;
56
56
57 public void Resolve(T result) {
57 public void Resolve(T result) {
58 if (resultHandler != null)
58 if (resultHandler != null)
59 try {
59 try {
60 resultHandler(result);
60 resultHandler(result);
61 } catch (Exception e) {
61 } catch (Exception e) {
62 Reject(e);
62 Reject(e);
63 }
63 }
64 }
64 }
65
65
66 public void Reject(Exception err) {
66 public void Reject(Exception err) {
67 if (errorHandler != null)
67 if (errorHandler != null)
68 try {
68 try {
69 errorHandler(err);
69 errorHandler(err);
70 } catch {
70 } catch {
71 }
71 }
72 }
72 }
73
73
74 public void Cancel() {
74 public void Cancel() {
75 if (cancellHandler != null)
75 if (cancellHandler != null)
76 try {
76 try {
77 cancellHandler();
77 cancellHandler();
78 } catch {
78 } catch {
79 }
79 }
80 }
80 }
81 }
81 }
82
82
83 const int UnresolvedSate = 0;
83 const int UnresolvedSate = 0;
84 const int TransitionalState = 1;
84 const int TransitionalState = 1;
85 const int SucceededState = 2;
85 const int SucceededState = 2;
86 const int RejectedState = 3;
86 const int RejectedState = 3;
87 const int CancelledState = 4;
87 const int CancelledState = 4;
88
88
89 readonly IPromiseBase m_parent;
89 readonly IPromiseBase m_parent;
90 readonly bool m_cancellable;
90 readonly bool m_cancellable;
91
91
92 int m_childrenCount = 0;
92 int m_childrenCount = 0;
93 int m_state;
93 int m_state;
94 T m_result;
94 T m_result;
95 Exception m_error;
95 Exception m_error;
96
96
97 readonly MTQueue<HandlerDescriptor> m_handlers = new MTQueue<HandlerDescriptor>();
97 readonly MTQueue<HandlerDescriptor> m_handlers = new MTQueue<HandlerDescriptor>();
98
98
99 public Promise() {
99 public Promise() {
100 m_cancellable = true;
100 m_cancellable = true;
101 }
101 }
102
102
103 public Promise(IPromiseBase parent, bool cancellable) {
103 public Promise(IPromiseBase parent, bool cancellable) {
104 m_cancellable = cancellable;
104 m_cancellable = cancellable;
105 m_parent = parent;
105 m_parent = parent;
106 }
106 }
107
107
108 void InternalCancel() {
108 void InternalCancel() {
109 // don't try to cancel parent :)
109 // don't try to cancel parent :)
110 Cancel(false);
110 Cancel(false);
111 }
111 }
112
112
113 bool BeginTransit() {
113 bool BeginTransit() {
114 return UnresolvedSate == Interlocked.CompareExchange(ref m_state, TransitionalState, UnresolvedSate);
114 return UnresolvedSate == Interlocked.CompareExchange(ref m_state, TransitionalState, UnresolvedSate);
115 }
115 }
116
116
117 void CompleteTransit(int state) {
117 void CompleteTransit(int state) {
118 if (TransitionalState != Interlocked.CompareExchange(ref m_state, state, TransitionalState))
118 if (TransitionalState != Interlocked.CompareExchange(ref m_state, state, TransitionalState))
119 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
119 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
120 }
120 }
121
121
122 void WaitTransition() {
122 void WaitTransition() {
123 while (m_state == TransitionalState) {
123 while (m_state == TransitionalState) {
124 /* noop */
124 /* noop */
125 }
125 }
126 }
126 }
127
127
128 public bool IsResolved {
128 public bool IsResolved {
129 get {
129 get {
130 return m_state > 1;
130 return m_state > 1;
131 }
131 }
132 }
132 }
133
133
134 public bool IsCancelled {
134 public bool IsCancelled {
135 get {
135 get {
136 return m_state == CancelledState;
136 return m_state == CancelledState;
137 }
137 }
138 }
138 }
139
139
140 public Type PromiseType {
140 public Type PromiseType {
141 get { return typeof(T); }
141 get { return typeof(T); }
142 }
142 }
143
143
144 /// <summary>
144 /// <summary>
145 /// ВыполняСт ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, сообщая ΠΎΠ± ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΌ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ.
145 /// ВыполняСт ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, сообщая ΠΎΠ± ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΌ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ.
146 /// </summary>
146 /// </summary>
147 /// <param name="result">Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполнСния.</param>
147 /// <param name="result">Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполнСния.</param>
148 /// <exception cref="InvalidOperationException">Π”Π°Π½Π½ΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΡƒΠΆΠ΅ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ</exception>
148 /// <exception cref="InvalidOperationException">Π”Π°Π½Π½ΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΡƒΠΆΠ΅ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ</exception>
149 public void Resolve(T result) {
149 public void Resolve(T result) {
150 if (BeginTransit()) {
150 if (BeginTransit()) {
151 m_result = result;
151 m_result = result;
152 CompleteTransit(SucceededState);
152 CompleteTransit(SucceededState);
153 OnStateChanged();
153 OnStateChanged();
154 } else {
154 } else {
155 WaitTransition();
155 WaitTransition();
156 if (m_state != CancelledState)
156 if (m_state != CancelledState)
157 throw new InvalidOperationException("The promise is already resolved");
157 throw new InvalidOperationException("The promise is already resolved");
158 }
158 }
159 }
159 }
160
160
161 /// <summary>
161 /// <summary>
162 /// ВыполняСт ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, сообщая ΠΎΠ± ошибкС
162 /// ВыполняСт ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, сообщая ΠΎΠ± ошибкС
163 /// </summary>
163 /// </summary>
164 /// <remarks>
164 /// <remarks>
165 /// ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π² ΠΌΠ½ΠΎΠ³ΠΎΠΏΡ‚ΠΎΡ‡Π½ΠΎΠΉ срСдС, ΠΏΡ€ΠΈ Π΅Π³ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ сразу нСсколько ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ²
165 /// ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π² ΠΌΠ½ΠΎΠ³ΠΎΠΏΡ‚ΠΎΡ‡Π½ΠΎΠΉ срСдС, ΠΏΡ€ΠΈ Π΅Π³ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ сразу нСсколько ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ²
166 /// ΠΌΠΎΠ³Ρƒ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ ΠΎΡˆΠΈΠ±ΠΊΡƒ, ΠΏΡ€ΠΈ этом Ρ‚ΠΎΠ»ΡŒΠΊΠΎ пСрвая Π±ΡƒΠ΄Π΅Ρ‚ использована Π² качСствС Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Π΅
166 /// ΠΌΠΎΠ³Ρƒ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ ΠΎΡˆΠΈΠ±ΠΊΡƒ, ΠΏΡ€ΠΈ этом Ρ‚ΠΎΠ»ΡŒΠΊΠΎ пСрвая Π±ΡƒΠ΄Π΅Ρ‚ использована Π² качСствС Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Π΅
167 /// Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΡ€ΠΎΠΈΠ³Π½ΠΎΡ€ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹.
167 /// Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΡ€ΠΎΠΈΠ³Π½ΠΎΡ€ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹.
168 /// </remarks>
168 /// </remarks>
169 /// <param name="error">Π˜ΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ возникшСС ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ</param>
169 /// <param name="error">Π˜ΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ возникшСС ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ</param>
170 /// <exception cref="InvalidOperationException">Π”Π°Π½Π½ΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΡƒΠΆΠ΅ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ</exception>
170 /// <exception cref="InvalidOperationException">Π”Π°Π½Π½ΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΡƒΠΆΠ΅ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ</exception>
171 public void Reject(Exception error) {
171 public void Reject(Exception error) {
172 if (BeginTransit()) {
172 if (BeginTransit()) {
173 m_error = error;
173 m_error = error;
174 CompleteTransit(RejectedState);
174 CompleteTransit(RejectedState);
175 OnStateChanged();
175 OnStateChanged();
176 } else {
176 } else {
177 WaitTransition();
177 WaitTransition();
178 if (m_state == SucceededState)
178 if (m_state == SucceededState)
179 throw new InvalidOperationException("The promise is already resolved");
179 throw new InvalidOperationException("The promise is already resolved");
180 }
180 }
181 }
181 }
182
182
183 /// <summary>
183 /// <summary>
184 /// ΠžΡ‚ΠΌΠ΅Π½ΡΠ΅Ρ‚ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΡŽ, Ссли это Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ.
184 /// ΠžΡ‚ΠΌΠ΅Π½ΡΠ΅Ρ‚ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΡŽ, Ссли это Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ.
185 /// </summary>
185 /// </summary>
186 /// <returns><c>true</c> ΠžΠΏΠ΅Ρ€Π°Ρ†ΠΈΡ Π±Ρ‹Π»Π° ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½Π°, ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π½Π΅ Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹Π·Π²Π°Π½Ρ‹.<c>false</c> ΠΎΡ‚ΠΌΠ΅Π½Π° Π½Π΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Π°, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΡƒΠΆΠ΅ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ ΠΎΡ‚Ρ€Π°Π±ΠΎΡ‚Π°Π»ΠΈ.</returns>
186 /// <returns><c>true</c> ΠžΠΏΠ΅Ρ€Π°Ρ†ΠΈΡ Π±Ρ‹Π»Π° ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½Π°, ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π½Π΅ Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹Π·Π²Π°Π½Ρ‹.<c>false</c> ΠΎΡ‚ΠΌΠ΅Π½Π° Π½Π΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Π°, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΡƒΠΆΠ΅ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ ΠΎΡ‚Ρ€Π°Π±ΠΎΡ‚Π°Π»ΠΈ.</returns>
187 public bool Cancel() {
187 public bool Cancel() {
188 return Cancel(true);
188 return Cancel(true);
189 }
189 }
190
190
191 /// <summary>
191 /// <summary>
192 /// Adds new handlers to this promise.
192 /// Adds new handlers to this promise.
193 /// </summary>
193 /// </summary>
194 /// <param name="success">The handler of the successfully completed operation.
194 /// <param name="success">The handler of the successfully completed operation.
195 /// This handler will recieve an operation result as a parameter.</param>
195 /// This handler will recieve an operation result as a parameter.</param>
196 /// <param name="error">Handles an exception that may occur during the operation.</param>
196 /// <param name="error">Handles an exception that may occur during the operation.</param>
197 /// <returns>The new promise chained to this one.</returns>
197 /// <returns>The new promise chained to this one.</returns>
198 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler error) {
198 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler error) {
199 if (success == null && error == null)
199 if (success == null && error == null)
200 return this;
200 return this;
201
201
202 var medium = new Promise<T>(this, true);
202 var medium = new Promise<T>(this, true);
203
203
204 ResultHandler<T> resultHandler;
204 ResultHandler<T> resultHandler;
205 if (success != null)
205 if (success != null)
206 resultHandler = x => {
206 resultHandler = x => {
207 success(x);
207 success(x);
208 medium.Resolve(x);
208 medium.Resolve(x);
209 };
209 };
210 else
210 else
211 resultHandler = medium.Resolve;
211 resultHandler = medium.Resolve;
212
212
213 ErrorHandler errorHandler;
213 ErrorHandler errorHandler;
214 if (error != null)
214 if (error != null)
215 errorHandler = x => {
215 errorHandler = x => {
216 // нСсмотря Π½Π° Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ошибки вызываСтся бСзопасно,
216 // нСсмотря Π½Π° Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ошибки вызываСтся бСзопасно,
217 // Ρ‚.Π΅. возникшиС Π² Π½Π΅ΠΌ ошибки Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠΎΠ΄Π°Π²Π»Π΅Π½Ρ‹, Π½Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ
217 // Ρ‚.Π΅. возникшиС Π² Π½Π΅ΠΌ ошибки Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠΎΠ΄Π°Π²Π»Π΅Π½Ρ‹, Π½Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ
218 // Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ ошибка Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π° дальшС ΠΏΠΎ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ
218 // Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ ошибка Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π° дальшС ΠΏΠΎ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ
219 try {
219 try {
220 error(x);
220 error(x);
221 } catch { }
221 } catch { }
222 medium.Reject(x);
222 medium.Reject(x);
223 };
223 };
224 else
224 else
225 errorHandler = medium.Reject;
225 errorHandler = medium.Reject;
226
226
227 AddHandler(resultHandler, errorHandler, medium.InternalCancel);
227 AddHandler(resultHandler, errorHandler, medium.InternalCancel);
228
228
229 return medium;
229 return medium;
230 }
230 }
231
231
232 public IPromiseBase Then(Action success,ErrorHandler error)
232 public IPromiseBase Then(Action success,ErrorHandler error)
233 {
233 {
234 return Then(x => success(), error);
234 return Then(x => success(), error);
235 }
235 }
236
236
237 public IPromiseBase Then(Action success)
237 public IPromiseBase Then(Action success)
238 {
238 {
239 return Then(success);
239 return Then(success);
240 }
240 }
241
241
242 /// <summary>
242 /// <summary>
243 /// Adds new handlers to this promise.
243 /// Adds new handlers to this promise.
244 /// </summary>
244 /// </summary>
245 /// <param name="success">The handler of the successfully completed operation.
245 /// <param name="success">The handler of the successfully completed operation.
246 /// This handler will recieve an operation result as a parameter.</param>
246 /// This handler will recieve an operation result as a parameter.</param>
247 /// <param name="error">Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation.</param>
247 /// <param name="error">Handles an exception that may occur during the operation and returns the value which will be used as the result of the operation.</param>
248 /// <returns>The new promise chained to this one.</returns>
248 /// <returns>The new promise chained to this one.</returns>
249 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) {
249 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error) {
250 if (success == null && error == null)
250 if (success == null && error == null)
251 return this;
251 return this;
252
252
253 var medium = new Promise<T>(this, true);
253 var medium = new Promise<T>(this, true);
254
254
255 ResultHandler<T> resultHandler;
255 ResultHandler<T> resultHandler;
256 ErrorHandler errorHandler;
256 ErrorHandler errorHandler;
257
257
258 if (success != null)
258 if (success != null)
259 resultHandler = x => {
259 resultHandler = x => {
260 success(x);
260 success(x);
261 medium.Resolve(x);
261 medium.Resolve(x);
262 };
262 };
263 else
263 else
264 resultHandler = medium.Resolve;
264 resultHandler = medium.Resolve;
265
265
266 if (error != null)
266 if (error != null)
267 errorHandler = x => {
267 errorHandler = x => {
268 try {
268 try {
269 medium.Resolve(error(x));
269 medium.Resolve(error(x));
270 } catch(Exception e) {
270 } catch(Exception e) {
271 medium.Reject(e);
271 medium.Reject(e);
272 }
272 }
273 };
273 };
274 else
274 else
275 errorHandler = medium.Reject;
275 errorHandler = medium.Reject;
276
276
277 AddHandler(resultHandler, errorHandler, medium.InternalCancel);
277 AddHandler(resultHandler, errorHandler, medium.InternalCancel);
278
278
279 return medium;
279 return medium;
280 }
280 }
281
281
282
282
283 public IPromise<T> Then(ResultHandler<T> success) {
283 public IPromise<T> Then(ResultHandler<T> success) {
284 if (success == null)
284 if (success == null)
285 return this;
285 return this;
286
286
287 var medium = new Promise<T>(this, true);
287 var medium = new Promise<T>(this, true);
288
288
289 ResultHandler<T> resultHandler;
289 ResultHandler<T> resultHandler;
290
290
291 if (success != null)
291 if (success != null)
292 resultHandler = x => {
292 resultHandler = x => {
293 success(x);
293 success(x);
294 medium.Resolve(x);
294 medium.Resolve(x);
295 };
295 };
296 else
296 else
297 resultHandler = medium.Resolve;
297 resultHandler = medium.Resolve;
298
298
299 AddHandler(resultHandler, medium.Reject, medium.InternalCancel);
299 AddHandler(resultHandler, medium.Reject, medium.InternalCancel);
300
300
301 return medium;
301 return medium;
302 }
302 }
303
303
304 public IPromise<T> Error(ErrorHandler error) {
304 public IPromise<T> Error(ErrorHandler error) {
305 return Then((ResultHandler<T>)null, error);
305 return Then((ResultHandler<T>)null, error);
306 }
306 }
307
307
308 /// <summary>
308 /// <summary>
309 /// Handles error and allows to keep the promise.
309 /// Handles error and allows to keep the promise.
310 /// </summary>
310 /// </summary>
311 /// <remarks>
311 /// <remarks>
312 /// If the specified handler throws an exception, this exception will be used to reject the promise.
312 /// If the specified handler throws an exception, this exception will be used to reject the promise.
313 /// </remarks>
313 /// </remarks>
314 /// <param name="handler">The error handler which returns the result of the promise.</param>
314 /// <param name="handler">The error handler which returns the result of the promise.</param>
315 /// <returns>New promise.</returns>
315 /// <returns>New promise.</returns>
316 public IPromise<T> Error(ErrorHandler<T> handler) {
316 public IPromise<T> Error(ErrorHandler<T> handler) {
317 if (handler == null)
317 if (handler == null)
318 return this;
318 return this;
319
319
320 var medium = new Promise<T>(this, true);
320 var medium = new Promise<T>(this, true);
321
321
322 AddHandler(
322 AddHandler(
323 x => medium.Resolve(x),
323 x => medium.Resolve(x),
324 e => {
324 e => {
325 try {
325 try {
326 medium.Resolve(handler(e));
326 medium.Resolve(handler(e));
327 } catch (Exception e2) {
327 } catch (Exception e2) {
328 medium.Reject(e2);
328 medium.Reject(e2);
329 }
329 }
330 },
330 },
331 medium.InternalCancel
331 medium.InternalCancel
332 );
332 );
333
333
334 return medium;
334 return medium;
335 }
335 }
336
336
337 public IPromise<T> Anyway(Action handler) {
337 public IPromise<T> Anyway(Action handler) {
338 if (handler == null)
338 if (handler == null)
339 return this;
339 return this;
340
340
341 var medium = new Promise<T>();
341 var medium = new Promise<T>();
342
342
343 AddHandler(
343 AddHandler(
344 x => {
344 x => {
345 // to avoid handler being called multiple times we handle exception by ourselfs
345 // to avoid handler being called multiple times we handle exception by ourselfs
346 try {
346 try {
347 handler();
347 handler();
348 medium.Resolve(x);
348 medium.Resolve(x);
349 } catch (Exception e) {
349 } catch (Exception e) {
350 medium.Reject(e);
350 medium.Reject(e);
351 }
351 }
352 },
352 },
353
353
354 e => {
354 e => {
355 try {
355 try {
356 handler();
356 handler();
357 } catch { }
357 } catch { }
358 medium.Reject(e);
358 medium.Reject(e);
359 },
359 },
360
360
361 medium.InternalCancel
361 medium.InternalCancel
362 );
362 );
363
363
364 return medium;
364 return medium;
365 }
365 }
366
366
367 /// <summary>
367 /// <summary>
368 /// ΠŸΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ‚ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Ρ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполСния ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΊ Π½ΠΎΠ²ΠΎΠΌΡƒ Ρ‚ΠΈΠΏΡƒ.
368 /// ΠŸΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ‚ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Ρ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполСния ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΊ Π½ΠΎΠ²ΠΎΠΌΡƒ Ρ‚ΠΈΠΏΡƒ.
369 /// </summary>
369 /// </summary>
370 /// <typeparam name="TNew">Новый Ρ‚ΠΈΠΏ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°.</typeparam>
370 /// <typeparam name="TNew">Новый Ρ‚ΠΈΠΏ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°.</typeparam>
371 /// <param name="mapper">ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π° ΠΊ Π½ΠΎΠ²ΠΎΠΌΡƒ Ρ‚ΠΈΠΏΡƒ.</param>
371 /// <param name="mapper">ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π° ΠΊ Π½ΠΎΠ²ΠΎΠΌΡƒ Ρ‚ΠΈΠΏΡƒ.</param>
372 /// <param name="error">ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ошибки. Π”Π°Π½Π½Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚
372 /// <param name="error">ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ошибки. Π”Π°Π½Π½Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚
373 /// ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ возникшСС ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.</param>
373 /// ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ возникшСС ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.</param>
374 /// <returns>НовоС ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ исходного обСщания.</returns>
374 /// <returns>НовоС ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ исходного обСщания.</returns>
375 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
375 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) {
376 if (mapper == null)
376 if (mapper == null)
377 throw new ArgumentNullException("mapper");
377 throw new ArgumentNullException("mapper");
378
378
379 // создаСм ΠΏΡ€ΠΈΡ†Π΅ΠΏΠ»Π΅Π½Π½ΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅
379 // создаСм ΠΏΡ€ΠΈΡ†Π΅ΠΏΠ»Π΅Π½Π½ΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅
380 var chained = new Promise<TNew>();
380 var chained = new Promise<TNew>();
381
381
382 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result));
382 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result));
383 ErrorHandler errorHandler = delegate(Exception e) {
383 ErrorHandler errorHandler = delegate(Exception e) {
384 if (error != null)
384 if (error != null)
385 try {
385 try {
386 error(e);
386 error(e);
387 } catch { }
387 } catch { }
388 // Π² случаС ошибки Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ дальшС ΠΏΠΎ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅
388 // Π² случаС ошибки Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ дальшС ΠΏΠΎ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅
389 chained.Reject(e);
389 chained.Reject(e);
390 };
390 };
391
391
392
392
393 AddHandler(
393 AddHandler(
394 resultHandler,
394 resultHandler,
395 errorHandler,
395 errorHandler,
396 chained.InternalCancel
396 chained.InternalCancel
397 );
397 );
398
398
399 return chained;
399 return chained;
400 }
400 }
401
401
402 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) {
402 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper) {
403 return Map(mapper, null);
403 return Map(mapper, null);
404 }
404 }
405
405
406 /// <summary>
406 /// <summary>
407 /// БцСпляСт нСсколько аснхронных ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ. Указанная асинхронная опСрация Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π·Π²Π°Π½Π° послС
407 /// БцСпляСт нСсколько аснхронных ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ. Указанная асинхронная опСрация Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π·Π²Π°Π½Π° послС
408 /// выполнСния Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ, Π° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ использован для ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ
408 /// выполнСния Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ, Π° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ использован для ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ
409 /// Π½ΠΎΠ²ΠΎΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.
409 /// Π½ΠΎΠ²ΠΎΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.
410 /// </summary>
410 /// </summary>
411 /// <typeparam name="TNew">Π’ΠΈΠΏ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π° ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠΉ асинхронной ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.</typeparam>
411 /// <typeparam name="TNew">Π’ΠΈΠΏ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π° ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠΉ асинхронной ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.</typeparam>
412 /// <param name="chained">Асинхронная опСрация, которая Π΄ΠΎΠ»ΠΆΠ½Π° Π±ΡƒΠ΄Π΅Ρ‚ Π½Π°Ρ‡Π°Ρ‚ΡŒΡΡ послС выполнСния Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ.</param>
412 /// <param name="chained">Асинхронная опСрация, которая Π΄ΠΎΠ»ΠΆΠ½Π° Π±ΡƒΠ΄Π΅Ρ‚ Π½Π°Ρ‡Π°Ρ‚ΡŒΡΡ послС выполнСния Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ.</param>
413 /// <param name="error">ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ошибки. Π”Π°Π½Π½Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚
413 /// <param name="error">ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ошибки. Π”Π°Π½Π½Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚
414 /// ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ возникшСС ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ Ρ‚Π΅ΠΊΡƒΠ΅Ρ‰ΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.</param>
414 /// ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ возникшСС ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ Ρ‚Π΅ΠΊΡƒΠ΅Ρ‰ΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.</param>
415 /// <returns>НовоС ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ ΠΏΠΎ ΠΎΠΊΠΎΠ½Ρ‡Π°Π½ΠΈΡŽ ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠΉ аснхронной ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.</returns>
415 /// <returns>НовоС ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ ΠΏΠΎ ΠΎΠΊΠΎΠ½Ρ‡Π°Π½ΠΈΡŽ ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠΉ аснхронной ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ.</returns>
416 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
416 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler error) {
417
417
418 // ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π½Π° ΠΌΠΎΠΌΠ΅Π½Ρ‚ связывания Π΅Ρ‰Π΅ Π½Π΅ Π½Π°Ρ‡Π°Ρ‚Π° асинхронная опСрация, поэтому Π½ΡƒΠΆΠ½ΠΎ
418 // ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π½Π° ΠΌΠΎΠΌΠ΅Π½Ρ‚ связывания Π΅Ρ‰Π΅ Π½Π΅ Π½Π°Ρ‡Π°Ρ‚Π° асинхронная опСрация, поэтому Π½ΡƒΠΆΠ½ΠΎ
419 // ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ посрСдника, ΠΊ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠΎΠ΄Π²Ρ‹Π·ΡΠ²Π°Ρ‚ΡŒΡΡ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ.
419 // ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ посрСдника, ΠΊ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠΎΠ΄Π²Ρ‹Π·ΡΠ²Π°Ρ‚ΡŒΡΡ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ.
420 // ΠΊΠΎΠ³Π΄Π° Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½Π° Ρ€Π΅Π°Π»ΡŒΠ½Π°Ρ асинхронная опСрация, ΠΎΠ½Π° ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚ΡŒΡΡ ΠΊ посрСднику, Ρ‡Ρ‚ΠΎΠ±Ρ‹
420 // ΠΊΠΎΠ³Π΄Π° Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½Π° Ρ€Π΅Π°Π»ΡŒΠ½Π°Ρ асинхронная опСрация, ΠΎΠ½Π° ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚ΡŒΡΡ ΠΊ посрСднику, Ρ‡Ρ‚ΠΎΠ±Ρ‹
421 // ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ Ρ‡Π΅Ρ€Π΅Π· Π½Π΅Π³ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Ρ€Π°Π±ΠΎΡ‚Ρ‹.
421 // ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ Ρ‡Π΅Ρ€Π΅Π· Π½Π΅Π³ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Ρ€Π°Π±ΠΎΡ‚Ρ‹.
422 var medium = new Promise<TNew>(this, true);
422 var medium = new Promise<TNew>(this, true);
423
423
424 ResultHandler<T> resultHandler = delegate(T result) {
424 ResultHandler<T> resultHandler = delegate(T result) {
425 if (medium.IsCancelled)
425 if (medium.IsCancelled)
426 return;
426 return;
427
427
428 var promise = chained(result);
428 var promise = chained(result);
429
429
430 // notify chained operation that it's not needed
430 // notify chained operation that it's not needed
431 medium.Cancelled(() => promise.Cancel());
431 medium.Cancelled(() => promise.Cancel());
432 promise.Then(
432 promise.Then(
433 x => medium.Resolve(x),
433 x => medium.Resolve(x),
434 e => medium.Reject(e)
434 e => medium.Reject(e)
435 );
435 );
436 };
436 };
437
437
438 ErrorHandler errorHandler = delegate(Exception e) {
438 ErrorHandler errorHandler = delegate(Exception e) {
439 if (error != null)
439 if (error != null)
440 error(e);
440 error(e);
441 // Π² случаС ошибки Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ дальшС ΠΏΠΎ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅
441 // Π² случаС ошибки Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ дальшС ΠΏΠΎ Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅
442 medium.Reject(e);
442 medium.Reject(e);
443 };
443 };
444
444
445 AddHandler(
445 AddHandler(
446 resultHandler,
446 resultHandler,
447 errorHandler,
447 errorHandler,
448 medium.InternalCancel
448 medium.InternalCancel
449 );
449 );
450
450
451 return medium;
451 return medium;
452 }
452 }
453
453
454 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) {
454 public IPromise<TNew> Chain<TNew>(ChainedOperation<T, TNew> chained) {
455 return Chain(chained, null);
455 return Chain(chained, null);
456 }
456 }
457
457
458 public IPromise<T> Cancelled(Action handler) {
458 public IPromise<T> Cancelled(Action handler) {
459 AddHandler(null, null, handler);
459 AddHandler(null, null, handler);
460 return this;
460 return this;
461 }
461 }
462
462
463 /// <summary>
463 /// <summary>
464 /// Adds the specified handler for all cases (success, error, cancel)
464 /// Adds the specified handler for all cases (success, error, cancel)
465 /// </summary>
465 /// </summary>
466 /// <param name="handler">The handler that will be called anyway</param>
466 /// <param name="handler">The handler that will be called anyway</param>
467 /// <returns>self</returns>
467 /// <returns>self</returns>
468 public IPromise<T> Finally(Action handler) {
468 public IPromise<T> Finally(Action handler) {
469 if (handler == null)
469 if (handler == null)
470 throw new ArgumentNullException("handler");
470 throw new ArgumentNullException("handler");
471 AddHandler(
471 AddHandler(
472 x => handler(),
472 x => handler(),
473 e => handler(),
473 e => handler(),
474 handler
474 handler
475 );
475 );
476 return this;
476 return this;
477 }
477 }
478
478
479 /// <summary>
479 /// <summary>
480 /// ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΡƒΠ΅Ρ‚ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ обСщания ΠΊ Π½ΡƒΠΆΠ½ΠΎΠΌΡƒ Ρ‚ΠΈΠΏΡƒ
480 /// ΠŸΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΡƒΠ΅Ρ‚ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ обСщания ΠΊ Π½ΡƒΠΆΠ½ΠΎΠΌΡƒ Ρ‚ΠΈΠΏΡƒ
481 /// </summary>
481 /// </summary>
482 /// <typeparam name="T2"></typeparam>
482 /// <typeparam name="T2"></typeparam>
483 /// <returns></returns>
483 /// <returns></returns>
484 public IPromise<T2> Cast<T2>() {
484 public IPromise<T2> Cast<T2>() {
485 return Map(x => (T2)(object)x, null);
485 return Map(x => (T2)(object)x, null);
486 }
486 }
487
487
488 /// <summary>
488 /// <summary>
489 /// ДоТидаСтся ΠΎΡ‚Π»ΠΎΠΆΠ΅Π½Π½ΠΎΠ³ΠΎ обСщания ΠΈ Π² случаС успСха, Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚
489 /// ДоТидаСтся ΠΎΡ‚Π»ΠΎΠΆΠ΅Π½Π½ΠΎΠ³ΠΎ обСщания ΠΈ Π² случаС успСха, Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚
490 /// Π΅Π³ΠΎ, Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚, Π² ΠΏΡ€ΠΎΡ‚ΠΈΠ²Π½ΠΎΠΌ случаС бросаСт ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅.
490 /// Π΅Π³ΠΎ, Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚, Π² ΠΏΡ€ΠΎΡ‚ΠΈΠ²Π½ΠΎΠΌ случаС бросаСт ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅.
491 /// </summary>
491 /// </summary>
492 /// <remarks>
492 /// <remarks>
493 /// <para>
493 /// <para>
494 /// Если ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ обСщания Π±Ρ‹Π»ΠΎ ΠΏΡ€Π΅Ρ€Π²Π°Π½ΠΎ ΠΏΠΎ Ρ‚Π°ΠΉΠΌΠ°ΡƒΡ‚Ρƒ, это Π½Π΅ Π·Π½Π°Ρ‡ΠΈΡ‚,
494 /// Если ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ обСщания Π±Ρ‹Π»ΠΎ ΠΏΡ€Π΅Ρ€Π²Π°Π½ΠΎ ΠΏΠΎ Ρ‚Π°ΠΉΠΌΠ°ΡƒΡ‚Ρƒ, это Π½Π΅ Π·Π½Π°Ρ‡ΠΈΡ‚,
495 /// Ρ‡Ρ‚ΠΎ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ Π±Ρ‹Π»ΠΎ ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½ΠΎ ΠΈΠ»ΠΈ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π² этом Ρ€ΠΎΠ΄Π΅, это Ρ‚ΠΎΠ»ΡŒΠΊΠΎ
495 /// Ρ‡Ρ‚ΠΎ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ Π±Ρ‹Π»ΠΎ ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½ΠΎ ΠΈΠ»ΠΈ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π² этом Ρ€ΠΎΠ΄Π΅, это Ρ‚ΠΎΠ»ΡŒΠΊΠΎ
496 /// ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ Π΅Π³ΠΎ Π½Π΅ доТдались, ΠΎΠ΄Π½Π°ΠΊΠΎ всС зарСгистрированныС
496 /// ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ Π΅Π³ΠΎ Π½Π΅ доТдались, ΠΎΠ΄Π½Π°ΠΊΠΎ всС зарСгистрированныС
497 /// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ, ΠΊΠ°ΠΊ Π±Ρ‹Π»ΠΈ Ρ‚Π°ΠΊ ΠΎΡΡ‚Π°Π»ΠΈΡΡŒ ΠΈ ΠΎΠ½ΠΈ Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹Π·Π²Π°Π½Ρ‹, ΠΊΠΎΠ³Π΄Π°
497 /// ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ, ΠΊΠ°ΠΊ Π±Ρ‹Π»ΠΈ Ρ‚Π°ΠΊ ΠΎΡΡ‚Π°Π»ΠΈΡΡŒ ΠΈ ΠΎΠ½ΠΈ Π±ΡƒΠ΄ΡƒΡ‚ Π²Ρ‹Π·Π²Π°Π½Ρ‹, ΠΊΠΎΠ³Π΄Π°
498 /// ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ.
498 /// ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ.
499 /// </para>
499 /// </para>
500 /// <para>
500 /// <para>
501 /// Π’Π°ΠΊΠΎΠ΅ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ Π²ΠΏΠΎΠ»Π½Π΅ ΠΎΠΏΡ€Π°Π²Π΄Π°Π½ΠΎ ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Ρ‚Π°ΠΉΠΌΠ°ΡƒΡ‚ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΡΡ‚Π΅Ρ‡ΡŒ
501 /// Π’Π°ΠΊΠΎΠ΅ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ Π²ΠΏΠΎΠ»Π½Π΅ ΠΎΠΏΡ€Π°Π²Π΄Π°Π½ΠΎ ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Ρ‚Π°ΠΉΠΌΠ°ΡƒΡ‚ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΡΡ‚Π΅Ρ‡ΡŒ
502 /// Π² Ρ‚ΠΎΡ‚ ΠΌΠΎΠΌΠ΅Π½Ρ‚, ΠΊΠΎΠ³Π΄Π° Π½Π°Ρ‡Π°Π»Π°ΡΡŒ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ², ΠΈ
502 /// Π² Ρ‚ΠΎΡ‚ ΠΌΠΎΠΌΠ΅Π½Ρ‚, ΠΊΠΎΠ³Π΄Π° Π½Π°Ρ‡Π°Π»Π°ΡΡŒ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ², ΠΈ
503 /// ΠΊ Ρ‚ΠΎΠΌΡƒ ΠΆΠ΅ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΡΡ‚ΠΎΡΡ‚ΡŒ Π² Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ ΠΈ Π΅Π³ΠΎ
503 /// ΠΊ Ρ‚ΠΎΠΌΡƒ ΠΆΠ΅ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΡΡ‚ΠΎΡΡ‚ΡŒ Π² Ρ†Π΅ΠΏΠΎΡ‡ΠΊΠ΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ ΠΈ Π΅Π³ΠΎ
504 /// ΠΎΡ‚ΠΊΠ»ΠΎΠ½Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ привСсти ΠΊ Π½Π΅ΠΏΡ€ΠΎΠ³Π½ΠΎΠ·ΠΈΡ€ΡƒΠ΅ΠΌΠΎΠΌΡƒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρƒ.
504 /// ΠΎΡ‚ΠΊΠ»ΠΎΠ½Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ привСсти ΠΊ Π½Π΅ΠΏΡ€ΠΎΠ³Π½ΠΎΠ·ΠΈΡ€ΡƒΠ΅ΠΌΠΎΠΌΡƒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρƒ.
505 /// </para>
505 /// </para>
506 /// </remarks>
506 /// </remarks>
507 /// <param name="timeout">ВрСмя оТидания</param>
507 /// <param name="timeout">ВрСмя оТидания</param>
508 /// <returns>Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполнСния обСщания</returns>
508 /// <returns>Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполнСния обСщания</returns>
509 public T Join(int timeout) {
509 public T Join(int timeout) {
510 var evt = new ManualResetEvent(false);
510 var evt = new ManualResetEvent(false);
511 Anyway(() => evt.Set());
511 Anyway(() => evt.Set());
512 Cancelled(() => evt.Set());
512 Cancelled(() => evt.Set());
513
513
514 if (!evt.WaitOne(timeout, true))
514 if (!evt.WaitOne(timeout, true))
515 throw new TimeoutException();
515 throw new TimeoutException();
516
516
517 switch (m_state) {
517 switch (m_state) {
518 case SucceededState:
518 case SucceededState:
519 return m_result;
519 return m_result;
520 case CancelledState:
520 case CancelledState:
521 throw new OperationCanceledException();
521 throw new OperationCanceledException();
522 case RejectedState:
522 case RejectedState:
523 throw new TargetInvocationException(m_error);
523 throw new TargetInvocationException(m_error);
524 default:
524 default:
525 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
525 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
526 }
526 }
527 }
527 }
528
528
529 public T Join() {
529 public T Join() {
530 return Join(Timeout.Infinite);
530 return Join(Timeout.Infinite);
531 }
531 }
532
532
533 void AddHandler(ResultHandler<T> success, ErrorHandler error, Action cancel) {
533 void AddHandler(ResultHandler<T> success, ErrorHandler error, Action cancel) {
534 Interlocked.Increment(ref m_childrenCount);
534 Interlocked.Increment(ref m_childrenCount);
535
535
536 HandlerDescriptor handler = new HandlerDescriptor {
536 HandlerDescriptor handler = new HandlerDescriptor {
537 resultHandler = success,
537 resultHandler = success,
538 errorHandler = error,
538 errorHandler = error,
539 cancellHandler = cancel
539 cancellHandler = cancel
540 };
540 };
541
541
542 bool queued;
542 bool queued;
543
543
544 if (!IsResolved) {
544 if (!IsResolved) {
545 m_handlers.Enqueue(handler);
545 m_handlers.Enqueue(handler);
546 queued = true;
546 queued = true;
547 } else {
547 } else {
548 // the promise is in resolved state, just invoke the handled with minimum overhead
548 // the promise is in resolved state, just invoke the handled with minimum overhead
549 queued = false;
549 queued = false;
550 InvokeHandler(handler);
550 InvokeHandler(handler);
551 }
551 }
552
552
553 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
553 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
554 // if the promise have been resolved while we was adding handler to the queue
554 // if the promise have been resolved while we was adding handler to the queue
555 // we can't guarantee that someone is still processing it
555 // we can't guarantee that someone is still processing it
556 // therefore we will fetch a handler from the queue and execute it
556 // therefore we will fetch a handler from the queue and execute it
557 // note that fetched handler may be not the one that we have added
557 // note that fetched handler may be not the one that we have added
558 // even we can fetch no handlers at all :)
558 // even we can fetch no handlers at all :)
559 InvokeHandler(handler);
559 InvokeHandler(handler);
560 }
560 }
561
561
562 protected virtual void InvokeHandler(HandlerDescriptor handler) {
562 protected virtual void InvokeHandler(HandlerDescriptor handler) {
563 switch (m_state) {
563 switch (m_state) {
564 case SucceededState:
564 case SucceededState:
565 handler.Resolve(m_result);
565 handler.Resolve(m_result);
566 break;
566 break;
567 case RejectedState:
567 case RejectedState:
568 handler.Reject(m_error);
568 handler.Reject(m_error);
569 break;
569 break;
570 case CancelledState:
570 case CancelledState:
571 handler.Cancel();
571 handler.Cancel();
572 break;
572 break;
573 default:
573 default:
574 // do nothing
574 // do nothing
575 return;
575 return;
576 }
576 }
577 }
577 }
578
578
579 protected virtual void OnStateChanged() {
579 protected virtual void OnStateChanged() {
580 HandlerDescriptor handler;
580 HandlerDescriptor handler;
581 while (m_handlers.TryDequeue(out handler))
581 while (m_handlers.TryDequeue(out handler))
582 InvokeHandler(handler);
582 InvokeHandler(handler);
583 }
583 }
584
584
585 public bool IsExclusive {
585 public bool IsExclusive {
586 get {
586 get {
587 return m_childrenCount <= 1;
587 return m_childrenCount <= 1;
588 }
588 }
589 }
589 }
590
590
591 protected bool Cancel(bool dependencies) {
591 protected bool Cancel(bool dependencies) {
592 if (BeginTransit()) {
592 if (BeginTransit()) {
593 CompleteTransit(CancelledState);
593 CompleteTransit(CancelledState);
594 OnStateChanged();
594 OnStateChanged();
595
595
596 if (dependencies && m_parent != null && m_parent.IsExclusive)
596 if (dependencies && m_parent != null && m_parent.IsExclusive)
597 m_parent.Cancel();
597 m_parent.Cancel();
598
598
599 return true;
599 return true;
600 } else {
600 } else {
601 return false;
601 return false;
602 }
602 }
603 }
603 }
604
604
605 /// <summary>
605 /// <summary>
606 /// ΠžΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΠ΅Ρ‚ нСсколько ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ Π² ΠΎΠ΄Π½ΠΎ, Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠΌ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ являСтся массив Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ² Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ.
606 /// ΠžΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΠ΅Ρ‚ нСсколько ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ Π² ΠΎΠ΄Π½ΠΎ, Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠΌ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ являСтся массив Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ² Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ.
607 /// Если хотябы ΠΎΠ΄Π½ΠΎ ΠΈΠ· ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π½Ρ‹Ρ… ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ, Ρ‚ΠΎ Π½ΠΎΠ²ΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π΅Π½ΠΈΠ΅ Ρ‚ΠΎΠΆΠ΅ Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ.
607 /// Если хотябы ΠΎΠ΄Π½ΠΎ ΠΈΠ· ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π½Ρ‹Ρ… ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ, Ρ‚ΠΎ Π½ΠΎΠ²ΠΎΠ΅ ΠΎΠ±Π΅Ρ‰Π΅Π½ΠΈΠ΅ Ρ‚ΠΎΠΆΠ΅ Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΎ.
608 /// ΠŸΡ€ΠΈ ΠΎΡ‚ΠΌΠ΅Π½Π΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ обСщания, ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π½Ρ‹Π΅ обСщания Ρ‚Π°ΠΊΠΆΠ΅ Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½Ρ‹, Ссли Π½ΠΈΠΊΡ‚ΠΎ большС Π½Π° Π½ΠΈΡ… Π½Π΅ подписан.
608 /// ΠŸΡ€ΠΈ ΠΎΡ‚ΠΌΠ΅Π½Π΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ обСщания, ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π½Ρ‹Π΅ обСщания Ρ‚Π°ΠΊΠΆΠ΅ Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΡ‚ΠΌΠ΅Π½Π΅Π½Ρ‹, Ссли Π½ΠΈΠΊΡ‚ΠΎ большС Π½Π° Π½ΠΈΡ… Π½Π΅ подписан.
609 /// </summary>
609 /// </summary>
610 /// <param name="promises">Бписок ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ. Если список пустой, Ρ‚ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚ΠΈΡ€ΡƒΡŽΡ‰Π΅Π΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ возвращаСтся ΡƒΠΆΠ΅ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½Π½Ρ‹ΠΌ.</param>
610 /// <param name="promises">Бписок ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ. Если список пустой, Ρ‚ΠΎ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚ΠΈΡ€ΡƒΡŽΡ‰Π΅Π΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ возвращаСтся ΡƒΠΆΠ΅ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½Π½Ρ‹ΠΌ.</param>
611 /// <returns>ΠžΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΡŽΡ‰Π΅Π΅ Π² сСбС Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π½Ρ‹Ρ… ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ.</returns>
611 /// <returns>ΠžΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅ ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΡŽΡ‰Π΅Π΅ Π² сСбС Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π½Ρ‹Ρ… ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠΉ.</returns>
612 /// <exception cref="ArgumentNullException"><paramref name="promises"/> Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ null</exception>
612 /// <exception cref="ArgumentNullException"><paramref name="promises"/> Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ null</exception>
613 public static Promise<T[]> CreateComposite(IList<Promise<T>> promises) {
613 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) {
614 if (promises == null)
614 if (promises == null)
615 throw new ArgumentNullException();
615 throw new ArgumentNullException();
616
616
617 // создаСм аккумулятор для Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ² ΠΈ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚ΠΈΡ€ΡƒΡŽΡ‰Π΅Π΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅
617 // создаСм аккумулятор для Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ² ΠΈ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚ΠΈΡ€ΡƒΡŽΡ‰Π΅Π΅ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅
618 var result = new T[promises.Count];
618 var result = new T[promises.Count];
619 var promise = new Promise<T[]>();
619 var promise = new Promise<T[]>();
620
620
621 // special case
621 // special case
622 if (promises.Count == 0) {
622 if (promises.Count == 0) {
623 promise.Resolve(result);
623 promise.Resolve(result);
624 return promise;
624 return promise;
625 }
625 }
626
626
627 int pending = promises.Count;
627 int pending = promises.Count;
628
628
629 for (int i = 0; i < promises.Count; i++) {
629 for (int i = 0; i < promises.Count; i++) {
630 var dest = i;
630 var dest = i;
631
631
632 promises[i].Then(
632 promises[i].Then(
633 x => {
633 x => {
634 result[dest] = x;
634 result[dest] = x;
635 if(Interlocked.Decrement(ref pending) == 0)
635 if(Interlocked.Decrement(ref pending) == 0)
636 promise.Resolve(result);
636 promise.Resolve(result);
637 },
637 },
638 e => promise.Reject(e)
638 e => promise.Reject(e)
639 );
639 );
640 }
640 }
641
641
642 promise.Cancelled(
642 promise.Cancelled(
643 () => {
643 () => {
644 foreach(var d in promises)
644 foreach(var d in promises)
645 if(d.IsExclusive)
645 if(d.IsExclusive)
646 d.Cancel();
646 d.Cancel();
647 }
647 }
648 );
648 );
649
649
650 return promise;
650 return promise;
651 }
651 }
652
652
653 public static Promise<T> ResultToPromise(T result) {
653 public static Promise<T> ResultToPromise(T result) {
654 var p = new Promise<T>();
654 var p = new Promise<T>();
655 p.Resolve(result);
655 p.Resolve(result);
656 return p;
656 return p;
657 }
657 }
658
658
659 public static Promise<T> ExceptionToPromise(Exception error) {
659 public static Promise<T> ExceptionToPromise(Exception error) {
660 if (error == null)
660 if (error == null)
661 throw new ArgumentNullException();
661 throw new ArgumentNullException();
662
662
663 var p = new Promise<T>();
663 var p = new Promise<T>();
664 p.Reject(error);
664 p.Reject(error);
665 return p;
665 return p;
666 }
666 }
667
667
668 }
668 }
669 }
669 }
General Comments 0
You need to be logged in to leave comments. Login now