##// 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 /// <example>
17 /// <example>
18 /// client
18 /// client
19 /// .Get("description.txt") // returns a promise
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 /// .Then(
21 /// .Then(
22 /// description => m_ctl.Text = description // now it's safe
22 /// description => m_ctl.Text = description // now it's safe
23 /// )
23 /// )
24 /// </example>
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)
27 Safe.ArgumentNotNull(that, "that");
28 throw new ArgumentNullException("that");
28 Safe.ArgumentNotNull(ctl, "ctl");
29 if (ctl == null)
30 throw new ArgumentNullException("ctl");
31
29
32 var directed = new ControlBoundPromise<T>(ctl,that,true);
30 var directed = new ControlBoundPromise<T>(ctl,that,true);
33
31
34 that.Then(
32 that.Last(
35 directed.Resolve,
33 directed.Resolve,
36 err =>
34 directed.Reject,
37 {
35 directed.Cancel
38 directed.Reject(err);
39 return default(T);
40 }
41 );
36 );
42
37
43 return directed;
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 // You can specify all the values or you can default the Build and Revision Numbers
32 // You can specify all the values or you can default the Build and Revision Numbers
33 // by using the '*' as shown below:
33 // by using the '*' as shown below:
34 // [assembly: AssemblyVersion("1.0.*")]
34 // [assembly: AssemblyVersion("1.0.*")]
35 [assembly: AssemblyVersion("1.0.0.0")]
35 [assembly: AssemblyVersion("2.0.*")]
36 [assembly: AssemblyFileVersion("1.0.0.0")]
@@ -38,6 +38,38 namespace Implab.Test {
38 }
38 }
39
39
40 [TestMethod]
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 public void JoinSuccessTest() {
73 public void JoinSuccessTest() {
42 var p = new Promise<int>();
74 var p = new Promise<int>();
43 p.Resolve(100);
75 p.Resolve(100);
@@ -63,7 +95,7 namespace Implab.Test {
63 public void MapTest() {
95 public void MapTest() {
64 var p = new Promise<int>();
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 p.Resolve(100);
99 p.Resolve(100);
68
100
69 Assert.AreEqual(p2.Join(), "100");
101 Assert.AreEqual(p2.Join(), "100");
@@ -185,8 +217,8 namespace Implab.Test {
185 var stop = new ManualResetEvent(false);
217 var stop = new ManualResetEvent(false);
186 int total = 0;
218 int total = 0;
187
219
188 int itemsPerWriter = 1000;
220 int itemsPerWriter = 10000;
189 int writersCount = 3;
221 int writersCount = 10;
190
222
191 for (int i = 0; i < writersCount; i++) {
223 for (int i = 0; i < writersCount; i++) {
192 Interlocked.Increment(ref writers);
224 Interlocked.Increment(ref writers);
@@ -318,7 +350,7 namespace Implab.Test {
318 .Chain(x =>
350 .Chain(x =>
319 PromiseHelper
351 PromiseHelper
320 .Sleep(200, "Hi, " + x)
352 .Sleep(200, "Hi, " + x)
321 .Map(y => y)
353 .Then(y => y)
322 .Cancelled(() => flags[1] = true)
354 .Cancelled(() => flags[1] = true)
323 )
355 )
324 .Cancelled(() => flags[2] = true);
356 .Cancelled(() => flags[2] = true);
@@ -341,7 +373,7 namespace Implab.Test {
341 // завершаться ошибкой OperationCanceledException
373 // завершаться ошибкой OperationCanceledException
342 var p = PromiseHelper
374 var p = PromiseHelper
343 .Sleep(1, "Hi, HAL!")
375 .Sleep(1, "Hi, HAL!")
344 .Chain(x => {
376 .Then(x => {
345 // запускаем две асинхронные операции
377 // запускаем две асинхронные операции
346 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
378 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
347 // вторая операция отменяет первую до завершения
379 // вторая операция отменяет первую до завершения
@@ -360,16 +392,15 namespace Implab.Test {
360 [TestMethod]
392 [TestMethod]
361 public void ChainedCancel2Test() {
393 public void ChainedCancel2Test() {
362 // при отмене цепочки обещаний, вложенные операции также должны отменяться
394 // при отмене цепочки обещаний, вложенные операции также должны отменяться
363 IPromise p = null;
364 var pSurvive = new Promise<bool>();
395 var pSurvive = new Promise<bool>();
365 var hemStarted = new ManualResetEvent(false);
396 var hemStarted = new ManualResetEvent(false);
366 p = PromiseHelper
397 var p = PromiseHelper
367 .Sleep(1, "Hi, HAL!")
398 .Sleep(1, "Hi, HAL!")
368 .Chain(x => {
399 .Chain(x => {
369 hemStarted.Set();
400 hemStarted.Set();
370 // запускаем две асинхронные операции
401 // запускаем две асинхронные операции
371 var result = PromiseHelper
402 var result = PromiseHelper
372 .Sleep(1000, "HEM ENABLED!!!")
403 .Sleep(10000, "HEM ENABLED!!!")
373 .Then(s => pSurvive.Resolve(false));
404 .Then(s => pSurvive.Resolve(false));
374
405
375 result
406 result
@@ -215,7 +215,7 namespace Implab.Diagnostics {
215 Safe.ArgumentNotNull(promise, "promise");
215 Safe.ArgumentNotNull(promise, "promise");
216
216
217 var ctx = DetachLogicalOperation();
217 var ctx = DetachLogicalOperation();
218 promise.Finally(() => {
218 promise.Anyway(() => {
219 var old = _current;
219 var old = _current;
220 TraceContext.Attach(ctx);
220 TraceContext.Attach(ctx);
221 TraceContext.Current.EndLogicalOperation();
221 TraceContext.Current.EndLogicalOperation();
@@ -5,6 +5,6 using System.Text;
5
5
6 namespace Implab {
6 namespace Implab {
7 public interface ICancellable {
7 public interface ICancellable {
8 bool Cancel();
8 void Cancel();
9 }
9 }
10 }
10 }
@@ -52,7 +52,7 namespace Implab {
52 /// </summary>
52 /// </summary>
53 /// <param name="handler">Обработчик.</param>
53 /// <param name="handler">Обработчик.</param>
54 /// <remarks>После обработке ошибки, она передается дальше.</remarks>
54 /// <remarks>После обработке ошибки, она передается дальше.</remarks>
55 IPromise Finally(Action handler);
55 IPromise Anyway(Action handler);
56 /// <summary>
56 /// <summary>
57 /// Обработчик для регистрации отмены обещания, событие отмены не может быть подавлено.
57 /// Обработчик для регистрации отмены обещания, событие отмены не может быть подавлено.
58 /// </summary>
58 /// </summary>
@@ -10,28 +10,34 namespace Implab {
10
10
11 new T Join(int timeout);
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 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel);
19 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel);
14
20
15 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
21 IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error);
16
22
17 IPromise<T> Then(ResultHandler<T> success);
23 IPromise<T> Then(ResultHandler<T> success);
18
24
19 void Last(ResultHandler<T> success, ErrorHandler error, Action cancel);
25 IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error, Action cancel);
20 void Last(ResultHandler<T> success, ErrorHandler error);
21 void Last(ResultHandler<T> success);
22
26
23 IPromise<T> Error(ErrorHandler<T> error);
27 IPromise<T2> Then<T2>(ResultMapper<T, T2> mapper, ErrorHandler<T2> error);
24
25 IPromise<T2> Then<T2>(ResultMapper<T,T2> mapper, ErrorHandler<T> error);
26
28
27 IPromise<T2> Then<T2>(ResultMapper<T,T2> mapper);
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 new IPromise<T> Cancelled(Action handler);
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 m_pending = source.Length;
29 m_pending = source.Length;
30 m_action = action;
30 m_action = action;
31
31
32 m_promise.Finally(Dispose);
32 m_promise.Anyway(Dispose);
33
33
34 InitPool();
34 InitPool();
35 }
35 }
@@ -85,7 +85,7 namespace Implab.Parallels {
85 m_transform = transform;
85 m_transform = transform;
86 m_traceContext = TraceContext.Snapshot();
86 m_traceContext = TraceContext.Snapshot();
87
87
88 m_promise.Finally(Dispose);
88 m_promise.Anyway(Dispose);
89
89
90 InitPool();
90 InitPool();
91 }
91 }
@@ -138,7 +138,7 namespace Implab.Parallels {
138 return iter.Promise;
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 if (source == null)
142 if (source == null)
143 throw new ArgumentNullException("source");
143 throw new ArgumentNullException("source");
144 if (transform == null)
144 if (transform == null)
@@ -165,7 +165,7 namespace Implab.Parallels {
165 semaphore.WaitOne();
165 semaphore.WaitOne();
166 try {
166 try {
167 var p1 = transform(source[i]);
167 var p1 = transform(source[i]);
168 p1.Finally(() => semaphore.Release());
168 p1.Anyway(() => semaphore.Release());
169 p1.Then(
169 p1.Then(
170 x => {
170 x => {
171 res[idx] = x;
171 res[idx] = x;
@@ -186,7 +186,7 namespace Implab.Parallels {
186 return 0;
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 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);
15
14
16 /// <summary>
15 /// <summary>
17 /// Класс для асинхронного получения результатов. Так называемое "обещание".
16 /// Класс для асинхронного получения результатов. Так называемое "обещание".
@@ -121,10 +120,15 namespace Implab {
121 public Promise(IPromise parent, bool cancellable) {
120 public Promise(IPromise parent, bool cancellable) {
122 m_cancellable = cancellable;
121 m_cancellable = cancellable;
123 if (parent != null)
122 if (parent != null)
124 Cancelled(() => {
123 AddHandler(
124 null,
125 null,
126 () => {
125 if (parent.IsExclusive)
127 if (parent.IsExclusive)
126 parent.Cancel();
128 parent.Cancel();
127 });
129 },
130 null
131 );
128 }
132 }
129
133
130 bool BeginTransit() {
134 bool BeginTransit() {
@@ -210,22 +214,14 namespace Implab {
210 /// <summary>
214 /// <summary>
211 /// Отменяет операцию, если это возможно.
215 /// Отменяет операцию, если это возможно.
212 /// </summary>
216 /// </summary>
213 /// <returns><c>true</c> Операция была отменена, обработчики не будут вызваны.<c>false</c> отмена не возможна, поскольку обещание уже выполнено и обработчики отработали.</returns>
217 /// <remarks>Для определения была ли операция отменена следует использовать свойство <see cref="IsCancelled"/>.</remarks>
214 public bool Cancel() {
218 public void Cancel() {
215 if (m_cancellable && BeginTransit()) {
219 if (m_cancellable && BeginTransit()) {
216 CompleteTransit(CANCELLED_STATE);
220 CompleteTransit(CANCELLED_STATE);
217 OnStateChanged();
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 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) {
225 public IPromise<T> Then(ResultHandler<T> success, ErrorHandler<T> error, Action cancel) {
230 if (success == null && error == null && cancel == null)
226 if (success == null && error == null && cancel == null)
231 return this;
227 return this;
@@ -255,30 +251,7 namespace Implab {
255 return medium;
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 public IPromise<T> Then(ResultHandler<T> success) {
257 public IPromise<T> Then(ResultHandler<T> success) {
@@ -292,6 +265,23 namespace Implab {
292 return medium;
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 public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) {
285 public void Last(ResultHandler<T> success, ErrorHandler error, Action cancel) {
296 if (success == null && error == null && cancel == null)
286 if (success == null && error == null && cancel == null)
297 return;
287 return;
@@ -313,18 +303,6 namespace Implab {
313 Last(success, null, null);
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 public IPromise Error(ErrorHandler error) {
306 public IPromise Error(ErrorHandler error) {
329 if (error == null)
307 if (error == null)
330 return this;
308 return this;
@@ -371,44 +349,56 namespace Implab {
371 /// <param name="error">Обработчик ошибки. Данный обработчик получит
349 /// <param name="error">Обработчик ошибки. Данный обработчик получит
372 /// исключение возникшее при выполнении операции.</param>
350 /// исключение возникшее при выполнении операции.</param>
373 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
351 /// <returns>Новое обещание, которое будет выполнено при выполнении исходного обещания.</returns>
374 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<T> error) {
352 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper, ErrorHandler<TNew> error, Action cancel) {
375 if (mapper == null)
353 Safe.ArgumentNotNull(mapper, "mapper");
376 throw new ArgumentNullException("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 ErrorHandler<T> errorHandler;
359 ErrorHandler<T> errorHandler;
383 if (error != null)
360 if (error != null)
384 errorHandler = e => {
361 errorHandler = e => {
385 try {
362 try {
386 return error(e);
363 medium.Resolve(error(e));
387 } catch (Exception e2) {
364 } catch (Exception e2) {
388 // в случае ошибки нужно передать исключение дальше по цепочке
365 // в случае ошибки нужно передать исключение дальше по цепочке
389 chained.Reject(e2);
366 medium.Reject(e2);
390 }
367 }
391 return default(T);
368 return default(T);
392 };
369 };
393 else
370 else
394 errorHandler = e => {
371 errorHandler = e => {
395 chained.Reject(e);
372 medium.Reject(e);
396 return default(T);
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 AddHandler(
386 AddHandler(
401 resultHandler,
387 resultHandler,
402 errorHandler,
388 errorHandler,
403 chained.InternalCancel,
389 cancelHandler,
404 null
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 public IPromise<TNew> Then<TNew>(ResultMapper<T, TNew> mapper) {
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 /// <summary>
404 /// <summary>
@@ -421,7 +411,9 namespace Implab {
421 /// <param name="error">Обработчик ошибки. Данный обработчик получит
411 /// <param name="error">Обработчик ошибки. Данный обработчик получит
422 /// исключение возникшее при выполнении текуещй операции.</param>
412 /// исключение возникшее при выполнении текуещй операции.</param>
423 /// <returns>Новое обещание, которое будет выполнено по окончанию указанной аснхронной операции.</returns>
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 var promise = chained(result);
428 var promise = chained(result);
437
429
438 promise.Then(
430 promise.Last(
439 medium.Resolve,
431 medium.Resolve,
440 err => {
432 medium.Reject,
441 medium.Reject(err);
433 () => medium.Reject(new OperationCanceledException()) // внешняя отмена связанной операции рассматривается как ошибка
442 throw new TransientPromiseException(err);
434 );
443 }
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 // notify chained operation that it's not needed anymore
458 // notify chained operation that it's not needed anymore
@@ -450,37 +462,44 namespace Implab {
450 if (promise.IsExclusive)
462 if (promise.IsExclusive)
451 promise.Cancel();
463 promise.Cancel();
452 });
464 });
453
465 } catch (Exception e2) {
454 // внешняя отмена связанной операции рассматривается как ошибка
466 medium.Reject(e2);
455 promise.Cancelled(() => medium.Reject(new OperationCanceledException()));
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) {
476
459 if (error != null) {
477 Action cancelHandler;
460 try {
478 if (cancel != null)
461 return error(e);
479 cancelHandler = () => {
462 } catch (Exception e2) {
480 if (cancel != null)
463 medium.Reject(e2);
481 cancel();
464 return default(T);
482 medium.Cancel();
465 }
466 }
467 // в случае ошибки нужно передать исключение дальше по цепочке
468 medium.Reject(e);
469 return default(T);
470 };
483 };
484 else
485 cancelHandler = medium.Cancel;
471
486
472 AddHandler(
487 AddHandler(
473 resultHandler,
488 resultHandler,
474 errorHandler,
489 errorHandler,
475 medium.InternalCancel,
490 cancelHandler,
476 null
491 null
477 );
492 );
478
493
479 return medium;
494 return medium;
480 }
495 }
481
496
482 public IPromise<TNew> Then<TNew>(ChainedOperation<T, TNew> chained) {
497 public IPromise<TNew> Chain<TNew>(ResultMapper<T, IPromise<TNew>> chained, ErrorHandler<IPromise<TNew>> error) {
483 return Then(chained, null);
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 public IPromise<T> Cancelled(Action handler) {
505 public IPromise<T> Cancelled(Action handler) {
@@ -494,9 +513,9 namespace Implab {
494 /// </summary>
513 /// </summary>
495 /// <param name="handler">The handler that will be called anyway</param>
514 /// <param name="handler">The handler that will be called anyway</param>
496 /// <returns>self</returns>
515 /// <returns>self</returns>
497 public IPromise<T> Finally(Action handler) {
516 public IPromise<T> Anyway(Action handler) {
498 if (handler == null)
517 Safe.ArgumentNotNull(handler, "handler");
499 throw new ArgumentNullException("handler");
518
500 AddHandler(
519 AddHandler(
501 x => handler(),
520 x => handler(),
502 e => {
521 e => {
@@ -541,7 +560,7 namespace Implab {
541 /// <returns>Результат выполнения обещания</returns>
560 /// <returns>Результат выполнения обещания</returns>
542 public T Join(int timeout) {
561 public T Join(int timeout) {
543 var evt = new ManualResetEvent(false);
562 var evt = new ManualResetEvent(false);
544 Finally(() => evt.Set());
563 Anyway(() => evt.Set());
545
564
546 if (!evt.WaitOne(timeout, true))
565 if (!evt.WaitOne(timeout, true))
547 throw new TimeoutException();
566 throw new TimeoutException();
@@ -736,12 +755,49 namespace Implab {
736
755
737 #region IPromiseBase explicit implementation
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 IPromise IPromise.Error(ErrorHandler error) {
795 IPromise IPromise.Error(ErrorHandler error) {
740 return Error(error);
796 return Error(error);
741 }
797 }
742
798
743 IPromise IPromise.Finally(Action handler) {
799 IPromise IPromise.Anyway(Action handler) {
744 return Finally(handler);
800 return Anyway(handler);
745 }
801 }
746
802
747 IPromise IPromise.Cancelled(Action handler) {
803 IPromise IPromise.Cancelled(Action handler) {
@@ -14,12 +14,10 namespace Implab {
14
14
15 var p = new SyncContextPromise<T>(context, that, true);
15 var p = new SyncContextPromise<T>(context, that, true);
16
16
17 that.Then(
17 that.Last(
18 x => p.Resolve(x),
18 p.Resolve,
19 e => {
19 p.Reject,
20 p.Reject(e);
20 p.Cancel
21 return default(T);
22 }
23 );
21 );
24 return p;
22 return p;
25 }
23 }
@@ -30,12 +28,10 namespace Implab {
30
28
31 var p = new SyncContextPromise<T>(context, that, true);
29 var p = new SyncContextPromise<T>(context, that, true);
32
30
33 that.Then(
31 that.Last(
34 x => p.Resolve(x),
32 p.Resolve,
35 e => {
33 p.Reject,
36 p.Reject(e);
34 p.Cancel
37 return default(T);
38 }
39 );
35 );
40 return p;
36 return p;
41 }
37 }
@@ -16,7 +16,7 using System.Runtime.InteropServices;
16 // The form "{Major}.{Minor}.*" will automatically update the build and revision,
16 // The form "{Major}.{Minor}.*" will automatically update the build and revision,
17 // and "{Major}.{Minor}.{Build}.*" will update just the revision.
17 // and "{Major}.{Minor}.{Build}.*" will update just the revision.
18
18
19 [assembly: AssemblyVersion("1.0.*")]
19 [assembly: AssemblyVersion("2.0.*")]
20 [assembly: ComVisible(false)]
20 [assembly: ComVisible(false)]
21
21
22 // The following attributes are used to specify the signing key for the assembly,
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 lock (m_lock) {
96 lock (m_lock) {
97 if (!m_cancelled) {
97 if (!m_cancelled)
98 m_cancelled = true;
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