##// END OF EJS Templates
promises refactoring
cin -
r106:d4e38929ce36 v2
parent child
Show More
@@ -0,0 +1,135
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Collections;
5
6 namespace Implab.Parallels {
7 public class MTCustomQueue<TNode> : IEnumerable<TNode> where TNode : MTCustomQueueNode<TNode> {
8 TNode m_first;
9 TNode m_last;
10
11 public void Enqueue(TNode next) {
12 Thread.MemoryBarrier();
13
14 var last = m_last;
15
16 // Interlocaked.CompareExchange implies Thread.MemoryBarrier();
17 // to ensure that the next node is completely constructed
18 while (last != Interlocked.CompareExchange(ref m_last, next, last))
19 last = m_last;
20
21 if (last != null)
22 last.next = next;
23 else
24 m_first = next;
25 }
26
27 public bool TryDequeue(out TNode node) {
28 TNode first;
29 TNode next;
30 node = null;
31
32 Thread.MemoryBarrier();
33 do {
34 first = m_first;
35 if (first == null)
36 return false;
37 next = first.next;
38 if (next == null) {
39 // this is the last element,
40 // then try to update the tail
41 if (first != Interlocked.CompareExchange(ref m_last, null, first)) {
42 // this is the race condition
43 if (m_last == null)
44 // the queue is empty
45 return false;
46 // tail has been changed, we need to restart
47 continue;
48 }
49
50 // tail succesfully updated and first.next will never be changed
51 // other readers will fail due to inconsistency m_last != m_fist && m_first.next == null
52 // however the parallel writer may update the m_first since the m_last is null
53
54 // so we need to fix inconsistency by setting m_first to null or if it has been
55 // updated by the writer already then we should just to give up
56 Interlocked.CompareExchange(ref m_first, null, first);
57 break;
58
59 }
60 if (first == Interlocked.CompareExchange(ref m_first, next, first))
61 // head succesfully updated
62 break;
63 } while (true);
64
65 node = first;
66 return true;
67 }
68
69 #region IEnumerable implementation
70
71 class Enumerator : IEnumerator<TNode> {
72 TNode m_current;
73 TNode m_first;
74
75 public Enumerator(TNode first) {
76 m_first = first;
77 }
78
79 #region IEnumerator implementation
80
81 public bool MoveNext() {
82 m_current = m_current == null ? m_first : m_current.next;
83 return m_current != null;
84 }
85
86 public void Reset() {
87 m_current = null;
88 }
89
90 object IEnumerator.Current {
91 get {
92 if (m_current == null)
93 throw new InvalidOperationException();
94 return m_current;
95 }
96 }
97
98 #endregion
99
100 #region IDisposable implementation
101
102 public void Dispose() {
103 }
104
105 #endregion
106
107 #region IEnumerator implementation
108
109 public TNode Current {
110 get {
111 if (m_current == null)
112 throw new InvalidOperationException();
113 return m_current;
114 }
115 }
116
117 #endregion
118 }
119
120 public IEnumerator<TNode> GetEnumerator() {
121 return new Enumerator(m_first);
122 }
123
124 #endregion
125
126 #region IEnumerable implementation
127
128 IEnumerator IEnumerable.GetEnumerator() {
129 return GetEnumerator();
130 }
131
132 #endregion
133 }
134 }
135
@@ -0,0 +1,6
1 namespace Implab.Parallels {
2 public class MTCustomQueueNode<TNode> where TNode : MTCustomQueueNode<TNode> {
3 public TNode next;
4 }
5 }
6
@@ -1,89 +1,95
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Windows.Forms;
6 6 using System.Diagnostics;
7 7
8 8 namespace Implab.Fx
9 9 {
10 10 public static class AnimationHelpers
11 11 {
12 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 14 if (animation == null)
15 15 throw new ArgumentNullException("animation");
16 16
17 17 TVal oldValue = getter(animation.Traget);
18 18
19 19 animation.Step += (target, elaped, duration) =>
20 20 {
21 21 var value = fx(oldValue, newValue, elaped, duration);
22 22 setter(target, value);
23 23 };
24 24
25 25 return animation;
26 26 }
27 27
28 28 public static Animation<T> AnimateTransparency<T>(this T ctl, float newValue) where T : Form
29 29 {
30 30 var anim = new Animation<T>(ctl);
31 31
32 32 anim.AnimateProperty(
33 33 (target, value) => target.Opacity = value,
34 34 target => target.Opacity,
35 35 newValue,
36 36 (ov, nv, el, du) => ov + ((float)el / du) * (nv - ov)
37 37 );
38 38 return anim;
39 39 }
40 40
41 41 public static IPromise<T> CloseFadeOut<T>(this T ctl) where T : Form
42 42 {
43 43 var anim = ctl.AnimateTransparency(0);
44 44
45 return anim.Play().DispatchToControl(ctl).Then(frm => frm.Close());
45 return anim
46 .Play()
47 .DispatchToControl(ctl)
48 .Then(frm => {
49 frm.Close();
50 return frm;
51 });
46 52 }
47 53
48 54 public static IPromise<T> OverlayFadeIn<T>(this Form that, T overlay) where T : Form
49 55 {
50 56 if (that == null)
51 57 throw new ArgumentNullException("that");
52 58 if (overlay == null)
53 59 throw new ArgumentNullException("overlay");
54 60
55 61 // setup overlay
56 62 overlay.Opacity = 0;
57 63 overlay.FormBorderStyle = FormBorderStyle.None;
58 64 overlay.ShowInTaskbar = false;
59 65
60 66 that.AddOwnedForm(overlay);
61 67
62 68 EventHandler handler = (object sender, EventArgs args) =>
63 69 {
64 70 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
65 71 };
66 72
67 73 // attach handlers
68 74 that.Move += handler;
69 75 that.Resize += handler;
70 76 that.Shown += handler;
71 77
72 78 // remove handlers to release overlay
73 79 overlay.FormClosed += (sender, args) =>
74 80 {
75 81 that.Move -= handler;
76 82 that.Resize -= handler;
77 83 that.Shown -= handler;
78 84 };
79 85
80 86 overlay.Show(that);
81 87 overlay.Bounds = that.RectangleToScreen(that.ClientRectangle);
82 88
83 89 return overlay
84 90 .AnimateTransparency(1)
85 91 .Play()
86 92 .DispatchToControl(overlay);
87 93 }
88 94 }
89 95 }
@@ -1,30 +1,30
1 1 using System.Windows.Forms;
2 2 using System;
3 3
4 4
5 5 namespace Implab.Fx {
6 6 public class ControlBoundPromise<T> : Promise<T> {
7 7 readonly Control m_target;
8 8
9 9 public ControlBoundPromise(Control target) {
10 10 Safe.ArgumentNotNull(target, "target");
11 11
12 12 m_target = target;
13 13 }
14 14
15 15 public ControlBoundPromise(Control target, IPromise parent)
16 16 : base(parent) {
17 17 Safe.ArgumentNotNull(target, "target");
18 18
19 19 m_target = target;
20 20 }
21 21
22 protected override void InvokeHandler(HandlerDescriptor handler) {
22 protected override void InvokeHandler(AbstractHandler handler) {
23 23 if (m_target.InvokeRequired)
24 m_target.BeginInvoke(new Action<HandlerDescriptor>(base.InvokeHandler), handler);
24 m_target.BeginInvoke(new Action<AbstractHandler>(base.InvokeHandler), handler);
25 25 else
26 26 base.InvokeHandler(handler);
27 27 }
28 28 }
29 29 }
30 30
@@ -1,449 +1,452
1 1 using System;
2 2 using System.Reflection;
3 3 using System.Threading;
4 4 using Implab.Parallels;
5 5
6 6 #if MONO
7 7
8 8 using NUnit.Framework;
9 9 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
10 10 using TestMethod = NUnit.Framework.TestAttribute;
11 11
12 12 #else
13 13
14 14 using Microsoft.VisualStudio.TestTools.UnitTesting;
15 15
16 16 #endif
17 17
18 18 namespace Implab.Test {
19 19 [TestClass]
20 20 public class AsyncTests {
21 21 [TestMethod]
22 22 public void ResolveTest() {
23 23 int res = -1;
24 24 var p = new Promise<int>();
25 25 p.Then(x => res = x);
26 26 p.Resolve(100);
27 27
28 28 Assert.AreEqual(100, res);
29 29 }
30 30
31 31 [TestMethod]
32 32 public void RejectTest() {
33 33 int res = -1;
34 34 Exception err = null;
35 35
36 36 var p = new Promise<int>();
37 37 p.Then(
38 38 x => res = x,
39 39 e => {
40 40 err = e;
41 41 return -2;
42 42 }
43 43 );
44 44 p.Reject(new ApplicationException("error"));
45 45
46 46 Assert.AreEqual(res, -1);
47 47 Assert.AreEqual(err.Message, "error");
48 48
49 49 }
50 50
51 51 [TestMethod]
52 52 public void CancelExceptionTest() {
53 53 var p = new Promise<bool>();
54 54 p.Cancel();
55 55
56 56 var p2 = p.Cancelled(() => {
57 57 throw new ApplicationException("CANCELLED");
58 58 });
59 59
60 60 try {
61 61 p2.Join();
62 62 Assert.Fail();
63 63 } catch (ApplicationException err) {
64 64 Assert.AreEqual("CANCELLED", err.InnerException.Message);
65 65 }
66 66
67 67 }
68 68
69 69 [TestMethod]
70 70 public void ContinueOnCancelTest() {
71 71 var p = new Promise<bool>();
72 72 p.Cancel();
73 73
74 74 var p2 = p
75 75 .Cancelled(() => {
76 76 throw new ApplicationException("CANCELLED");
77 77 })
78 78 .Error(e => true);
79 79
80 80 Assert.AreEqual(true, p2.Join());
81 81 }
82 82
83 83 [TestMethod]
84 84 public void JoinSuccessTest() {
85 85 var p = new Promise<int>();
86 86 p.Resolve(100);
87 87 Assert.AreEqual(p.Join(), 100);
88 88 }
89 89
90 90 [TestMethod]
91 91 public void JoinFailTest() {
92 92 var p = new Promise<int>();
93 93 p.Reject(new ApplicationException("failed"));
94 94
95 95 try {
96 96 p.Join();
97 97 throw new ApplicationException("WRONG!");
98 98 } catch (TargetInvocationException err) {
99 99 Assert.AreEqual(err.InnerException.Message, "failed");
100 100 } catch {
101 101 Assert.Fail("Got wrong excaption");
102 102 }
103 103 }
104 104
105 105 [TestMethod]
106 106 public void MapTest() {
107 107 var p = new Promise<int>();
108 108
109 109 var p2 = p.Then(x => x.ToString());
110 110 p.Resolve(100);
111 111
112 112 Assert.AreEqual(p2.Join(), "100");
113 113 }
114 114
115 115 [TestMethod]
116 116 public void FixErrorTest() {
117 117 var p = new Promise<int>();
118 118
119 119 var p2 = p.Error(e => 101);
120 120
121 121 p.Reject(new Exception());
122 122
123 123 Assert.AreEqual(p2.Join(), 101);
124 124 }
125 125
126 126 [TestMethod]
127 127 public void ChainTest() {
128 128 var p1 = new Promise<int>();
129 129
130 130 var p3 = p1.Chain(x => {
131 131 var p2 = new Promise<string>();
132 132 p2.Resolve(x.ToString());
133 133 return p2;
134 134 });
135 135
136 136 p1.Resolve(100);
137 137
138 138 Assert.AreEqual(p3.Join(), "100");
139 139 }
140 140
141 141 [TestMethod]
142 142 public void ChainFailTest() {
143 143 var p1 = new Promise<int>();
144 144
145 145 var p3 = p1.Chain(x => {
146 146 var p2 = new Promise<string>();
147 147 p2.Reject(new Exception("DIE!!!"));
148 148 return p2;
149 149 });
150 150
151 151 p1.Resolve(100);
152 152
153 153 Assert.IsTrue(p3.IsResolved);
154 154 }
155 155
156 156 [TestMethod]
157 157 public void PoolTest() {
158 158 var pid = Thread.CurrentThread.ManagedThreadId;
159 159 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
160 160
161 161 Assert.AreNotEqual(pid, p.Join());
162 162 }
163 163
164 164 [TestMethod]
165 165 public void WorkerPoolSizeTest() {
166 166 var pool = new WorkerPool(5, 10, 1);
167 167
168 168 Assert.AreEqual(5, pool.PoolSize);
169 169
170 170 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
171 171 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
172 172 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
173 173
174 174 Assert.AreEqual(5, pool.PoolSize);
175 175
176 176 for (int i = 0; i < 100; i++)
177 177 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
178 178 Thread.Sleep(200);
179 179 Assert.AreEqual(10, pool.PoolSize);
180 180
181 181 pool.Dispose();
182 182 }
183 183
184 184 [TestMethod]
185 185 public void WorkerPoolCorrectTest() {
186 186 var pool = new WorkerPool(0,1000,100);
187 187
188 188 const int iterations = 1000;
189 189 int pending = iterations;
190 190 var stop = new ManualResetEvent(false);
191 191
192 192 var count = 0;
193 193 for (int i = 0; i < iterations; i++) {
194 194 pool
195 195 .Invoke(() => 1)
196 196 .Then(x => Interlocked.Add(ref count, x))
197 197 .Then(x => Math.Log10(x))
198 198 .Anyway(() => {
199 199 Interlocked.Decrement(ref pending);
200 200 if (pending == 0)
201 201 stop.Set();
202 202 });
203 203 }
204 204
205 205 stop.WaitOne();
206 206
207 207 Assert.AreEqual(iterations, count);
208 208 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
209 209 pool.Dispose();
210 210
211 211 }
212 212
213 213 [TestMethod]
214 214 public void WorkerPoolDisposeTest() {
215 215 var pool = new WorkerPool(5, 20);
216 216 Assert.AreEqual(5, pool.PoolSize);
217 217 pool.Dispose();
218 218 Thread.Sleep(500);
219 219 Assert.AreEqual(0, pool.PoolSize);
220 220 pool.Dispose();
221 221 }
222 222
223 223 [TestMethod]
224 224 public void MTQueueTest() {
225 225 var queue = new MTQueue<int>();
226 226 int res;
227 227
228 228 queue.Enqueue(10);
229 229 Assert.IsTrue(queue.TryDequeue(out res));
230 230 Assert.AreEqual(10, res);
231 231 Assert.IsFalse(queue.TryDequeue(out res));
232 232
233 233 for (int i = 0; i < 1000; i++)
234 234 queue.Enqueue(i);
235 235
236 236 for (int i = 0; i < 1000; i++) {
237 237 queue.TryDequeue(out res);
238 238 Assert.AreEqual(i, res);
239 239 }
240 240
241 241 int writers = 0;
242 242 int readers = 0;
243 243 var stop = new ManualResetEvent(false);
244 244 int total = 0;
245 245
246 246 const int itemsPerWriter = 10000;
247 247 const int writersCount = 10;
248 248
249 249 for (int i = 0; i < writersCount; i++) {
250 250 Interlocked.Increment(ref writers);
251 251 AsyncPool
252 252 .InvokeNewThread(() => {
253 253 for (int ii = 0; ii < itemsPerWriter; ii++) {
254 254 queue.Enqueue(1);
255 255 }
256 256 return 1;
257 257 })
258 258 .Anyway(() => Interlocked.Decrement(ref writers));
259 259 }
260 260
261 261 for (int i = 0; i < 10; i++) {
262 262 Interlocked.Increment(ref readers);
263 263 AsyncPool
264 264 .InvokeNewThread(() => {
265 265 int t;
266 266 do {
267 267 while (queue.TryDequeue(out t))
268 268 Interlocked.Add(ref total, t);
269 269 } while (writers > 0);
270 270 return 1;
271 271 })
272 272 .Anyway(() => {
273 273 Interlocked.Decrement(ref readers);
274 274 if (readers == 0)
275 275 stop.Set();
276 276 });
277 277 }
278 278
279 279 stop.WaitOne();
280 280
281 281 Assert.AreEqual(itemsPerWriter * writersCount, total);
282 282 }
283 283
284 284 [TestMethod]
285 285 public void ParallelMapTest() {
286 286
287 287 const int count = 100000;
288 288
289 289 var args = new double[count];
290 290 var rand = new Random();
291 291
292 292 for (int i = 0; i < count; i++)
293 293 args[i] = rand.NextDouble();
294 294
295 295 var t = Environment.TickCount;
296 296 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
297 297
298 298 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
299 299
300 300 t = Environment.TickCount;
301 301 for (int i = 0; i < count; i++)
302 302 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
303 303 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
304 304 }
305 305
306 306 [TestMethod]
307 307 public void ChainedMapTest() {
308 308
309 309 using (var pool = new WorkerPool(0,10,1)) {
310 310 const int count = 10000;
311 311
312 312 var args = new double[count];
313 313 var rand = new Random();
314 314
315 315 for (int i = 0; i < count; i++)
316 316 args[i] = rand.NextDouble();
317 317
318 318 var t = Environment.TickCount;
319 319 var res = args
320 320 .ChainedMap(
321 321 // Analysis disable once AccessToDisposedClosure
322 322 x => pool.Invoke(
323 323 () => Math.Sin(x * x)
324 324 ),
325 325 4
326 326 )
327 327 .Join();
328 328
329 329 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
330 330
331 331 t = Environment.TickCount;
332 332 for (int i = 0; i < count; i++)
333 333 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
334 334 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
335 335 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
336 336 }
337 337 }
338 338
339 339 [TestMethod]
340 340 public void ParallelForEachTest() {
341 341
342 342 const int count = 100000;
343 343
344 344 var args = new int[count];
345 345 var rand = new Random();
346 346
347 347 for (int i = 0; i < count; i++)
348 348 args[i] = (int)(rand.NextDouble() * 100);
349 349
350 350 int result = 0;
351 351
352 352 var t = Environment.TickCount;
353 353 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
354 354
355 355 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
356 356
357 357 int result2 = 0;
358 358
359 359 t = Environment.TickCount;
360 360 for (int i = 0; i < count; i++)
361 361 result2 += args[i];
362 362 Assert.AreEqual(result2, result);
363 363 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
364 364 }
365 365
366 366 [TestMethod]
367 367 public void ComplexCase1Test() {
368 368 var flags = new bool[3];
369 369
370 370 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
371 371
372 372 var step1 = PromiseHelper
373 373 .Sleep(200, "Alan")
374 374 .Cancelled(() => flags[0] = true);
375 375 var p = step1
376 376 .Chain(x =>
377 377 PromiseHelper
378 378 .Sleep(200, "Hi, " + x)
379 379 .Then(y => y)
380 380 .Cancelled(() => flags[1] = true)
381 381 )
382 382 .Cancelled(() => flags[2] = true);
383 383 step1.Join();
384 384 p.Cancel();
385 385 try {
386 386 Assert.AreEqual(p.Join(), "Hi, Alan");
387 387 Assert.Fail("Shouldn't get here");
388 388 } catch (OperationCanceledException) {
389 389 }
390 390
391 391 Assert.IsFalse(flags[0]);
392 392 Assert.IsTrue(flags[1]);
393 393 Assert.IsTrue(flags[2]);
394 394 }
395 395
396 396 [TestMethod]
397 397 public void ChainedCancel1Test() {
398 398 // при отмене сцепленной асинхронной операции все обещание должно
399 399 // завершаться ошибкой OperationCanceledException
400 400 var p = PromiseHelper
401 401 .Sleep(1, "Hi, HAL!")
402 402 .Then(x => {
403 403 // запускаем две асинхронные операции
404 404 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
405 405 // вторая операция отменяет первую до завершения
406 406 PromiseHelper
407 407 .Sleep(100, "HAL, STOP!")
408 408 .Then(result.Cancel);
409 409 return result;
410 410 });
411 411 try {
412 412 p.Join();
413 413 } catch (TargetInvocationException err) {
414 414 Assert.IsTrue(err.InnerException is OperationCanceledException);
415 415 }
416 416 }
417 417
418 418 [TestMethod]
419 419 public void ChainedCancel2Test() {
420 420 // при отмене цепочки обещаний, вложенные операции также должны отменяться
421 421 var pSurvive = new Promise<bool>();
422 422 var hemStarted = new ManualResetEvent(false);
423 423 var p = PromiseHelper
424 424 .Sleep(1, "Hi, HAL!")
425 425 .Chain(x => {
426 426 hemStarted.Set();
427 427 // запускаем две асинхронные операции
428 428 var result = PromiseHelper
429 .Sleep(10000, "HEM ENABLED!!!")
430 .Then(s => pSurvive.Resolve(false));
429 .Sleep(100000000, "HEM ENABLED!!!")
430 .Then(s => {
431 pSurvive.Resolve(false);
432 return s;
433 });
431 434
432 435 result
433 436 .Cancelled(() => pSurvive.Resolve(true));
434 437
435 438 return result;
436 439 });
437 440
438 441 hemStarted.WaitOne();
439 442 p.Cancel();
440 443
441 444 try {
442 445 p.Join();
443 446 } catch (OperationCanceledException) {
444 447 Assert.IsTrue(pSurvive.Join());
445 448 }
446 449 }
447 450 }
448 451 }
449 452
@@ -1,40 +1,34
1 1 using System;
2 2
3 3 namespace Implab {
4 4 public interface IPromise<T> : IPromise {
5 5
6 6 new T Join();
7 7
8 8 new T Join(int timeout);
9 9
10 10 void On(Action<T> success, Action<Exception> error, Action cancel);
11 11
12 12 void On(Action<T> success, Action<Exception> error);
13 13
14 14 void On(Action<T> success);
15 15
16 IPromise<T> Then(Action<T> success, Func<Exception,T> error, Action cancel);
17
18 IPromise<T> Then(Action<T> success, Func<Exception,T> error);
19
20 IPromise<T> Then(Action<T> success);
21
22 16 IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error, Action cancel);
23 17
24 18 IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception,T2> error);
25 19
26 20 IPromise<T2> Then<T2>(Func<T, T2> mapper);
27 21
28 22 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error, Action cancel);
29 23
30 24 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception,IPromise<T2>> error);
31 25
32 26 IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained);
33 27
34 28 IPromise<T> Error(Func<Exception,T> error);
35 29
36 30 new IPromise<T> Cancelled(Action handler);
37 31
38 32 new IPromise<T> Anyway(Action handler);
39 33 }
40 34 }
@@ -1,223 +1,225
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
7 7 <OutputType>Library</OutputType>
8 8 <RootNamespace>Implab</RootNamespace>
9 9 <AssemblyName>Implab</AssemblyName>
10 10 </PropertyGroup>
11 11 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
12 12 <DebugSymbols>true</DebugSymbols>
13 13 <DebugType>full</DebugType>
14 14 <Optimize>false</Optimize>
15 15 <OutputPath>bin\Debug</OutputPath>
16 16 <DefineConstants>TRACE;DEBUG;</DefineConstants>
17 17 <ErrorReport>prompt</ErrorReport>
18 18 <WarningLevel>4</WarningLevel>
19 19 <ConsolePause>false</ConsolePause>
20 20 <RunCodeAnalysis>true</RunCodeAnalysis>
21 21 </PropertyGroup>
22 22 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
23 23 <DebugType>full</DebugType>
24 24 <Optimize>true</Optimize>
25 25 <OutputPath>bin\Release</OutputPath>
26 26 <ErrorReport>prompt</ErrorReport>
27 27 <WarningLevel>4</WarningLevel>
28 28 <ConsolePause>false</ConsolePause>
29 29 </PropertyGroup>
30 30 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
31 31 <DebugSymbols>true</DebugSymbols>
32 32 <DebugType>full</DebugType>
33 33 <Optimize>false</Optimize>
34 34 <OutputPath>bin\Debug</OutputPath>
35 35 <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants>
36 36 <ErrorReport>prompt</ErrorReport>
37 37 <WarningLevel>4</WarningLevel>
38 38 <RunCodeAnalysis>true</RunCodeAnalysis>
39 39 <ConsolePause>false</ConsolePause>
40 40 </PropertyGroup>
41 41 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
42 42 <Optimize>true</Optimize>
43 43 <OutputPath>bin\Release</OutputPath>
44 44 <ErrorReport>prompt</ErrorReport>
45 45 <WarningLevel>4</WarningLevel>
46 46 <ConsolePause>false</ConsolePause>
47 47 <DefineConstants>NET_4_5</DefineConstants>
48 48 </PropertyGroup>
49 49 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMono|AnyCPU' ">
50 50 <DebugSymbols>true</DebugSymbols>
51 51 <DebugType>full</DebugType>
52 52 <Optimize>false</Optimize>
53 53 <OutputPath>bin\Debug</OutputPath>
54 54 <DefineConstants>TRACE;DEBUG;NET_4_5;MONO</DefineConstants>
55 55 <ErrorReport>prompt</ErrorReport>
56 56 <WarningLevel>4</WarningLevel>
57 57 <RunCodeAnalysis>true</RunCodeAnalysis>
58 58 <ConsolePause>false</ConsolePause>
59 59 </PropertyGroup>
60 60 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMono|AnyCPU' ">
61 61 <Optimize>true</Optimize>
62 62 <OutputPath>bin\Release</OutputPath>
63 63 <DefineConstants>NET_4_5;MONO;</DefineConstants>
64 64 <ErrorReport>prompt</ErrorReport>
65 65 <WarningLevel>4</WarningLevel>
66 66 <ConsolePause>false</ConsolePause>
67 67 </PropertyGroup>
68 68 <ItemGroup>
69 69 <Reference Include="System" />
70 70 <Reference Include="System.Xml" />
71 71 </ItemGroup>
72 72 <ItemGroup>
73 73 <Compile Include="Component.cs" />
74 74 <Compile Include="CustomEqualityComparer.cs" />
75 75 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
76 76 <Compile Include="Diagnostics\EventText.cs" />
77 77 <Compile Include="Diagnostics\IEventTextFormatter.cs" />
78 78 <Compile Include="Diagnostics\LogChannel.cs" />
79 79 <Compile Include="Diagnostics\LogicalOperation.cs" />
80 80 <Compile Include="Diagnostics\TextFileListener.cs" />
81 81 <Compile Include="Diagnostics\TextListenerBase.cs" />
82 82 <Compile Include="Diagnostics\TraceLog.cs" />
83 83 <Compile Include="Diagnostics\TraceEvent.cs" />
84 84 <Compile Include="Diagnostics\TraceEventType.cs" />
85 85 <Compile Include="Disposable.cs" />
86 86 <Compile Include="ICancellable.cs" />
87 87 <Compile Include="IProgressHandler.cs" />
88 88 <Compile Include="IProgressNotifier.cs" />
89 89 <Compile Include="IPromiseT.cs" />
90 90 <Compile Include="IPromise.cs" />
91 91 <Compile Include="IServiceLocator.cs" />
92 92 <Compile Include="ITaskController.cs" />
93 93 <Compile Include="JSON\JSONElementContext.cs" />
94 94 <Compile Include="JSON\JSONElementType.cs" />
95 95 <Compile Include="JSON\JSONGrammar.cs" />
96 96 <Compile Include="JSON\JSONParser.cs" />
97 97 <Compile Include="JSON\JSONScanner.cs" />
98 98 <Compile Include="JSON\JsonTokenType.cs" />
99 99 <Compile Include="JSON\JSONWriter.cs" />
100 100 <Compile Include="JSON\JSONXmlReader.cs" />
101 101 <Compile Include="JSON\JSONXmlReaderOptions.cs" />
102 102 <Compile Include="JSON\StringTranslator.cs" />
103 103 <Compile Include="Parallels\DispatchPool.cs" />
104 104 <Compile Include="Parallels\ArrayTraits.cs" />
105 105 <Compile Include="Parallels\MTQueue.cs" />
106 106 <Compile Include="Parallels\WorkerPool.cs" />
107 107 <Compile Include="Parsing\Alphabet.cs" />
108 108 <Compile Include="Parsing\AlphabetBase.cs" />
109 109 <Compile Include="Parsing\AltToken.cs" />
110 110 <Compile Include="Parsing\BinaryToken.cs" />
111 111 <Compile Include="Parsing\CatToken.cs" />
112 112 <Compile Include="Parsing\CDFADefinition.cs" />
113 113 <Compile Include="Parsing\DFABuilder.cs" />
114 114 <Compile Include="Parsing\DFADefinitionBase.cs" />
115 115 <Compile Include="Parsing\DFAStateDescriptor.cs" />
116 116 <Compile Include="Parsing\DFAutomaton.cs" />
117 117 <Compile Include="Parsing\EDFADefinition.cs" />
118 118 <Compile Include="Parsing\EmptyToken.cs" />
119 119 <Compile Include="Parsing\EndToken.cs" />
120 120 <Compile Include="Parsing\EnumAlphabet.cs" />
121 121 <Compile Include="Parsing\Grammar.cs" />
122 122 <Compile Include="Parsing\IAlphabet.cs" />
123 123 <Compile Include="Parsing\IDFADefinition.cs" />
124 124 <Compile Include="Parsing\IVisitor.cs" />
125 125 <Compile Include="Parsing\ParserException.cs" />
126 126 <Compile Include="Parsing\Scanner.cs" />
127 127 <Compile Include="Parsing\StarToken.cs" />
128 128 <Compile Include="Parsing\SymbolToken.cs" />
129 129 <Compile Include="Parsing\Token.cs" />
130 130 <Compile Include="SafePool.cs" />
131 131 <Compile Include="ServiceLocator.cs" />
132 132 <Compile Include="TaskController.cs" />
133 133 <Compile Include="ProgressInitEventArgs.cs" />
134 134 <Compile Include="Properties\AssemblyInfo.cs" />
135 135 <Compile Include="Promise.cs" />
136 136 <Compile Include="Parallels\AsyncPool.cs" />
137 137 <Compile Include="Safe.cs" />
138 138 <Compile Include="ValueEventArgs.cs" />
139 139 <Compile Include="PromiseExtensions.cs" />
140 140 <Compile Include="TransientPromiseException.cs" />
141 141 <Compile Include="SyncContextPromise.cs" />
142 142 <Compile Include="ObjectPool.cs" />
143 143 <Compile Include="Diagnostics\OperationContext.cs" />
144 144 <Compile Include="Diagnostics\TraceContext.cs" />
145 145 <Compile Include="Diagnostics\LogEventArgs.cs" />
146 146 <Compile Include="Diagnostics\LogEventArgsT.cs" />
147 147 <Compile Include="Diagnostics\Extensions.cs" />
148 148 <Compile Include="IComponentContainer.cs" />
149 149 <Compile Include="MTComponentContainer.cs" />
150 150 <Compile Include="PromiseEventType.cs" />
151 <Compile Include="Parallels\MTCustomQueue.cs" />
152 <Compile Include="Parallels\MTCustomQueueNode.cs" />
151 153 </ItemGroup>
152 154 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
153 155 <ItemGroup />
154 156 <ProjectExtensions>
155 157 <MonoDevelop>
156 158 <Properties>
157 159 <Policies>
158 160 <CSharpFormattingPolicy IndentSwitchBody="True" NamespaceBraceStyle="EndOfLine" ClassBraceStyle="EndOfLine" InterfaceBraceStyle="EndOfLine" StructBraceStyle="EndOfLine" EnumBraceStyle="EndOfLine" MethodBraceStyle="EndOfLine" ConstructorBraceStyle="EndOfLine" DestructorBraceStyle="EndOfLine" BeforeMethodDeclarationParentheses="False" BeforeMethodCallParentheses="False" BeforeConstructorDeclarationParentheses="False" NewLineBeforeConstructorInitializerColon="NewLine" NewLineAfterConstructorInitializerColon="SameLine" BeforeIndexerDeclarationBracket="False" BeforeDelegateDeclarationParentheses="False" NewParentheses="False" SpacesBeforeBrackets="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
159 161 <TextStylePolicy FileWidth="120" EolMarker="Unix" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
160 162 <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
161 163 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="application/xml" />
162 164 <XmlFormattingPolicy inheritsSet="Mono" inheritsScope="application/xml" scope="application/xml" />
163 165 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/plain" />
164 166 <NameConventionPolicy>
165 167 <Rules>
166 168 <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
167 169 <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
168 170 <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
169 171 <RequiredPrefixes>
170 172 <String>I</String>
171 173 </RequiredPrefixes>
172 174 </NamingRule>
173 175 <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
174 176 <RequiredSuffixes>
175 177 <String>Attribute</String>
176 178 </RequiredSuffixes>
177 179 </NamingRule>
178 180 <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
179 181 <RequiredSuffixes>
180 182 <String>EventArgs</String>
181 183 </RequiredSuffixes>
182 184 </NamingRule>
183 185 <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
184 186 <RequiredSuffixes>
185 187 <String>Exception</String>
186 188 </RequiredSuffixes>
187 189 </NamingRule>
188 190 <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
189 191 <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
190 192 <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
191 193 <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
192 194 <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
193 195 <RequiredPrefixes>
194 196 <String>m_</String>
195 197 </RequiredPrefixes>
196 198 </NamingRule>
197 199 <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
198 200 <RequiredPrefixes>
199 201 <String>_</String>
200 202 </RequiredPrefixes>
201 203 </NamingRule>
202 204 <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
203 205 <RequiredPrefixes>
204 206 <String>m_</String>
205 207 </RequiredPrefixes>
206 208 </NamingRule>
207 209 <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
208 210 <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
209 211 <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
210 212 <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
211 213 <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
212 214 <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
213 215 <RequiredPrefixes>
214 216 <String>T</String>
215 217 </RequiredPrefixes>
216 218 </NamingRule>
217 219 </Rules>
218 220 </NameConventionPolicy>
219 221 </Policies>
220 222 </Properties>
221 223 </MonoDevelop>
222 224 </ProjectExtensions>
223 225 </Project> No newline at end of file
@@ -1,932 +1,894
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Reflection;
4 4 using System.Threading;
5 5 using Implab.Parallels;
6 6
7 7 namespace Implab {
8 8
9 9 /// <summary>
10 10 /// Класс для асинхронного получения результатов. Так называемое "обещание".
11 11 /// </summary>
12 12 /// <typeparam name="T">Тип получаемого результата</typeparam>
13 13 /// <remarks>
14 14 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции,
15 15 /// клиент получив такое обещание может установить ряд обратных вызово для получения
16 16 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para>
17 17 /// <para>
18 18 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на
19 19 /// данные события клиент должен использовать методы <c>Then</c>.
20 20 /// </para>
21 21 /// <para>
22 22 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой),
23 23 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о
24 24 /// выполнении обещания.
25 25 /// </para>
26 26 /// <para>
27 27 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался,
28 28 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном
29 29 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в
30 30 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении
31 31 /// обещания.
32 32 /// </para>
33 33 /// <para>
34 34 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать
35 35 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует
36 36 /// использовать соответствующую форму методе <c>Then</c>.
37 37 /// </para>
38 38 /// <para>
39 39 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать
40 40 /// только инициатор обещания иначе могут возникнуть противоречия.
41 41 /// </para>
42 42 /// </remarks>
43 43 public class Promise<T> : IPromise<T> {
44 44
45 protected struct HandlerDescriptor {
46 public Action<T> resultHandler;
47 public Func<Exception,T> errorHandler;
48 public Action cancellHandler;
49 public Promise<T> medium;
45 protected abstract class AbstractHandler : MTCustomQueueNode<AbstractHandler> {
46 public abstract void Resolve(T result);
47 public abstract void Reject(Exception error);
48 public abstract void Cancel();
49 }
50
51 protected class HandlerDescriptor<T2> : AbstractHandler {
52
53 readonly Func<T,T2> m_resultHandler;
54 readonly Func<Exception,T2> m_errorHandler;
55 readonly Action m_cancellHandler;
56 readonly Promise<T2> m_medium;
50 57
51 public void Resolve(T result) {
52 if (resultHandler != null) {
58 public HandlerDescriptor(Func<T,T2> resultHandler, Func<Exception,T2> errorHandler, Action cancelHandler, Promise<T2> medium) {
59 m_resultHandler = resultHandler;
60 m_errorHandler = errorHandler;
61 m_cancellHandler = cancelHandler;
62 m_medium = medium;
63 }
64
65 public override void Resolve(T result) {
66 if (m_resultHandler != null) {
53 67 try {
54 resultHandler(result);
68 if (m_medium != null)
69 m_medium.Resolve(m_resultHandler(result));
70 else
71 m_resultHandler(result);
55 72 } catch (Exception e) {
56 73 Reject(e);
57 return;
58 74 }
59 }
60 if (medium != null)
61 medium.Resolve(result);
75 } else if(m_medium != null)
76 m_medium.Resolve(default(T2));
62 77 }
63 78
64 public void Reject(Exception err) {
65 if (errorHandler != null) {
79 public override void Reject(Exception error) {
80 if (m_errorHandler != null) {
66 81 try {
67 var res = errorHandler(err);
68 if (medium != null)
69 medium.Resolve(res);
82 var res = m_errorHandler(error);
83 if (m_medium != null)
84 m_medium.Resolve(res);
70 85 /*} catch (TransientPromiseException err2) {
71 86 if (medium != null)
72 87 medium.Reject(err2.InnerException);*/
73 88 } catch (Exception err2) {
74 if (medium != null)
75 medium.Reject(err2);
89 if (m_medium != null)
90 m_medium.Reject(err2);
76 91 }
77 } else if (medium != null)
78 medium.Reject(err);
92 } else if (m_medium != null)
93 m_medium.Reject(error);
79 94 }
80 95
81 public void Cancel() {
82 if (cancellHandler != null) {
96 public override void Cancel() {
97 if (m_cancellHandler != null) {
83 98 try {
84 cancellHandler();
99 m_cancellHandler();
85 100 } catch (Exception err) {
86 101 Reject(err);
87 102 return;
88 103 }
89 104 }
90 if (medium != null)
91 medium.Cancel();
105 if (m_medium != null)
106 m_medium.Cancel();
92 107 }
93 108 }
94 109
95 110 const int UNRESOLVED_SATE = 0;
96 111 const int TRANSITIONAL_STATE = 1;
97 112 const int SUCCEEDED_STATE = 2;
98 113 const int REJECTED_STATE = 3;
99 114 const int CANCELLED_STATE = 4;
100 115
101 116 int m_childrenCount;
102 117 int m_state;
103 118 T m_result;
104 119 Exception m_error;
105 120
106 readonly MTQueue<HandlerDescriptor> m_handlers = new MTQueue<HandlerDescriptor>();
121 readonly MTCustomQueue<AbstractHandler> m_handlers = new MTCustomQueue<AbstractHandler>();
122 //readonly MTQueue<AbstractHandler> m_handlers = new MTQueue<AbstractHandler>();
107 123
108 124 public Promise() {
109 125 }
110 126
111 127 public Promise(IPromise parent) {
112 128 if (parent != null)
113 AddHandler(
129 AddHandler<T>(
114 130 null,
115 131 null,
116 132 () => {
117 133 if (parent.IsExclusive)
118 134 parent.Cancel();
119 135 },
120 136 null,
121 137 false
122 138 );
123 139 }
124 140
125 141 bool BeginTransit() {
126 142 return UNRESOLVED_SATE == Interlocked.CompareExchange(ref m_state, TRANSITIONAL_STATE, UNRESOLVED_SATE);
127 143 }
128 144
129 145 void CompleteTransit(int state) {
130 146 if (TRANSITIONAL_STATE != Interlocked.CompareExchange(ref m_state, state, TRANSITIONAL_STATE))
131 147 throw new InvalidOperationException("Can't complete transition when the object isn't in the transitional state");
132 148 }
133 149
134 150 void WaitTransition() {
135 151 while (m_state == TRANSITIONAL_STATE) {
136 152 Thread.MemoryBarrier();
137 153 }
138 154 }
139 155
140 156 public bool IsResolved {
141 157 get {
142 158 Thread.MemoryBarrier();
143 159 return m_state > 1;
144 160 }
145 161 }
146 162
147 163 public bool IsCancelled {
148 164 get {
149 165 Thread.MemoryBarrier();
150 166 return m_state == CANCELLED_STATE;
151 167 }
152 168 }
153 169
154 170 public Type PromiseType {
155 171 get { return typeof(T); }
156 172 }
157 173
158 174 /// <summary>
159 175 /// Выполняет обещание, сообщая об успешном выполнении.
160 176 /// </summary>
161 177 /// <param name="result">Результат выполнения.</param>
162 178 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
163 179 public void Resolve(T result) {
164 180 if (BeginTransit()) {
165 181 m_result = result;
166 182 CompleteTransit(SUCCEEDED_STATE);
167 183 OnStateChanged();
168 184 } else {
169 185 WaitTransition();
170 186 if (m_state != CANCELLED_STATE)
171 187 throw new InvalidOperationException("The promise is already resolved");
172 188 }
173 189 }
174 190
175 191 /// <summary>
176 192 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения.
177 193 /// </summary>
178 194 /// <remarks>
179 195 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение.
180 196 /// </remarks>
181 197 public void Resolve() {
182 198 Resolve(default(T));
183 199 }
184 200
185 201 /// <summary>
186 202 /// Выполняет обещание, сообщая об ошибке
187 203 /// </summary>
188 204 /// <remarks>
189 205 /// Поскольку обещание должно работать в многопточной среде, при его выполнении сразу несколько потоков
190 206 /// могу вернуть ошибку, при этом только первая будет использована в качестве результата, остальные
191 207 /// будут проигнорированы.
192 208 /// </remarks>
193 209 /// <param name="error">Исключение возникшее при выполнении операции</param>
194 210 /// <exception cref="InvalidOperationException">Данное обещание уже выполнено</exception>
195 211 public void Reject(Exception error) {
196 212 if (BeginTransit()) {
197 213 m_error = error is TransientPromiseException ? error.InnerException : error;
198 214 CompleteTransit(REJECTED_STATE);
199 215 OnStateChanged();
200 216 } else {
201 217 WaitTransition();
202 218 if (m_state == SUCCEEDED_STATE)
203 219 throw new InvalidOperationException("The promise is already resolved");
204 220 }
205 221 }
206 222
207 223 /// <summary>
208 224 /// Отменяет операцию, если это возможно.
209 225 /// </summary>
210 226 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
211 227 public void Cancel() {
212 228 if (BeginTransit()) {
213 229 CompleteTransit(CANCELLED_STATE);
214 230 OnStateChanged();
215 231 }
216 232 }
217 233
218 public IPromise<T> Then(Action<T> success, Func<Exception,T> error, Action cancel) {
219 if (success == null && error == null && cancel == null)
220 return this;
221
222 var medium = new Promise<T>(this);
223
224 AddHandler(success, error, cancel, medium, true);
225
226 return medium;
227 }
228
229 /// <summary>
230 /// Adds new handlers to this promise.
231 /// </summary>
232 /// <param name="success">The handler of the successfully completed operation.
233 /// This handler will recieve an operation result as a parameter.</param>
234 /// <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>
235 /// <returns>The new promise chained to this one.</returns>
236 public IPromise<T> Then(Action<T> success, Func<Exception,T> error) {
237 if (success == null && error == null)
238 return this;
239
240 var medium = new Promise<T>(this);
241
242 AddHandler(success, error, null, medium, true);
243
244 return medium;
245 }
246
247
248
249
250 public IPromise<T> Then(Action<T> success) {
251 if (success == null)
252 return this;
253
254 var medium = new Promise<T>(this);
255
256 AddHandler(success, null, null, medium, true);
257
258 return medium;
259 }
260
261 234 /// <summary>
262 235 /// Последний обработчик в цепочки обещаний.
263 236 /// </summary>
264 237 /// <param name="success"></param>
265 238 /// <param name="error"></param>
266 239 /// <param name="cancel"></param>
267 240 /// <remarks>
268 241 /// <para>
269 242 /// Данный метод не создает связанного с текущим обещания и предназначен для окончания
270 243 /// фсинхронной цепочки.
271 244 /// </para>
272 245 /// <para>
273 246 /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка
274 247 /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена
275 248 /// всей цепи обещаний снизу (с самого последнего обещания).
276 249 /// </para>
277 250 /// </remarks>
278 251 public void On(Action<T> success, Action<Exception> error, Action cancel) {
279 252 if (success == null && error == null && cancel == null)
280 253 return;
281 254
282 Func<Exception,T> errorHandler = null;
283 if (error != null)
284 errorHandler = err => {
285 error(err);
255 AddHandler(
256 success != null ? new Func<T,T>(x => {
257 success(x);
258 return x;
259 }) : null,
260 error != null ? new Func<Exception,T>(e => {
261 error(e);
286 262 return default(T);
287 };
288 AddHandler(success, errorHandler, cancel, null, false);
263 }) : null,
264 cancel,
265 null,
266 false
267 );
289 268 }
290 269
291 270 public void On(Action<T> success, Action<Exception> error) {
292 271 On(success, error, null);
293 272 }
294 273
295 274 public void On(Action<T> success) {
296 275 On(success, null, null);
297 276 }
298 277
299 278 public void On(Action handler, PromiseEventType events) {
300 279 Safe.ArgumentNotNull(handler, "handler");
301 280
302 Action<T> success = events.HasFlag(PromiseEventType.Success) ? new Action<T>(x => handler()) : null;
281 Func<T,T> success = events.HasFlag(PromiseEventType.Success) ? new Func<T,T>(x => {
282 handler();
283 return x;
284 }) : null;
303 285 Func<Exception,T> error = events.HasFlag(PromiseEventType.Error) ? new Func<Exception,T>(e => {
304 286 handler();
305 287 return default(T);
306 288 }) : null;
307 289 Action cancel = events.HasFlag(PromiseEventType.Cancelled) ? handler : null;
308 290
309 291 AddHandler(success, error, cancel, null, false);
310 292 }
311 293
312 294 public IPromise Error(Action<Exception> error) {
313 295 if (error == null)
314 296 return this;
315 297
316 298 var medium = new Promise<T>(this);
317 299
318 300 AddHandler(
319 301 null,
320 302 e => {
321 303 error(e);
322 304 return default(T);
323 305 },
324 306 null,
325 307 medium,
326 308 true
327 309 );
328 310
329 311 return medium;
330 312 }
331 313
332 314 /// <summary>
333 315 /// Handles error and allows to keep the promise.
334 316 /// </summary>
335 317 /// <remarks>
336 318 /// If the specified handler throws an exception, this exception will be used to reject the promise.
337 319 /// </remarks>
338 320 /// <param name="handler">The error handler which returns the result of the promise.</param>
339 321 /// <returns>New promise.</returns>
340 322 public IPromise<T> Error(Func<Exception,T> handler) {
341 323 if (handler == null)
342 324 return this;
343 325
344 326 var medium = new Promise<T>(this);
345 327
346 328 AddHandler(null, handler, null, medium, true);
347 329
348 330 return medium;
349 331 }
350 332
351 333 /// <summary>
352 334 /// Позволяет преобразовать результат выполения операции к новому типу.
353 335 /// </summary>
354 336 /// <typeparam name="TNew">Новый тип результата.</typeparam>
355 337 /// <param name="mapper">Преобразование результата к новому типу.</param>
356 338 /// <param name="error">Обработчик ошибки. Данный обработчик получит
357 339 /// исключение возникшее при выполнении операции.</param>
358 340 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
359 341 /// <param name = "cancel"></param>
360 342 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error, Action cancel) {
361 343 Safe.ArgumentNotNull(mapper, "mapper");
362 344
363 345 // создаем прицепленное обещание
364 346 var medium = new Promise<TNew>(this);
365 347
366 Action<T> resultHandler = result => medium.Resolve(mapper(result));
367 Func<Exception,T> errorHandler;
368 if (error != null)
369 errorHandler = e => {
370 try {
371 medium.Resolve(error(e));
372 } catch (Exception e2) {
373 // в случае ошибки нужно передать исключение дальше по цепочке
374 medium.Reject(e2);
375 }
376 return default(T);
377 };
378 else
379 errorHandler = e => {
380 medium.Reject(e);
381 return default(T);
382 };
383
384 Action cancelHandler;
385 if (cancel != null)
386 cancelHandler = () => {
387 cancel();
388 medium.Cancel();
389 };
390 else
391 cancelHandler = medium.Cancel;
392
393
394 348 AddHandler(
395 resultHandler,
396 errorHandler,
397 cancelHandler,
398 null,
349 mapper,
350 error,
351 cancel,
352 medium,
399 353 true
400 354 );
401 355
402 356 return medium;
403 357 }
404 358
405 359 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper, Func<Exception,TNew> error) {
406 360 return Then(mapper, error, null);
407 361 }
408 362
409 363 public IPromise<TNew> Then<TNew>(Func<T, TNew> mapper) {
410 364 return Then(mapper, null, null);
411 365 }
412 366
413 367 /// <summary>
414 368 /// Сцепляет несколько аснхронных операций. Указанная асинхронная операция будет вызвана после
415 369 /// выполнения текущей, а результат текущей операции может быть использован для инициализации
416 370 /// новой операции.
417 371 /// </summary>
418 372 /// <typeparam name="TNew">Тип результата указанной асинхронной операции.</typeparam>
419 373 /// <param name="chained">Асинхронная операция, которая должна будет начаться после выполнения текущей.</param>
420 374 /// <param name="error">Обработчик ошибки. Данный обработчик получит
421 375 /// исключение возникшее при выполнении текуещй операции.</param>
422 376 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
423 377 /// <param name = "cancel"></param>
424 378 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error, Action cancel) {
425 379
426 380 Safe.ArgumentNotNull(chained, "chained");
427 381
428 382 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
429 383 // создать посредника, к которому будут подвызяваться следующие обработчики.
430 384 // когда будет выполнена реальная асинхронная операция, она обратиться к посреднику, чтобы
431 385 // передать через него результаты работы.
432 386 var medium = new Promise<TNew>(this);
433 387
434 Action<T> resultHandler = delegate(T result) {
388 Func<T,T> resultHandler = delegate(T result) {
435 389 if (medium.IsCancelled)
436 return;
390 return default(T);
437 391
438 392 var promise = chained(result);
439 393
440 394 promise.On(
441 395 medium.Resolve,
442 396 medium.Reject,
443 397 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
444 398 );
445 399
446 400 // notify chained operation that it's not needed anymore
447 401 // порядок вызова Then, Cancelled важен, поскольку от этого
448 402 // зависит IsExclusive
449 403 medium.On(
450 404 null,
451 405 null,
452 406 () => {
453 407 if (promise.IsExclusive)
454 408 promise.Cancel();
455 409 }
456 410 );
411
412 return default(T);
457 413 };
458 414
459 415 Func<Exception,T> errorHandler;
460 416
461 417 if (error != null)
462 418 errorHandler = delegate(Exception e) {
463 419 try {
464 420 var promise = error(e);
465 421
466 422 promise.On(
467 423 medium.Resolve,
468 424 medium.Reject,
469 425 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
470 426 );
471 427
472 428 // notify chained operation that it's not needed anymore
473 429 // порядок вызова Then, Cancelled важен, поскольку от этого
474 430 // зависит IsExclusive
475 431 medium.Cancelled(() => {
476 432 if (promise.IsExclusive)
477 433 promise.Cancel();
478 434 });
479 435 } catch (Exception e2) {
480 436 medium.Reject(e2);
481 437 }
482 438 return default(T);
483 439 };
484 440 else
485 441 errorHandler = err => {
486 442 medium.Reject(err);
487 443 return default(T);
488 444 };
489 445
490 446
491 447 Action cancelHandler;
492 448 if (cancel != null)
493 449 cancelHandler = () => {
494 450 if (cancel != null)
495 451 cancel();
496 452 medium.Cancel();
497 453 };
498 454 else
499 455 cancelHandler = medium.Cancel;
500 456
501 457 AddHandler(
502 458 resultHandler,
503 459 errorHandler,
504 460 cancelHandler,
505 461 null,
506 462 true
507 463 );
508 464
509 465 return medium;
510 466 }
511 467
512 468 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained, Func<Exception,IPromise<TNew>> error) {
513 469 return Chain(chained, error, null);
514 470 }
515 471
516 472 public IPromise<TNew> Chain<TNew>(Func<T, IPromise<TNew>> chained) {
517 473 return Chain(chained, null, null);
518 474 }
519 475
520 476 public IPromise<T> Cancelled(Action handler) {
521 477 var medium = new Promise<T>(this);
522 478 AddHandler(null, null, handler, medium, false);
523 479 return medium;
524 480 }
525 481
526 482 /// <summary>
527 483 /// Adds the specified handler for all cases (success, error, cancel)
528 484 /// </summary>
529 485 /// <param name="handler">The handler that will be called anyway</param>
530 486 /// <returns>self</returns>
531 487 public IPromise<T> Anyway(Action handler) {
532 488 Safe.ArgumentNotNull(handler, "handler");
533 489
534 490 var medium = new Promise<T>(this);
535 491
536 492 AddHandler(
537 x => handler(),
493 x => {
494 handler();
495 return x;
496 },
538 497 e => {
539 498 handler();
540 499 throw new TransientPromiseException(e);
541 500 },
542 501 handler,
543 502 medium,
544 503 true
545 504 );
546 505
547 506 return medium;
548 507 }
549 508
550 509 /// <summary>
551 510 /// Преобразует результат обещания к нужному типу
552 511 /// </summary>
553 512 /// <typeparam name="T2"></typeparam>
554 513 /// <returns></returns>
555 514 public IPromise<T2> Cast<T2>() {
556 515 return Then(x => (T2)(object)x, null);
557 516 }
558 517
559 518 /// <summary>
560 519 /// Дожидается отложенного обещания и в случае успеха, возвращает
561 520 /// его, результат, в противном случае бросает исключение.
562 521 /// </summary>
563 522 /// <remarks>
564 523 /// <para>
565 524 /// Если ожидание обещания было прервано по таймауту, это не значит,
566 525 /// что обещание было отменено или что-то в этом роде, это только
567 526 /// означает, что мы его не дождались, однако все зарегистрированные
568 527 /// обработчики, как были так остались и они будут вызваны, когда
569 528 /// обещание будет выполнено.
570 529 /// </para>
571 530 /// <para>
572 531 /// Такое поведение вполне оправдано поскольку таймаут может истечь
573 532 /// в тот момент, когда началась обработка цепочки обработчиков, и
574 533 /// к тому же текущее обещание может стоять в цепочке обещаний и его
575 534 /// отклонение может привести к непрогнозируемому результату.
576 535 /// </para>
577 536 /// </remarks>
578 537 /// <param name="timeout">Время ожидания</param>
579 538 /// <returns>Результат выполнения обещания</returns>
580 539 public T Join(int timeout) {
581 540 var evt = new ManualResetEvent(false);
582 541 Anyway(() => evt.Set());
583 542
584 543 if (!evt.WaitOne(timeout, true))
585 544 throw new TimeoutException();
586 545
587 546 switch (m_state) {
588 547 case SUCCEEDED_STATE:
589 548 return m_result;
590 549 case CANCELLED_STATE:
591 550 throw new OperationCanceledException();
592 551 case REJECTED_STATE:
593 552 throw new TargetInvocationException(m_error);
594 553 default:
595 554 throw new ApplicationException(String.Format("Invalid promise state {0}", m_state));
596 555 }
597 556 }
598 557
599 558 public T Join() {
600 559 return Join(Timeout.Infinite);
601 560 }
602 561
603 void AddHandler(Action<T> success, Func<Exception,T> error, Action cancel, Promise<T> medium, bool inc) {
562 void AddHandler<T2>(Func<T,T2> success, Func<Exception,T2> error, Action cancel, Promise<T2> medium, bool inc) {
604 563 if (inc)
605 564 Interlocked.Increment(ref m_childrenCount);
606 565
607 var handler = new HandlerDescriptor {
608 resultHandler = success,
609 errorHandler = error,
610 cancellHandler = cancel,
611 medium = medium
612 };
566 AbstractHandler handler = new HandlerDescriptor<T2>(success, error, cancel, medium);
613 567
614 568 bool queued;
615 569
616 570 if (!IsResolved) {
617 571 m_handlers.Enqueue(handler);
618 572 queued = true;
619 573 } else {
620 574 // the promise is in resolved state, just invoke the handled with minimum overhead
621 575 queued = false;
622 576 InvokeHandler(handler);
623 577 }
624 578
625 579 if (queued && IsResolved && m_handlers.TryDequeue(out handler))
626 580 // if the promise have been resolved while we was adding handler to the queue
627 581 // we can't guarantee that someone is still processing it
628 582 // therefore we will fetch a handler from the queue and execute it
629 583 // note that fetched handler may be not the one that we have added
630 584 // even we can fetch no handlers at all :)
631 585 InvokeHandler(handler);
632 586 }
633 587
634 protected virtual void InvokeHandler(HandlerDescriptor handler) {
588 protected virtual void InvokeHandler(AbstractHandler handler) {
635 589 switch (m_state) {
636 590 case SUCCEEDED_STATE:
637 591 handler.Resolve(m_result);
638 592 break;
639 593 case REJECTED_STATE:
640 594 handler.Reject(m_error);
641 595 break;
642 596 case CANCELLED_STATE:
643 597 handler.Cancel();
644 598 break;
645 599 default:
646 600 // do nothing
647 601 return;
648 602 }
649 603 }
650 604
651 605 void OnStateChanged() {
652 HandlerDescriptor handler;
606 AbstractHandler handler;
653 607 while (m_handlers.TryDequeue(out handler))
654 608 InvokeHandler(handler);
655 609 }
656 610
657 611 public bool IsExclusive {
658 612 get {
659 613 return m_childrenCount <= 1;
660 614 }
661 615 }
662 616
663 617 /// <summary>
664 618 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний.
665 619 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено.
666 620 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан.
667 621 /// </summary>
668 622 /// <param name="promises">Список обещаний. Если список пустой, то результирующее обещание возвращается уже выполненным.</param>
669 623 /// <returns>Обещание объединяющее в себе результат переданных обещаний.</returns>
670 624 /// <exception cref="ArgumentNullException"><paramref name="promises"/> не может быть null</exception>
671 625 public static IPromise<T[]> CreateComposite(IList<IPromise<T>> promises) {
672 626 if (promises == null)
673 627 throw new ArgumentNullException();
674 628
675 629 // создаем аккумулятор для результатов и результирующее обещание
676 630 var result = new T[promises.Count];
677 631 var promise = new Promise<T[]>();
678 632
679 633 // special case
680 634 if (promises.Count == 0) {
681 635 promise.Resolve(result);
682 636 return promise;
683 637 }
684 638
685 639 int pending = promises.Count;
686 640
687 641 for (int i = 0; i < promises.Count; i++) {
688 642 var dest = i;
689 643
690 644 if (promises[i] != null) {
691 promises[i].Then(
645 promises[i].On(
692 646 x => {
693 647 result[dest] = x;
694 648 if (Interlocked.Decrement(ref pending) == 0)
695 649 promise.Resolve(result);
696 650 },
697 e => {
698 promise.Reject(e);
699 return default(T);
700 }
651 promise.Reject
701 652 );
702 653 } else {
703 654 if (Interlocked.Decrement(ref pending) == 0)
704 655 promise.Resolve(result);
705 656 }
706 657 }
707 658
708 659 promise.Cancelled(
709 660 () => {
710 661 foreach (var d in promises)
711 662 if (d != null && d.IsExclusive)
712 663 d.Cancel();
713 664 }
714 665 );
715 666
716 667 return promise;
717 668 }
718 669
719 670 /// <summary>
720 671 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при
721 672 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний
722 673 /// игнорируются.
723 674 /// </summary>
724 675 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param>
725 676 /// <returns>Новое обещание, объединяющее в себе переданные.</returns>
726 677 /// <remarks>
727 678 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания.
728 679 /// </remarks>
729 680 public static IPromise CreateComposite(ICollection<IPromise> promises) {
730 681 if (promises == null)
731 682 throw new ArgumentNullException();
732 683 if (promises.Count == 0)
733 684 return Promise<object>.ResultToPromise(null);
734 685
735 686 int countdown = promises.Count;
736 687
737 688 var result = new Promise<object>();
738 689
739 690 foreach (var d in promises) {
740 691 if (d == null) {
741 692 if (Interlocked.Decrement(ref countdown) == 0)
742 693 result.Resolve(null);
743 694 } else {
744 695 d.Then(() => {
745 696 if (Interlocked.Decrement(ref countdown) == 0)
746 697 result.Resolve(null);
747 698 });
748 699 }
749 700 }
750 701
751 702 result.Cancelled(() => {
752 703 foreach (var d in promises)
753 704 if (d != null && d.IsExclusive)
754 705 d.Cancel();
755 706 });
756 707
757 708 return result;
758 709 }
759 710
760 711 public static Promise<T> ResultToPromise(T result) {
761 712 var p = new Promise<T>();
762 713 p.Resolve(result);
763 714 return p;
764 715 }
765 716
766 717 public static Promise<T> ExceptionToPromise(Exception error) {
767 718 if (error == null)
768 719 throw new ArgumentNullException();
769 720
770 721 var p = new Promise<T>();
771 722 p.Reject(error);
772 723 return p;
773 724 }
774 725
775 726 #region IPromiseBase explicit implementation
776 727
777 728 IPromise IPromise.Then(Action success, Action<Exception> error, Action cancel) {
778 729 return Then(
779 success != null ? new Action<T>(x => success()) : null,
730 success != null ? new Func<T,T>(x => {
731 success();
732 return x;
733 }) : null,
780 734 error != null ? new Func<Exception,T>(e => {
781 735 error(e);
782 736 return default(T);
783 737 }) : null,
784 738 cancel
785 739 );
786 740 }
787 741
788 742 IPromise IPromise.Then(Action success, Action<Exception> error) {
789 743 return Then(
790 success != null ? new Action<T>(x => success()) : null,
744 success != null ? new Func<T,T>(x => {
745 success();
746 return x;
747 }) : null,
791 748 error != null ? new Func<Exception,T>(e => {
792 749 error(e);
793 750 return default(T);
794 751 }) : null
795 752 );
796 753 }
797 754
798 755 IPromise IPromise.Then(Action success) {
799 756 Safe.ArgumentNotNull(success, "success");
800 return Then(x => success());
757 return Then(x => {
758 success();
759 return x;
760 });
801 761 }
802 762
803 763 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) {
804 764 return ChainNoResult(chained, error, cancel);
805 765 }
806 766
807 767 IPromise ChainNoResult(Func<IPromise> chained, Func<Exception,IPromise> error, Action cancel) {
808 768 Safe.ArgumentNotNull(chained, "chained");
809 769
810 770 var medium = new Promise<object>(this);
811 771
812 Action<T> resultHandler = delegate {
772 Func<T,T> resultHandler = delegate {
813 773 if (medium.IsCancelled)
814 return;
774 return default(T);
815 775
816 776 var promise = chained();
817 777
818 778 promise.On(
819 779 medium.Resolve,
820 780 medium.Reject,
821 781 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
822 782 );
823 783
824 784 // notify chained operation that it's not needed anymore
825 785 // порядок вызова Then, Cancelled важен, поскольку от этого
826 786 // зависит IsExclusive
827 787 medium.Cancelled(() => {
828 788 if (promise.IsExclusive)
829 789 promise.Cancel();
830 790 });
791
792 return default(T);
831 793 };
832 794
833 795 Func<Exception,T> errorHandler;
834 796
835 797 if (error != null)
836 798 errorHandler = delegate(Exception e) {
837 799 try {
838 800 var promise = error(e);
839 801
840 802 promise.On(
841 803 medium.Resolve,
842 804 medium.Reject,
843 805 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
844 806 );
845 807
846 808 // notify chained operation that it's not needed anymore
847 809 // порядок вызова Then, Cancelled важен, поскольку от этого
848 810 // зависит IsExclusive
849 811 medium.Cancelled(() => {
850 812 if (promise.IsExclusive)
851 813 promise.Cancel();
852 814 });
853 815 } catch (Exception e2) {
854 816 medium.Reject(e2);
855 817 }
856 818 return default(T);
857 819 };
858 820 else
859 821 errorHandler = err => {
860 822 medium.Reject(err);
861 823 return default(T);
862 824 };
863 825
864 826
865 827 Action cancelHandler;
866 828 if (cancel != null)
867 829 cancelHandler = () => {
868 830 if (cancel != null)
869 831 cancel();
870 832 medium.Cancel();
871 833 };
872 834 else
873 835 cancelHandler = medium.Cancel;
874 836
875 837 AddHandler(
876 838 resultHandler,
877 839 errorHandler,
878 840 cancelHandler,
879 841 null,
880 842 true
881 843 );
882 844
883 845 return medium;
884 846 }
885 847
886 848 IPromise IPromise.Chain(Func<IPromise> chained, Func<Exception,IPromise> error) {
887 849 return ChainNoResult(chained, error, null);
888 850 }
889 851
890 852 IPromise IPromise.Chain(Func<IPromise> chained) {
891 853 return ChainNoResult(chained, null, null);
892 854 }
893 855
894 856
895 857 void IPromise.On(Action success, Action<Exception> error, Action cancel) {
896 858 On(success != null ? new Action<T>(x => success()) : null, error, cancel);
897 859 }
898 860
899 861 void IPromise.On(Action success, Action<Exception> error) {
900 862 On(x => success(), error, null);
901 863 }
902 864
903 865 void IPromise.On(Action success) {
904 866 On(x => success(), null, null);
905 867 }
906 868
907 869 IPromise IPromise.Error(Action<Exception> error) {
908 870 return Error(error);
909 871 }
910 872
911 873 IPromise IPromise.Anyway(Action handler) {
912 874 return Anyway(handler);
913 875 }
914 876
915 877 IPromise IPromise.Cancelled(Action handler) {
916 878 return Cancelled(handler);
917 879 }
918 880
919 881 void IPromise.Join() {
920 882 Join();
921 883 }
922 884
923 885 void IPromise.Join(int timeout) {
924 886 Join(timeout);
925 887 }
926 888
927 889 #endregion
928 890
929 891
930 892
931 893 }
932 894 }
@@ -1,22 +1,22
1 1 using System.Threading;
2 2
3 3 namespace Implab {
4 4 public class SyncContextPromise<T> : Promise<T> {
5 5 readonly SynchronizationContext m_context;
6 6
7 7 public SyncContextPromise(SynchronizationContext context) {
8 8 Safe.ArgumentNotNull(context, "context");
9 9 m_context = context;
10 10 }
11 11
12 12 public SyncContextPromise(SynchronizationContext context, IPromise parent)
13 13 : base(parent) {
14 14 Safe.ArgumentNotNull(context, "context");
15 15 m_context = context;
16 16 }
17 protected override void InvokeHandler(HandlerDescriptor handler) {
17 protected override void InvokeHandler(AbstractHandler handler) {
18 18 m_context.Post(x => base.InvokeHandler(handler),null);
19 19 }
20 20 }
21 21 }
22 22
General Comments 0
You need to be logged in to leave comments. Login now