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 }