Mercurial > pub > ImplabNet
annotate Implab/PromiseT.cs @ 136:e9e7940c7d98 v2
shared locks + tests
author | cin |
---|---|
date | Mon, 16 Feb 2015 01:14:09 +0300 |
parents | 656815cb7147 |
children | f75cfa58e3d4 |
rev | line source |
---|---|
119 | 1 using System; |
2 using System.Diagnostics; | |
3 | |
4 namespace Implab { | |
5 | |
6 /// <summary> | |
7 /// Класс для асинхронного получения результатов. Так называемое "обещание". | |
8 /// </summary> | |
9 /// <typeparam name="T">Тип получаемого результата</typeparam> | |
10 /// <remarks> | |
11 /// <para>Сервис при обращении к его методу дает обещаиние о выполнении операции, | |
12 /// клиент получив такое обещание может установить ряд обратных вызово для получения | |
13 /// событий выполнения обещания, тоесть завершения операции и предоставлении результатов.</para> | |
14 /// <para> | |
15 /// Обещение может быть как выполнено, так и выполнено с ошибкой. Для подписки на | |
16 /// данные события клиент должен использовать методы <c>Then</c>. | |
17 /// </para> | |
18 /// <para> | |
19 /// Сервис, в свою очередь, по окончанию выполнения операции (возможно с ошибкой), | |
20 /// использует методы <c>Resolve</c> либо <c>Reject</c> для оповещения клиетна о | |
21 /// выполнении обещания. | |
22 /// </para> | |
23 /// <para> | |
24 /// Если сервер успел выполнить обещание еще до того, как клиент на него подписался, | |
25 /// то в момент подписки клиента будут вызваны соответсвующие события в синхронном | |
26 /// режиме и клиент будет оповещен в любом случае. Иначе, обработчики добавляются в | |
27 /// список в порядке подписания и в этом же порядке они будут вызваны при выполнении | |
28 /// обещания. | |
29 /// </para> | |
30 /// <para> | |
31 /// Обрабатывая результаты обещания можно преобразовывать результаты либо инициировать | |
32 /// связанные асинхронные операции, которые также возвращают обещания. Для этого следует | |
33 /// использовать соответствующую форму методе <c>Then</c>. | |
34 /// </para> | |
35 /// <para> | |
36 /// Также хорошим правилом является то, что <c>Resolve</c> и <c>Reject</c> должен вызывать | |
37 /// только инициатор обещания иначе могут возникнуть противоречия. | |
38 /// </para> | |
39 /// </remarks> | |
40 public class Promise<T> : AbstractPromise<IDeferred<T>>, IPromise<T>, IDeferred<T> { | |
41 | |
42 class StubDeferred : IDeferred<T> { | |
43 public static readonly StubDeferred instance = new StubDeferred(); | |
44 | |
45 StubDeferred() { | |
46 } | |
47 | |
48 #region IDeferred implementation | |
49 | |
50 public void Resolve(T value) { | |
51 } | |
52 | |
53 public void Reject(Exception error) { | |
54 } | |
55 | |
56 #endregion | |
57 | |
58 #region ICancellable implementation | |
59 | |
60 public void Cancel() { | |
61 } | |
62 | |
63 #endregion | |
64 | |
65 | |
66 } | |
67 | |
68 class RemapDescriptor<T2> : IDeferred<T> { | |
69 readonly Func<T,T2> m_remap; | |
70 readonly Func<Exception,T2> m_failed; | |
71 readonly Func<T2> m_cancel; | |
72 readonly IDeferred<T2> m_deferred; | |
73 | |
74 public RemapDescriptor(Func<T,T2> remap, Func<Exception,T2> failed, Func<T2> cancel, IDeferred<T2> deferred ) { | |
75 Debug.Assert(deferred != null); | |
76 m_remap = remap; | |
77 m_failed = failed; | |
78 m_cancel = cancel; | |
79 m_deferred = deferred; | |
80 } | |
81 | |
82 | |
83 | |
84 #region IDeferred implementation | |
85 | |
86 public void Resolve(T value) { | |
87 if (m_remap != null) { | |
88 try { | |
89 m_deferred.Resolve(m_remap(value)); | |
90 } catch (Exception ex) { | |
91 Reject(ex); | |
92 } | |
93 } | |
94 } | |
95 | |
96 public void Reject(Exception error) { | |
97 if (m_failed != null) { | |
98 try { | |
99 m_deferred.Resolve(m_failed(error)); | |
100 } catch (Exception ex) { | |
101 m_deferred.Reject(ex); | |
102 } | |
103 } else { | |
104 m_deferred.Reject(error); | |
105 } | |
106 } | |
107 | |
108 | |
109 #endregion | |
110 | |
111 #region ICancellable implementation | |
112 | |
113 public void Cancel() { | |
114 if (m_cancel != null) { | |
115 try { | |
116 m_deferred.Resolve(m_cancel()); | |
117 } catch (Exception ex) { | |
118 Reject(ex); | |
119 } | |
120 } else { | |
121 m_deferred.Cancel(); | |
122 } | |
123 } | |
124 | |
125 #endregion | |
126 } | |
127 | |
128 class ListenerDescriptor : IDeferred<T> { | |
129 readonly Action m_handler; | |
130 readonly PromiseEventType m_events; | |
131 | |
132 public ListenerDescriptor(Action handler, PromiseEventType events) { | |
133 Debug.Assert(handler != null); | |
134 | |
135 m_handler = handler; | |
136 m_events = events; | |
137 } | |
138 | |
139 #region IDeferred implementation | |
140 | |
141 public void Resolve(T value) { | |
142 if (m_events.HasFlag(PromiseEventType.Success)) { | |
143 try { | |
144 m_handler(); | |
145 // Analysis disable once EmptyGeneralCatchClause | |
146 } catch { | |
147 } | |
148 } | |
149 } | |
150 | |
151 public void Reject(Exception error) { | |
152 if (m_events.HasFlag(PromiseEventType.Error)){ | |
153 try { | |
154 m_handler(); | |
155 // Analysis disable once EmptyGeneralCatchClause | |
156 } catch { | |
157 } | |
158 } | |
159 } | |
160 | |
161 #endregion | |
162 | |
163 #region ICancellable implementation | |
164 | |
165 public void Cancel() { | |
166 if (m_events.HasFlag(PromiseEventType.Cancelled)){ | |
167 try { | |
168 m_handler(); | |
169 // Analysis disable once EmptyGeneralCatchClause | |
170 } catch { | |
171 } | |
172 } | |
173 } | |
174 | |
175 #endregion | |
176 } | |
177 | |
178 class ValueEventDescriptor : IDeferred<T> { | |
179 readonly Action<T> m_success; | |
180 readonly Action<Exception> m_failed; | |
181 readonly Action m_cancelled; | |
182 readonly IDeferred<T> m_deferred; | |
183 | |
184 public ValueEventDescriptor(Action<T> success, Action<Exception> failed, Action cancelled, IDeferred<T> deferred) { | |
185 Debug.Assert(deferred != null); | |
186 | |
187 m_success = success; | |
188 m_failed = failed; | |
189 m_cancelled = cancelled; | |
190 m_deferred = deferred; | |
191 } | |
192 | |
193 #region IDeferred implementation | |
194 | |
195 public void Resolve(T value) { | |
196 if (m_success != null) { | |
197 try { | |
198 m_success(value); | |
199 m_deferred.Resolve(value); | |
200 } catch (Exception ex) { | |
201 Reject(ex); | |
202 } | |
203 } | |
204 } | |
205 | |
206 public void Reject(Exception error) { | |
207 if (m_failed != null) { | |
208 try { | |
209 m_failed(error); | |
210 m_deferred.Resolve(default(T)); | |
211 } catch(Exception ex) { | |
212 m_deferred.Reject(ex); | |
213 } | |
214 } else { | |
215 m_deferred.Reject(error); | |
216 } | |
217 } | |
218 | |
219 #endregion | |
220 | |
221 #region ICancellable implementation | |
222 | |
223 public void Cancel() { | |
224 if (m_cancelled != null) { | |
225 try { | |
226 m_cancelled(); | |
227 m_deferred.Resolve(default(T)); | |
228 } catch(Exception ex) { | |
229 Reject(ex); | |
230 } | |
231 } else { | |
232 m_deferred.Cancel(); | |
233 } | |
234 } | |
235 | |
236 #endregion | |
237 } | |
238 | |
239 public class EventDescriptor : IDeferred<T> { | |
240 readonly Action m_success; | |
241 readonly Action<Exception> m_failed; | |
242 readonly Action m_cancelled; | |
243 readonly IDeferred<T> m_deferred; | |
244 | |
245 public EventDescriptor(Action success, Action<Exception> failed, Action cancelled, IDeferred<T> deferred) { | |
246 Debug.Assert(deferred != null); | |
247 | |
248 m_success = success; | |
249 m_failed = failed; | |
250 m_cancelled = cancelled; | |
251 m_deferred = deferred; | |
252 } | |
253 | |
254 #region IDeferred implementation | |
255 | |
256 public void Resolve(T value) { | |
257 if (m_success != null) { | |
258 try { | |
259 m_success(); | |
260 m_deferred.Resolve(value); | |
261 } catch (Exception ex) { | |
262 Reject(ex); | |
263 } | |
264 } | |
265 } | |
266 | |
267 public void Reject(Exception error) { | |
268 if (m_failed != null) { | |
269 try { | |
270 m_failed(error); | |
271 m_deferred.Resolve(default(T)); | |
272 }catch (Exception ex) | |
273 { | |
274 m_deferred.Reject(ex); | |
275 } | |
276 } else { | |
277 m_deferred.Reject(error); | |
278 } | |
279 | |
280 } | |
281 | |
282 #endregion | |
283 | |
284 #region ICancellable implementation | |
285 | |
286 public void Cancel() { | |
287 if (m_cancelled != null) { | |
288 try { | |
289 m_cancelled(); | |
290 m_deferred.Resolve(default(T)); | |
291 } catch (Exception ex) { | |
292 Reject(ex); | |
293 } | |
294 } else { | |
295 m_deferred.Cancel(); | |
296 } | |
297 } | |
298 | |
299 #endregion | |
300 } | |
301 | |
302 T m_result; | |
303 | |
304 public virtual void Resolve(T value) { | |
130
671f60cd0250
fixed Resove method bug when calling it on already cancelled promise
cin
parents:
119
diff
changeset
|
305 if (BeginSetResult()) { |
671f60cd0250
fixed Resove method bug when calling it on already cancelled promise
cin
parents:
119
diff
changeset
|
306 m_result = value; |
671f60cd0250
fixed Resove method bug when calling it on already cancelled promise
cin
parents:
119
diff
changeset
|
307 EndSetResult(); |
671f60cd0250
fixed Resove method bug when calling it on already cancelled promise
cin
parents:
119
diff
changeset
|
308 } |
119 | 309 } |
310 | |
311 public void Reject(Exception error) { | |
312 SetError(error); | |
313 } | |
314 | |
315 public Type PromiseType { | |
316 get { | |
317 return typeof(T); | |
318 } | |
319 } | |
320 | |
321 public new T Join() { | |
322 WaitResult(-1); | |
323 return m_result; | |
324 } | |
325 public new T Join(int timeout) { | |
326 WaitResult(timeout); | |
327 return m_result; | |
328 } | |
329 | |
330 public IPromise<T> On(Action<T> success, Action<Exception> error, Action cancel) { | |
331 AddHandler(new ValueEventDescriptor(success, error, cancel, StubDeferred.instance)); | |
332 return this; | |
333 } | |
334 | |
335 public IPromise<T> On(Action<T> success, Action<Exception> error) { | |
336 AddHandler(new ValueEventDescriptor(success, error, null, StubDeferred.instance)); | |
337 return this; | |
338 } | |
339 | |
340 public IPromise<T> On(Action<T> success) { | |
341 AddHandler(new ValueEventDescriptor(success, null, null, StubDeferred.instance)); | |
342 return this; | |
343 } | |
344 | |
345 public IPromise<T> On(Action handler, PromiseEventType events) { | |
346 Listen(events, handler); | |
347 return this; | |
348 } | |
349 | |
350 public IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception, T2> error, Func<T2> cancel) { | |
351 var promise = new Promise<T2>(); | |
135 | 352 if (mapper != null) |
353 promise.On(Cancel, PromiseEventType.Cancelled); | |
119 | 354 AddHandler(new RemapDescriptor<T2>(mapper, error, cancel, promise)); |
355 return promise; | |
356 } | |
357 | |
358 public IPromise<T2> Then<T2>(Func<T, T2> mapper, Func<Exception, T2> error) { | |
359 var promise = new Promise<T2>(); | |
135 | 360 if (mapper != null) |
361 promise.On(Cancel, PromiseEventType.Cancelled); | |
119 | 362 AddHandler(new RemapDescriptor<T2>(mapper, error, null, promise)); |
363 return promise; | |
364 } | |
365 | |
366 public IPromise<T2> Then<T2>(Func<T, T2> mapper) { | |
367 var promise = new Promise<T2>(); | |
135 | 368 if (mapper != null) |
369 promise.On(Cancel, PromiseEventType.Cancelled); | |
119 | 370 AddHandler(new RemapDescriptor<T2>(mapper, null, null, promise)); |
371 return promise; | |
372 } | |
373 | |
374 public IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception, IPromise<T2>> error, Func<IPromise<T2>> cancel) { | |
375 // this promise will be resolved when an asyc operation is started | |
376 var promise = new Promise<IPromise<T2>>(); | |
377 | |
378 AddHandler(new RemapDescriptor<IPromise<T2>>( | |
379 chained, | |
380 error, | |
381 cancel, | |
382 promise | |
383 )); | |
384 | |
385 var medium = new Promise<T2>(); | |
386 | |
387 if (chained != null) | |
388 medium.On(Cancel, PromiseEventType.Cancelled); | |
389 | |
390 // we need to connect started async operation with the medium | |
391 // if the async operation hasn't been started by the some reason | |
392 // report is to the medium | |
393 promise.On( | |
394 result => ConnectPromise<T2>(result, medium), | |
395 medium.Reject, | |
396 medium.Cancel | |
397 ); | |
398 | |
399 return medium; | |
400 } | |
401 | |
402 static void ConnectPromise<T2>(IPromise<T2> result, Promise<T2> medium) { | |
403 if (result != null) { | |
404 result.On( | |
405 medium.Resolve, | |
406 medium.Reject, | |
407 () => medium.Reject(new OperationCanceledException()) | |
408 ); | |
409 medium.On(result.Cancel, PromiseEventType.Cancelled); | |
410 } else { | |
411 medium.Reject( | |
412 new NullReferenceException( | |
413 "The chained asynchronous operation returned" + | |
414 " 'null' where the promise instance is expected" | |
415 ) | |
416 ); | |
417 } | |
418 } | |
419 | |
420 public IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained, Func<Exception, IPromise<T2>> error) { | |
421 return Chain(chained, error, null); | |
422 } | |
423 | |
424 public IPromise<T2> Chain<T2>(Func<T, IPromise<T2>> chained) { | |
425 return Chain(chained, null, null); | |
426 } | |
427 | |
428 public IPromise<T2> Error<T2>(Func<Exception, T2> error) { | |
429 var promise = new Promise<T2>(); | |
430 if (error != null) | |
431 On( | |
432 (Action<T>)null, | |
433 ex => { | |
434 try { | |
435 promise.Resolve(error(ex)); | |
436 } catch (Exception ex2) { | |
437 promise.Reject(ex2); | |
438 } | |
439 } | |
440 ); | |
441 else | |
442 Listen(PromiseEventType.Error, () => promise.Resolve(default(T2))); | |
443 return promise; | |
444 } | |
445 | |
446 public IPromise<T2> Cancelled<T2>(Func<T2> handler) { | |
447 var promise = new Promise<T2>(); | |
448 if (handler != null) | |
449 On( | |
450 (Action<T>)null, | |
451 null, | |
452 () => { | |
453 try { | |
454 promise.Resolve(handler()); | |
455 } catch (Exception ex) { | |
456 promise.Reject(ex); | |
457 } | |
458 }); | |
459 else | |
460 Listen(PromiseEventType.Cancelled, () => promise.Resolve(default(T2))); | |
461 return promise; | |
462 } | |
463 | |
464 public IPromise Then(Action success, Action<Exception> error, Action cancel) { | |
465 var promise = new Promise<T>(); | |
466 if (success != null) | |
467 promise.On(Cancel, PromiseEventType.Cancelled); | |
468 | |
469 AddHandler(new EventDescriptor(success, error, cancel, promise)); | |
470 | |
471 return promise; | |
472 } | |
473 | |
474 public IPromise Then(Action success, Action<Exception> error) { | |
475 return Then(success, error, null); | |
476 } | |
477 | |
478 public IPromise Then(Action success) { | |
479 return Then(success, null, null); | |
480 } | |
481 | |
482 public IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error, Func<IPromise> cancel) { | |
483 var promise = new Promise<IPromise>(); | |
484 | |
485 AddHandler( | |
486 new RemapDescriptor<IPromise>( | |
487 x => chained(), | |
488 error, | |
489 cancel, | |
490 promise | |
491 ) | |
492 ); | |
493 | |
494 var medium = new Promise(); | |
495 if (chained != null) | |
496 medium.On(Cancel, PromiseEventType.Cancelled); | |
497 | |
498 promise.On( | |
499 result => ConnectPromise(result, medium), | |
500 medium.Reject, | |
501 medium.Cancel | |
502 ); | |
503 | |
504 return medium; | |
505 } | |
506 | |
507 static void ConnectPromise(IPromise result, Promise medium) { | |
508 if (result != null) { | |
509 result.On( | |
510 medium.Resolve, | |
511 medium.Reject, | |
512 () => medium.Reject(new OperationCanceledException()) | |
513 ); | |
514 medium.On(result.Cancel, PromiseEventType.Cancelled); | |
515 } else { | |
516 medium.Reject( | |
517 new NullReferenceException( | |
518 "The chained asynchronous operation returned" + | |
519 " 'null' where the promise instance is expected" | |
520 ) | |
521 ); | |
522 } | |
523 } | |
524 | |
525 public IPromise Chain(Func<IPromise> chained, Func<Exception, IPromise> error) { | |
526 return Chain(chained, error, null); | |
527 } | |
528 | |
529 public IPromise Chain(Func<IPromise> chained) { | |
530 return Chain(chained, null, null); | |
531 } | |
532 | |
533 public IPromise On(Action success, Action<Exception> error, Action cancel) { | |
534 AddHandler(new EventDescriptor(success,error,cancel, StubDeferred.instance)); | |
535 return this; | |
536 } | |
537 | |
538 public IPromise On(Action success, Action<Exception> error) { | |
539 AddHandler(new EventDescriptor(success, error, null, StubDeferred.instance)); | |
540 return this; | |
541 } | |
542 | |
543 public IPromise On(Action success) { | |
544 Listen(PromiseEventType.Success, success); | |
545 return this; | |
546 } | |
547 | |
548 IPromise IPromise.On(Action handler, PromiseEventType events) { | |
549 Listen(events,handler); | |
550 return this; | |
551 } | |
552 | |
553 public IPromise Error(Action<Exception> error) { | |
554 var promise = new Promise(); | |
555 if (error != null) | |
556 On( | |
557 (Action<T>)null, | |
558 ex => { | |
559 try { | |
560 error(ex); | |
561 promise.Resolve(); | |
562 } catch (Exception ex2) { | |
563 promise.Reject(ex2); | |
564 } | |
565 }); | |
566 else | |
567 Listen(PromiseEventType.Error, promise.Resolve); | |
568 return promise; | |
569 } | |
570 | |
571 public IPromise Cancelled(Action handler) { | |
572 var promise = new Promise(); | |
573 if (handler != null) | |
574 On( | |
575 (Action<T>)null, | |
576 null, | |
577 () => { | |
578 try { | |
579 handler(); | |
580 promise.Resolve(); | |
581 } catch (Exception ex) { | |
582 promise.Reject(ex); | |
583 } | |
584 }); | |
585 else | |
586 Listen(PromiseEventType.Cancelled, promise.Resolve); | |
587 return promise; | |
588 } | |
589 | |
590 public IPromise<T2> Cast<T2>() { | |
591 return (IPromise<T2>)this; | |
592 } | |
593 | |
594 #region implemented abstract members of AbstractPromise | |
595 | |
596 protected override void SignalSuccess(IDeferred<T> handler) { | |
597 handler.Resolve(m_result); | |
598 } | |
599 | |
600 protected override void SignalError(IDeferred<T> handler, Exception error) { | |
601 handler.Reject(error); | |
602 } | |
603 | |
604 protected override void SignalCancelled(IDeferred<T> handler) { | |
605 handler.Cancel(); | |
606 } | |
607 | |
608 protected override void Listen(PromiseEventType events, Action handler) { | |
609 if (handler != null) | |
610 AddHandler(new ListenerDescriptor(handler, events)); | |
611 } | |
612 | |
613 #endregion | |
614 | |
615 public static IPromise<T> ResultToPromise(T value) { | |
616 var p = new Promise<T>(); | |
617 p.Resolve(value); | |
618 return p; | |
619 } | |
620 | |
621 public static IPromise<T> ExceptionToPromise(Exception error) { | |
622 var p = new Promise<T>(); | |
623 p.Reject(error); | |
624 return p; | |
625 } | |
626 | |
627 } | |
628 } |