Mercurial > pub > ImplabNet
comparison Implab/Promise.cs @ 33:b255e4aeef17
removed the reference to the parent from the promise object this allows
resolved promises to release parents and results they are holding.
Added complete set of operations to IPromiseBase interface
Subscribing to the cancellation event of the promise should not affect it's
IsExclusive property
More tests.
author | cin |
---|---|
date | Thu, 10 Apr 2014 02:39:29 +0400 |
parents | 8eca2652d2ff |
children | 653c4e04968b |
comparison
equal
deleted
inserted
replaced
32:8eca2652d2ff | 33:b255e4aeef17 |
---|---|
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 IPromise<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> |
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; | |
90 readonly bool m_cancellable; | 89 readonly bool m_cancellable; |
91 | 90 |
92 int m_childrenCount = 0; | 91 int m_childrenCount = 0; |
93 int m_state; | 92 int m_state; |
94 T m_result; | 93 T m_result; |
100 m_cancellable = true; | 99 m_cancellable = true; |
101 } | 100 } |
102 | 101 |
103 public Promise(IPromiseBase parent, bool cancellable) { | 102 public Promise(IPromiseBase parent, bool cancellable) { |
104 m_cancellable = cancellable; | 103 m_cancellable = cancellable; |
105 m_parent = parent; | 104 if (parent != null) |
106 } | 105 AddHandler( |
107 | 106 null, |
108 void InternalCancel() { | 107 null, |
109 // don't try to cancel parent :) | 108 () => { |
110 Cancel(false); | 109 if (parent.IsExclusive) |
110 parent.Cancel(); | |
111 } | |
112 ); | |
111 } | 113 } |
112 | 114 |
113 bool BeginTransit() { | 115 bool BeginTransit() { |
114 return UnresolvedSate == Interlocked.CompareExchange(ref m_state, TransitionalState, UnresolvedSate); | 116 return UnresolvedSate == Interlocked.CompareExchange(ref m_state, TransitionalState, UnresolvedSate); |
115 } | 117 } |
154 } else { | 156 } else { |
155 WaitTransition(); | 157 WaitTransition(); |
156 if (m_state != CancelledState) | 158 if (m_state != CancelledState) |
157 throw new InvalidOperationException("The promise is already resolved"); | 159 throw new InvalidOperationException("The promise is already resolved"); |
158 } | 160 } |
161 } | |
162 | |
163 /// <summary> | |
164 /// Выполняет обещание, сообщая об успешном выполнении. Результатом выполнения будет пустое значения. | |
165 /// </summary> | |
166 /// <remarks> | |
167 /// Данный вариант удобен в случаях, когда интересен факт выполнения операции, нежели полученное значение. | |
168 /// </remarks> | |
169 public void Resolve() { | |
170 Resolve(default(T)); | |
159 } | 171 } |
160 | 172 |
161 /// <summary> | 173 /// <summary> |
162 /// Выполняет обещание, сообщая об ошибке | 174 /// Выполняет обещание, сообщая об ошибке |
163 /// </summary> | 175 /// </summary> |
183 /// <summary> | 195 /// <summary> |
184 /// Отменяет операцию, если это возможно. | 196 /// Отменяет операцию, если это возможно. |
185 /// </summary> | 197 /// </summary> |
186 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> | 198 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns> |
187 public bool Cancel() { | 199 public bool Cancel() { |
188 return Cancel(true); | 200 if (BeginTransit()) { |
201 CompleteTransit(CancelledState); | |
202 OnStateChanged(); | |
203 return true; | |
204 } else { | |
205 return false; | |
206 } | |
207 } | |
208 | |
209 // сделано для возвращаемого типа void | |
210 protected void InternalCancel() { | |
211 Cancel(); | |
189 } | 212 } |
190 | 213 |
191 /// <summary> | 214 /// <summary> |
192 /// Adds new handlers to this promise. | 215 /// Adds new handlers to this promise. |
193 /// </summary> | 216 /// </summary> |
227 AddHandler(resultHandler, errorHandler, medium.InternalCancel); | 250 AddHandler(resultHandler, errorHandler, medium.InternalCancel); |
228 | 251 |
229 return medium; | 252 return medium; |
230 } | 253 } |
231 | 254 |
232 public IPromiseBase Then(Action success,ErrorHandler error) | 255 public IPromiseBase Then(Action success, ErrorHandler error) { |
233 { | |
234 return Then(x => success(), error); | 256 return Then(x => success(), error); |
235 } | 257 } |
236 | 258 |
237 public IPromiseBase Then(Action success) | 259 public IPromiseBase Then(Action success) { |
238 { | |
239 return Then(x => success()); | 260 return Then(x => success()); |
240 } | 261 } |
241 | 262 |
242 /// <summary> | 263 /// <summary> |
243 /// Adds new handlers to this promise. | 264 /// Adds new handlers to this promise. |
265 | 286 |
266 if (error != null) | 287 if (error != null) |
267 errorHandler = x => { | 288 errorHandler = x => { |
268 try { | 289 try { |
269 medium.Resolve(error(x)); | 290 medium.Resolve(error(x)); |
270 } catch(Exception e) { | 291 } catch (Exception e) { |
271 medium.Reject(e); | 292 medium.Reject(e); |
272 } | 293 } |
273 }; | 294 }; |
274 else | 295 else |
275 errorHandler = medium.Reject; | 296 errorHandler = medium.Reject; |
336 | 357 |
337 public IPromise<T> Anyway(Action handler) { | 358 public IPromise<T> Anyway(Action handler) { |
338 if (handler == null) | 359 if (handler == null) |
339 return this; | 360 return this; |
340 | 361 |
341 var medium = new Promise<T>(); | 362 var medium = new Promise<T>(this,true); |
342 | 363 |
343 AddHandler( | 364 AddHandler( |
344 x => { | 365 x => { |
345 // to avoid handler being called multiple times we handle exception by ourselfs | 366 // to avoid handler being called multiple times we handle exception by ourselfs |
346 try { | 367 try { |
375 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) { | 396 public IPromise<TNew> Map<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler error) { |
376 if (mapper == null) | 397 if (mapper == null) |
377 throw new ArgumentNullException("mapper"); | 398 throw new ArgumentNullException("mapper"); |
378 | 399 |
379 // создаем прицепленное обещание | 400 // создаем прицепленное обещание |
380 var chained = new Promise<TNew>(); | 401 var chained = new Promise<TNew>(this,true); |
381 | 402 |
382 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result)); | 403 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result)); |
383 ErrorHandler errorHandler = delegate(Exception e) { | 404 ErrorHandler errorHandler = delegate(Exception e) { |
384 if (error != null) | 405 if (error != null) |
385 try { | 406 try { |
425 if (medium.IsCancelled) | 446 if (medium.IsCancelled) |
426 return; | 447 return; |
427 | 448 |
428 var promise = chained(result); | 449 var promise = chained(result); |
429 | 450 |
430 // notify chained operation that it's not needed | |
431 medium.Cancelled(() => promise.Cancel()); | |
432 promise.Then( | 451 promise.Then( |
433 x => medium.Resolve(x), | 452 x => medium.Resolve(x), |
434 e => medium.Reject(e) | 453 e => medium.Reject(e) |
435 ); | 454 ); |
455 | |
456 // notify chained operation that it's not needed anymore | |
457 // порядок вызова Then, Cancelled важен, поскольку от этого | |
458 // зависит IsExclusive | |
459 medium.Cancelled(() => { | |
460 if(promise.IsExclusive) | |
461 promise.Cancel(); | |
462 }); | |
463 | |
464 // внешняя отмена связанной операции рассматривается как ошибка | |
465 promise.Cancelled(() => medium.Reject(new OperationCanceledException())); | |
436 }; | 466 }; |
437 | 467 |
438 ErrorHandler errorHandler = delegate(Exception e) { | 468 ErrorHandler errorHandler = delegate(Exception e) { |
439 if (error != null) | 469 if (error != null) |
440 error(e); | 470 error(e); |
529 public T Join() { | 559 public T Join() { |
530 return Join(Timeout.Infinite); | 560 return Join(Timeout.Infinite); |
531 } | 561 } |
532 | 562 |
533 void AddHandler(ResultHandler<T> success, ErrorHandler error, Action cancel) { | 563 void AddHandler(ResultHandler<T> success, ErrorHandler error, Action cancel) { |
534 Interlocked.Increment(ref m_childrenCount); | 564 if (success != null || error != null) |
565 Interlocked.Increment(ref m_childrenCount); | |
535 | 566 |
536 HandlerDescriptor handler = new HandlerDescriptor { | 567 HandlerDescriptor handler = new HandlerDescriptor { |
537 resultHandler = success, | 568 resultHandler = success, |
538 errorHandler = error, | 569 errorHandler = error, |
539 cancellHandler = cancel | 570 cancellHandler = cancel |
586 get { | 617 get { |
587 return m_childrenCount <= 1; | 618 return m_childrenCount <= 1; |
588 } | 619 } |
589 } | 620 } |
590 | 621 |
591 protected bool Cancel(bool dependencies) { | |
592 if (BeginTransit()) { | |
593 CompleteTransit(CancelledState); | |
594 OnStateChanged(); | |
595 | |
596 if (dependencies && m_parent != null && m_parent.IsExclusive) | |
597 m_parent.Cancel(); | |
598 | |
599 return true; | |
600 } else { | |
601 return false; | |
602 } | |
603 } | |
604 | |
605 /// <summary> | 622 /// <summary> |
606 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний. | 623 /// Объединяет несколько обещаний в одно, результатом которого является массив результатов других обещаний. |
607 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено. | 624 /// Если хотябы одно из переданных обещаний не будет выполнено, то новое обещение тоже не будет выполнено. |
608 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан. | 625 /// При отмене нового обещания, переданные обещания также будут отменены, если никто больше на них не подписан. |
609 /// </summary> | 626 /// </summary> |
627 int pending = promises.Count; | 644 int pending = promises.Count; |
628 | 645 |
629 for (int i = 0; i < promises.Count; i++) { | 646 for (int i = 0; i < promises.Count; i++) { |
630 var dest = i; | 647 var dest = i; |
631 | 648 |
632 promises[i].Then( | 649 if (promises[i] != null) { |
633 x => { | 650 promises[i].Then( |
634 result[dest] = x; | 651 x => { |
635 if(Interlocked.Decrement(ref pending) == 0) | 652 result[dest] = x; |
636 promise.Resolve(result); | 653 if (Interlocked.Decrement(ref pending) == 0) |
637 }, | 654 promise.Resolve(result); |
638 e => promise.Reject(e) | 655 }, |
639 ); | 656 e => promise.Reject(e) |
657 ); | |
658 } else { | |
659 if (Interlocked.Decrement(ref pending) == 0) | |
660 promise.Resolve(result); | |
661 } | |
640 } | 662 } |
641 | 663 |
642 promise.Cancelled( | 664 promise.Cancelled( |
643 () => { | 665 () => { |
644 foreach(var d in promises) | 666 foreach (var d in promises) |
645 if(d.IsExclusive) | 667 if (d != null && d.IsExclusive) |
646 d.Cancel(); | 668 d.Cancel(); |
647 } | 669 } |
648 ); | 670 ); |
649 | 671 |
650 return promise; | 672 return promise; |
673 } | |
674 | |
675 /// <summary> | |
676 /// Объединяет несколько обещаний в одно. Результирующее обещание будет выполнено при | |
677 /// выполнении всех указанных обещаний. При этом возвращаемые значения первичных обещаний | |
678 /// игнорируются. | |
679 /// </summary> | |
680 /// <param name="promises">Коллекция первичных обещаний, которые будут объеденены в одно.</param> | |
681 /// <returns>Новое обещание, объединяющее в себе переданные.</returns> | |
682 /// <remarks> | |
683 /// Если в коллекции встречаюься <c>null</c>, то они воспринимаются как выполненные обещания. | |
684 /// </remarks> | |
685 public static IPromiseBase CreateComposite(ICollection<IPromiseBase> promises) { | |
686 if (promises == null) | |
687 throw new ArgumentNullException(); | |
688 if (promises.Count == 0) | |
689 return Promise<object>.ResultToPromise(null); | |
690 | |
691 int countdown = promises.Count; | |
692 | |
693 var result = new Promise<object>(); | |
694 | |
695 foreach (var d in promises) { | |
696 if (d == null) { | |
697 if (Interlocked.Decrement(ref countdown) == 0) | |
698 result.Resolve(null); | |
699 } else { | |
700 d.Then(() => { | |
701 if (Interlocked.Decrement(ref countdown) == 0) | |
702 result.Resolve(null); | |
703 }); | |
704 } | |
705 } | |
706 | |
707 result.Cancelled(() => { | |
708 foreach (var d in promises) | |
709 if (d != null && d.IsExclusive) | |
710 d.Cancel(); | |
711 }); | |
712 | |
713 return result; | |
651 } | 714 } |
652 | 715 |
653 public static Promise<T> ResultToPromise(T result) { | 716 public static Promise<T> ResultToPromise(T result) { |
654 var p = new Promise<T>(); | 717 var p = new Promise<T>(); |
655 p.Resolve(result); | 718 p.Resolve(result); |
663 var p = new Promise<T>(); | 726 var p = new Promise<T>(); |
664 p.Reject(error); | 727 p.Reject(error); |
665 return p; | 728 return p; |
666 } | 729 } |
667 | 730 |
731 #region IPromiseBase explicit implementation | |
732 | |
733 IPromiseBase IPromiseBase.Error(ErrorHandler error) { | |
734 return Error(error); | |
735 } | |
736 | |
737 IPromiseBase IPromiseBase.Anyway(Action handler) { | |
738 return Anyway(handler); | |
739 } | |
740 | |
741 IPromiseBase IPromiseBase.Finally(Action handler) { | |
742 return Finally(handler); | |
743 } | |
744 | |
745 IPromiseBase IPromiseBase.Cancelled(Action handler) { | |
746 return Cancelled(handler); | |
747 } | |
748 | |
749 void IPromiseBase.Join() { | |
750 Join(); | |
751 } | |
752 | |
753 void IPromiseBase.Join(int timeout) { | |
754 Join(timeout); | |
755 } | |
756 | |
757 #endregion | |
758 | |
759 | |
760 | |
668 } | 761 } |
669 } | 762 } |