##// END OF EJS Templates
Refactoring of the IPromise<T> interface...
cin -
r76:c761fc982e1d v2
parent child
Show More
@@ -17,71 +17,25 namespace Implab.Fx
17 17 /// <example>
18 18 /// client
19 19 /// .Get("description.txt") // returns a promise
20 /// .DirectToControl(m_ctl) // handle the promise in the thread of the control
20 /// .DispatchToControl(m_ctl) // handle the promise in the thread of the control
21 21 /// .Then(
22 22 /// description => m_ctl.Text = description // now it's safe
23 23 /// )
24 24 /// </example>
25 public static Promise<T> DispatchToControl<T>(this Promise<T> that, Control ctl)
25 public static IPromise<T> DispatchToControl<T>(this IPromise<T> that, Control ctl)
26 26 {
27 if (that == null)
28 throw new ArgumentNullException("that");
29 if (ctl == null)
30 throw new ArgumentNullException("ctl");
27 Safe.ArgumentNotNull(that, "that");
28 Safe.ArgumentNotNull(ctl, "ctl");
31 29
32 30 var directed = new ControlBoundPromise<T>(ctl,that,true);
33 31
34 that.Then(
32 that.Last(
35 33 directed.Resolve,
36 err =>
37 {
38 directed.Reject(err);
39 return default(T);
40 }
34 directed.Reject,
35 directed.Cancel
41 36 );
42 37
43 38 return directed;
44 39 }
45
46 /// <summary>
47 /// Направляет обработку обещания в текущий поток, если у него существует контекст синхронизации.
48 /// </summary>
49 /// <typeparam name="T">Тип результата обещания.</typeparam>
50 /// <param name="that">Обещание которое нужно обработать в текущем потоке.</param>
51 /// <returns>Перенаправленное обещание.</returns>
52 public static Promise<T> DispatchToCurrentThread<T>(this Promise<T> that)
53 {
54 var sync = SynchronizationContext.Current;
55 if (sync == null)
56 throw new InvalidOperationException("The current thread doesn't have a syncronization context");
57 return DispatchToSyncContext(that, sync);
58 }
59
60 /// <summary>
61 /// Направляет обработку обещания в указанный контекст синхронизации.
62 /// </summary>
63 /// <typeparam name="T">Тип результата обещания.</typeparam>
64 /// <param name="that">Обещание, которое требуется обработать в указанном контексте синхронизации.</param>
65 /// <param name="sync">Контекст синхронизации в который будет направлено обещание.</param>
66 /// <returns>Новое обещание, которое будет обрабатываться в указанном контексте.</returns>
67 public static Promise<T> DispatchToSyncContext<T>(this Promise<T> that, SynchronizationContext sync)
68 {
69 if (that == null)
70 throw new ArgumentNullException("that");
71 if (sync == null)
72 throw new ArgumentNullException("sync");
73
74 var d = new Promise<T>();
75
76 that.Then(
77 res => sync.Post(state => d.Resolve(res), null),
78 err => {
79 sync.Post(state => d.Reject(err), null);
80 return default(T);
81 }
82 );
83
84 return d;
85 40 }
86 41 }
87 }
@@ -32,5 +32,4 using System.Runtime.InteropServices;
32 32 // You can specify all the values or you can default the Build and Revision Numbers
33 33 // by using the '*' as shown below:
34 34 // [assembly: AssemblyVersion("1.0.*")]
35 [assembly: AssemblyVersion("1.0.0.0")]
36 [assembly: AssemblyFileVersion("1.0.0.0")]
35 [assembly: AssemblyVersion("2.0.*")]
@@ -38,6 +38,38 namespace Implab.Test {
38 38 }
39 39
40 40 [TestMethod]
41 public void CancelExceptionTest() {
42 var p = new Promise<bool>();
43 p.Cancel();
44
45 var p2 = p.Cancelled(() => {
46 throw new ApplicationException("CANCELLED");
47 });
48
49 try {
50 p2.Join();
51 Assert.Fail();
52 } catch (ApplicationException err) {
53 Assert.AreEqual("CANCELLED", err.InnerException.Message);
54 }
55
56 }
57
58 [TestMethod]
59 public void ContinueOnCancelTest() {
60 var p = new Promise<bool>();
61 p.Cancel();
62
63 var p2 = p
64 .Cancelled(() => {
65 throw new ApplicationException("CANCELLED");
66 })
67 .Error(e => true);
68
69 Assert.AreEqual(true, p2.Join());
70 }
71
72 [TestMethod]
41 73 public void JoinSuccessTest() {
42 74 var p = new Promise<int>();
43 75 p.Resolve(100);
@@ -63,7 +95,7 namespace Implab.Test {
63 95 public void MapTest() {
64 96 var p = new Promise<int>();
65 97
66 var p2 = p.Map(x => x.ToString());
98 var p2 = p.Then(x => x.ToString());
67 99 p.Resolve(100);
68 100
69 101 Assert.AreEqual(p2.Join(), "100");
@@ -185,8 +217,8 namespace Implab.Test {
185 217 var stop = new ManualResetEvent(false);
186 218 int total = 0;
187 219
188 int itemsPerWriter = 1000;
189 int writersCount = 3;
220 int itemsPerWriter = 10000;
221 int writersCount = 10;
190 222
191 223 for (int i = 0; i < writersCount; i++) {
192 224 Interlocked.Increment(ref writers);
@@ -318,7 +350,7 namespace Implab.Test {
318 350 .Chain(x =>
319 351 PromiseHelper
320 352 .Sleep(200, "Hi, " + x)
321 .Map(y => y)
353 .Then(y => y)
322 354 .Cancelled(() => flags[1] = true)
323 355 )
324 356 .Cancelled(() => flags[2] = true);
@@ -341,7 +373,7 namespace Implab.Test {
341 373 // завершаться ошибкой OperationCanceledException
342 374 var p = PromiseHelper
343 375 .Sleep(1, "Hi, HAL!")
344 .Chain(x => {
376 .Then(x => {
345 377 // запускаем две асинхронные операции
346 378 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
347 379 // вторая операция отменяет первую до завершения
@@ -360,16 +392,15 namespace Implab.Test {
360 392 [TestMethod]
361 393 public void ChainedCancel2Test() {
362 394 // при отмене цепочки обещаний, вложенные операции также должны отменяться
363 IPromise p = null;
364 395 var pSurvive = new Promise<bool>();
365 396 var hemStarted = new ManualResetEvent(false);
366 p = PromiseHelper
397 var p = PromiseHelper
367 398 .Sleep(1, "Hi, HAL!")
368 399 .Chain(x => {
369 400 hemStarted.Set();
370 401 // запускаем две асинхронные операции
371 402 var result = PromiseHelper
372 .Sleep(1000, "HEM ENABLED!!!")
403 .Sleep(10000, "HEM ENABLED!!!")
373 404 .Then(s => pSurvive.Resolve(false));
374 405
375 406 result
@@ -215,7 +215,7 namespace Implab.Diagnostics {
215 215 Safe.ArgumentNotNull(promise, "promise");
216 216
217 217 var ctx = DetachLogicalOperation();
218 promise.Finally(() => {
218 promise.Anyway(() => {
219 219 var old = _current;
220 220 TraceContext.Attach(ctx);
221 221 TraceContext.Current.EndLogicalOperation();
@@ -5,6 +5,6 using System.Text;
5 5
6 6 namespace Implab {
7 7 public interface ICancellable {
8 bool Cancel();
8 void Cancel();
9 9 }
10 10 }
@@ -52,7 +52,7 namespace Implab {
52 52 /// </summary>
53 53 /// <param name="handler">Обработчик.</param>
54 54 /// <remarks>После обработке ошибки, она передается дальше.</remarks>
55 IPromise Finally(Action handler);
55 IPromise Anyway(Action handler);
56 56 /// <summary>
57 57 /// Обработчик для регистрации отмены обещания, событие отмены не может быть подавлено.
58 58 /// </summary>
@@ -10,28 +10,34 namespace Implab {
10 10
11 11 new T Join(int timeout);
12 12
13 void Last(ResultHandler<T> success, ErrorHandler error, Action cancel);
14
15 void Last(ResultHandler<T> success, ErrorHandler error);
16
17 void Last(ResultHandler<T> success);
18
13 19 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel);
14 20
15 21 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
16 22
17 23 IPromise<T> Then(ResultHandler<T> success);
18 24
19 void Last(ResultHandler<T> success, ErrorHandler error, Action cancel);
20 void Last(ResultHandler<T> success, ErrorHandler error);
21 void Last(ResultHandler<T> success);
25 IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error, Action cancel);
22 26
23 IPromise<T> Error(ErrorHandler<T> error);
24
25 IPromise<T2> Then<T2>(ResultMapper<T,T2> mapper, ErrorHandler<T> error);
27 IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error);
26 28
27 29 IPromise<T2> Then<T2>(ResultMapper<T,T2> mapper);
28 30
29 IPromise<T2> Then<T2>(ChainedOperation<T, T2> chained, ErrorHandler<T> error);
31 IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained, ErrorHandler<IPromise<T2>> error, Action cancel);
32
33 IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained, ErrorHandler<IPromise<T2>> error);
30 34
31 IPromise<T2> Then<T2>(ChainedOperation<T, T2> chained);
35 IPromise<T2> Chain<T2>(ResultMapper<T, IPromise<T2>> chained);
36
37 IPromise<T> Error(ErrorHandler<T> error);
32 38
33 39 new IPromise<T> Cancelled(Action handler);
34 40
35 new IPromise<T> Finally(Action handler);
41 new IPromise<T> Anyway(Action handler);
36 42 }
37 43 }
@@ -29,7 +29,7 namespace Implab.Parallels {
29 29 m_pending = source.Length;
30 30 m_action = action;
31 31
32 m_promise.Finally(Dispose);
32 m_promise.Anyway(Dispose);
33 33
34 34 InitPool();
35 35 }
@@ -85,7 +85,7 namespace Implab.Parallels {
85 85 m_transform = transform;
86 86 m_traceContext = TraceContext.Snapshot();
87 87
88 m_promise.Finally(Dispose);
88 m_promise.Anyway(Dispose);
89 89
90 90 InitPool();
91 91 }
@@ -138,7 +138,7 namespace Implab.Parallels {
138 138 return iter.Promise;
139 139 }
140 140
141 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, ChainedOperation<TSrc, TDst> transform, int threads) {
141 public static IPromise<TDst[]> ChainedMap<TSrc, TDst>(this TSrc[] source, ResultMapper<TSrc, IPromise<TDst>> transform, int threads) {
142 142 if (source == null)
143 143 throw new ArgumentNullException("source");
144 144 if (transform == null)
@@ -165,7 +165,7 namespace Implab.Parallels {
165 165 semaphore.WaitOne();
166 166 try {
167 167 var p1 = transform(source[i]);
168 p1.Finally(() => semaphore.Release());
168 p1.Anyway(() => semaphore.Release());
169 169 p1.Then(
170 170 x => {
171 171 res[idx] = x;
@@ -186,7 +186,7 namespace Implab.Parallels {
186 186 return 0;
187 187 });
188 188
189 return promise.Finally(semaphore.Dispose);
189 return promise.Anyway(semaphore.Dispose);
190 190 }
191 191
192 192 }
@@ -11,7 +11,6 namespace Implab {
11 11 public delegate T ErrorHandler<out T>(Exception e);
12 12 public delegate void ResultHandler<in T>(T result);
13 13 public delegate TNew ResultMapper<in TSrc,out TNew>(TSrc result);
14 public delegate IPromise<TNew> ChainedOperation<in TSrc,TNew>(TSrc result);
15 14
16 15 /// <summary>
17 16 /// Класс для асинхронного получения результатов. Так называемое "обещание".
@@ -121,10 +120,15 namespace Implab {
121 120 public Promise(IPromise parent, bool cancellable) {
122 121 m_cancellable = cancellable;
123 122 if (parent != null)
124 Cancelled(() => {
123 AddHandler(
124 null,
125 null,
126 () => {
125 127 if (parent.IsExclusive)
126 128 parent.Cancel();
127 });
129 },
130 null
131 );
128 132 }
129 133
130 134 bool BeginTransit() {
@@ -210,22 +214,14 namespace Implab {
210 214 /// <summary>
211 215 /// Отменяет операцию, если это возможно.
212 216 /// </summary>
213 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
214 public bool Cancel() {
217 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
218 public void Cancel() {
215 219 if (m_cancellable && BeginTransit()) {
216 220 CompleteTransit(CANCELLED_STATE);
217 221 OnStateChanged();
218 return true;
219 }
220 return false;
221 222 }
222
223 // сделано для возвращаемого типа void
224 protected void InternalCancel() {
225 Cancel();
226 223 }
227 224
228
229 225 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) {
230 226 if (success == null && error == null && cancel == null)
231 227 return this;
@@ -255,30 +251,7 namespace Implab {
255 251 return medium;
256 252 }
257 253
258 public IPromise Then(Action success, ErrorHandler error, Action cancel) {
259 return Then(
260 x => success(),
261 e => {
262 error(e);
263 return default(T);
264 },
265 cancel
266 );
267 }
268 254
269 public IPromise Then(Action success, ErrorHandler error) {
270 return Then(
271 x => success(),
272 e => {
273 error(e);
274 return default(T);
275 }
276 );
277 }
278
279 public IPromise Then(Action success) {
280 return Then(x => success());
281 }
282 255
283 256
284 257 public IPromise<T> Then(ResultHandler<T> success) {
@@ -292,6 +265,23 namespace Implab {
292 265 return medium;
293 266 }
294 267
268 /// <summary>
269 /// Последний обработчик в цепочки обещаний.
270 /// </summary>
271 /// <param name="success"></param>
272 /// <param name="error"></param>
273 /// <param name="cancel"></param>
274 /// <remarks>
275 /// <para>
276 /// Данный метод не создает связанного с текущим обещания и предназначен для окончания
277 /// фсинхронной цепочки.
278 /// </para>
279 /// <para>
280 /// Если данный метод вызвать несколько раз, либо добавить другие обработчики, то цепочка
281 /// не будет одиночной <see cref="IsExclusive"/> и, как следствие, будет невозможна отмена
282 /// всей цепи обещаний снизу (с самого последнего обещания).
283 /// </para>
284 /// </remarks>
295 285 public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) {
296 286 if (success == null && error == null && cancel == null)
297 287 return;
@@ -313,18 +303,6 namespace Implab {
313 303 Last(success, null, null);
314 304 }
315 305
316 public void Last(Action success,ErrorHandler error, Action cancel) {
317 Last(x => success(), error, cancel);
318 }
319
320 public void Last(Action success,ErrorHandler error) {
321 Last(x => success(), error, null);
322 }
323
324 public void Last(Action success) {
325 Last(x => success(), null, null);
326 }
327
328 306 public IPromise Error(ErrorHandler error) {
329 307 if (error == null)
330 308 return this;
@@ -371,44 +349,56 namespace Implab {
371 349 /// <param name="error">Обработчик ошибки. Данный обработчик получит
372 350 /// исключение возникшее при выполнении операции.</param>
373 351 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
374 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<T> error) {
375 if (mapper == null)
376 throw new ArgumentNullException("mapper");
352 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error, Action cancel) {
353 Safe.ArgumentNotNull(mapper, "mapper");
377 354
378 355 // создаем прицепленное обещание
379 var chained = new Promise<TNew>(this, true);
356 var medium = new Promise<TNew>(this, true);
380 357
381 ResultHandler<T> resultHandler = result => chained.Resolve(mapper(result));
358 ResultHandler<T> resultHandler = result => medium.Resolve(mapper(result));
382 359 ErrorHandler<T> errorHandler;
383 360 if (error != null)
384 361 errorHandler = e => {
385 362 try {
386 return error(e);
363 medium.Resolve(error(e));
387 364 } catch (Exception e2) {
388 365 // в случае ошибки нужно передать исключение дальше по цепочке
389 chained.Reject(e2);
366 medium.Reject(e2);
390 367 }
391 368 return default(T);
392 369 };
393 370 else
394 371 errorHandler = e => {
395 chained.Reject(e);
372 medium.Reject(e);
396 373 return default(T);
397 374 };
398 375
376 Action cancelHandler;
377 if (cancel != null)
378 cancelHandler = () => {
379 cancel();
380 medium.Cancel();
381 };
382 else
383 cancelHandler = medium.Cancel;
384
399 385
400 386 AddHandler(
401 387 resultHandler,
402 388 errorHandler,
403 chained.InternalCancel,
389 cancelHandler,
404 390 null
405 391 );
406 392
407 return chained;
393 return medium;
394 }
395
396 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error) {
397 return Then(mapper, error, null);
408 398 }
409 399
410 400 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper) {
411 return Then(mapper, null);
401 return Then(mapper, null, null);
412 402 }
413 403
414 404 /// <summary>
@@ -421,7 +411,9 namespace Implab {
421 411 /// <param name="error">Обработчик ошибки. Данный обработчик получит
422 412 /// исключение возникшее при выполнении текуещй операции.</param>
423 413 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
424 public IPromise<TNew> Then<TNew>(ChainedOperation<T, TNew> chained, ErrorHandler<T> error) {
414 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error, Action cancel) {
415
416 Safe.ArgumentNotNull(chained, "chained");
425 417
426 418 // проблема в том, что на момент связывания еще не начата асинхронная операция, поэтому нужно
427 419 // создать посредника, к которому будут подвызяваться следующие обработчики.
@@ -435,12 +427,32 namespace Implab {
435 427
436 428 var promise = chained(result);
437 429
438 promise.Then(
430 promise.Last(
439 431 medium.Resolve,
440 err => {
441 medium.Reject(err);
442 throw new TransientPromiseException(err);
443 }
432 medium.Reject,
433 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
434 );
435
436 // notify chained operation that it's not needed anymore
437 // порядок вызова Then, Cancelled важен, поскольку от этого
438 // зависит IsExclusive
439 medium.Cancelled(() => {
440 if (promise.IsExclusive)
441 promise.Cancel();
442 });
443 };
444
445 ErrorHandler<T> errorHandler;
446
447 if (error != null)
448 errorHandler = delegate(Exception e) {
449 try {
450 var promise = error(e);
451
452 promise.Last(
453 medium.Resolve,
454 medium.Reject,
455 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
444 456 );
445 457
446 458 // notify chained operation that it's not needed anymore
@@ -450,37 +462,44 namespace Implab {
450 462 if (promise.IsExclusive)
451 463 promise.Cancel();
452 464 });
453
454 // внешняя отмена связанной операции рассматривается как ошибка
455 promise.Cancelled(() => medium.Reject(new OperationCanceledException()));
465 } catch (Exception e2) {
466 medium.Reject(e2);
467 }
468 return default(T);
469 };
470 else
471 errorHandler = err => {
472 medium.Reject(err);
473 return default(T);
456 474 };
457 475
458 ErrorHandler<T> errorHandler = delegate(Exception e) {
459 if (error != null) {
460 try {
461 return error(e);
462 } catch (Exception e2) {
463 medium.Reject(e2);
464 return default(T);
465 }
466 }
467 // в случае ошибки нужно передать исключение дальше по цепочке
468 medium.Reject(e);
469 return default(T);
476
477 Action cancelHandler;
478 if (cancel != null)
479 cancelHandler = () => {
480 if (cancel != null)
481 cancel();
482 medium.Cancel();
470 483 };
484 else
485 cancelHandler = medium.Cancel;
471 486
472 487 AddHandler(
473 488 resultHandler,
474 489 errorHandler,
475 medium.InternalCancel,
490 cancelHandler,
476 491 null
477 492 );
478 493
479 494 return medium;
480 495 }
481 496
482 public IPromise<TNew> Then<TNew>(ChainedOperation<T, TNew> chained) {
483 return Then(chained, null);
497 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error) {
498 return Chain(chained, error, null);
499 }
500
501 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained) {
502 return Chain(chained, null, null);
484 503 }
485 504
486 505 public IPromise<T> Cancelled(Action handler) {
@@ -494,9 +513,9 namespace Implab {
494 513 /// </summary>
495 514 /// <param name="handler">The handler that will be called anyway</param>
496 515 /// <returns>self</returns>
497 public IPromise<T> Finally(Action handler) {
498 if (handler == null)
499 throw new ArgumentNullException("handler");
516 public IPromise<T> Anyway(Action handler) {
517 Safe.ArgumentNotNull(handler, "handler");
518
500 519 AddHandler(
501 520 x => handler(),
502 521 e => {
@@ -541,7 +560,7 namespace Implab {
541 560 /// <returns>Результат выполнения обещания</returns>
542 561 public T Join(int timeout) {
543 562 var evt = new ManualResetEvent(false);
544 Finally(() => evt.Set());
563 Anyway(() => evt.Set());
545 564
546 565 if (!evt.WaitOne(timeout, true))
547 566 throw new TimeoutException();
@@ -736,12 +755,49 namespace Implab {
736 755
737 756 #region IPromiseBase explicit implementation
738 757
758 IPromise IPromise.Then(Action success, ErrorHandler error, Action cancel) {
759 return Then(
760 x => success(),
761 e => {
762 error(e);
763 return default(T);
764 },
765 cancel
766 );
767 }
768
769 IPromise IPromise.Then(Action success, ErrorHandler error) {
770 return Then(
771 x => success(),
772 e => {
773 error(e);
774 return default(T);
775 }
776 );
777 }
778
779 IPromise IPromise.Then(Action success) {
780 return Then(x => success());
781 }
782
783 void IPromise.Last(Action success, ErrorHandler error, Action cancel) {
784 Last(x => success(), error, cancel);
785 }
786
787 void IPromise.Last(Action success, ErrorHandler error) {
788 Last(x => success(), error, null);
789 }
790
791 void IPromise.Last(Action success) {
792 Last(x => success(), null, null);
793 }
794
739 795 IPromise IPromise.Error(ErrorHandler error) {
740 796 return Error(error);
741 797 }
742 798
743 IPromise IPromise.Finally(Action handler) {
744 return Finally(handler);
799 IPromise IPromise.Anyway(Action handler) {
800 return Anyway(handler);
745 801 }
746 802
747 803 IPromise IPromise.Cancelled(Action handler) {
@@ -14,12 +14,10 namespace Implab {
14 14
15 15 var p = new SyncContextPromise<T>(context, that, true);
16 16
17 that.Then(
18 x => p.Resolve(x),
19 e => {
20 p.Reject(e);
21 return default(T);
22 }
17 that.Last(
18 p.Resolve,
19 p.Reject,
20 p.Cancel
23 21 );
24 22 return p;
25 23 }
@@ -30,12 +28,10 namespace Implab {
30 28
31 29 var p = new SyncContextPromise<T>(context, that, true);
32 30
33 that.Then(
34 x => p.Resolve(x),
35 e => {
36 p.Reject(e);
37 return default(T);
38 }
31 that.Last(
32 p.Resolve,
33 p.Reject,
34 p.Cancel
39 35 );
40 36 return p;
41 37 }
@@ -16,7 +16,7 using System.Runtime.InteropServices;
16 16 // The form "{Major}.{Minor}.*" will automatically update the build and revision,
17 17 // and "{Major}.{Minor}.{Build}.*" will update just the revision.
18 18
19 [assembly: AssemblyVersion("1.0.*")]
19 [assembly: AssemblyVersion("2.0.*")]
20 20 [assembly: ComVisible(false)]
21 21
22 22 // The following attributes are used to specify the signing key for the assembly,
@@ -92,14 +92,10 namespace Implab
92 92 }
93 93 }
94 94
95 public bool Cancel() {
95 public void Cancel() {
96 96 lock (m_lock) {
97 if (!m_cancelled) {
97 if (!m_cancelled)
98 98 m_cancelled = true;
99 return true;
100 } else {
101 return false;
102 }
103 99 }
104 100 }
105 101
General Comments 0
You need to be logged in to leave comments. Login now