##// END OF EJS Templates
Added ResetState to RunnableComponent to reset in case of failure...
cin -
r205:8200ab154c8a v2
parent child
Show More
@@ -0,0 +1,4
1 <?xml version="1.0" encoding="utf-8"?>
2 <packages>
3 <package id="NUnit" version="2.6.4" targetFramework="net45" />
4 </packages> No newline at end of file
@@ -0,0 +1,16
1 using System;
2
3 namespace Implab.Components
4 {
5 public class StateChangeEventArgs {
6 /// <summary>
7 /// The error information if any
8 /// </summary>
9 public Exception LastError { get; set; }
10
11 /// <summary>
12 /// The state of the service corresponding to this event
13 /// </summary>
14 public ExecutionState State { get; set; }
15 }
16 }
@@ -1,863 +1,863
1 1 using System;
2 2 using System.Reflection;
3 3 using System.Threading;
4 4 using Implab.Parallels;
5 5
6 6 #if MONO
7 7
8 8 using NUnit.Framework;
9 9 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
10 10 using TestMethodAttribute = NUnit.Framework.TestAttribute;
11 11
12 12 #else
13 13
14 14 using Microsoft.VisualStudio.TestTools.UnitTesting;
15 15
16 16 #endif
17 17
18 18 namespace Implab.Test {
19 19 [TestClass]
20 20 public class AsyncTests {
21 21 [TestMethod]
22 22 public void ResolveTest() {
23 23 int res = -1;
24 24 var p = new Promise<int>();
25 25 p.Then(x => res = x);
26 26 p.Resolve(100);
27 27
28 28 Assert.AreEqual(100, res);
29 29 }
30 30
31 31 [TestMethod]
32 32 public void RejectTest() {
33 33 int res = -1;
34 34 Exception err = null;
35 35
36 36 var p = new Promise<int>();
37 37 p.Then(
38 38 x => res = x,
39 39 e => {
40 40 err = e;
41 41 return -2;
42 42 }
43 43 );
44 44 p.Reject(new ApplicationException("error"));
45 45
46 46 Assert.AreEqual(res, -1);
47 47 Assert.AreEqual(err.Message, "error");
48 48
49 49 }
50 50
51 51 [TestMethod]
52 52 public void CancelExceptionTest() {
53 53 var p = new Promise<bool>();
54 54 p.CancelOperation(null);
55 55
56 56 var p2 = p.Then(x => x, null, reason => {
57 57 throw new ApplicationException("CANCELLED");
58 58 });
59 59
60 60 try {
61 61 p2.Join();
62 62 Assert.Fail();
63 63 } catch (ApplicationException err) {
64 64 Assert.AreEqual("CANCELLED", err.InnerException.Message);
65 65 }
66 66
67 67 }
68 68
69 69 [TestMethod]
70 70 public void ContinueOnCancelTest() {
71 71 var p = new Promise<bool>();
72 72 p.CancelOperation(null);
73 73
74 74 var p2 = p
75 75 .Then(x => x, null, reason => {
76 76 throw new ApplicationException("CANCELLED");
77 77 })
78 78 .Then(x => x, e => true);
79 79
80 80 Assert.AreEqual(true, p2.Join());
81 81 }
82 82
83 83 [TestMethod]
84 84 public void JoinSuccessTest() {
85 85 var p = new Promise<int>();
86 86 p.Resolve(100);
87 87 Assert.AreEqual(p.Join(), 100);
88 88 }
89 89
90 90 [TestMethod]
91 91 public void JoinFailTest() {
92 92 var p = new Promise<int>();
93 93 p.Reject(new ApplicationException("failed"));
94 94
95 95 try {
96 96 p.Join();
97 97 throw new ApplicationException("WRONG!");
98 98 } catch (TargetInvocationException err) {
99 99 Assert.AreEqual(err.InnerException.Message, "failed");
100 100 } catch {
101 101 Assert.Fail("Got wrong excaption");
102 102 }
103 103 }
104 104
105 105 [TestMethod]
106 106 public void MapTest() {
107 107 var p = new Promise<int>();
108 108
109 109 var p2 = p.Then(x => x.ToString());
110 110 p.Resolve(100);
111 111
112 112 Assert.AreEqual(p2.Join(), "100");
113 113 }
114 114
115 115 [TestMethod]
116 116 public void FixErrorTest() {
117 117 var p = new Promise<int>();
118 118
119 119 var p2 = p.Then(x => x, e => 101);
120 120
121 121 p.Reject(new Exception());
122 122
123 123 Assert.AreEqual(p2.Join(), 101);
124 124 }
125 125
126 126 [TestMethod]
127 127 public void ChainTest() {
128 128 var p1 = new Promise<int>();
129 129
130 130 var p3 = p1.Chain(x => {
131 131 var p2 = new Promise<string>();
132 132 p2.Resolve(x.ToString());
133 133 return p2;
134 134 });
135 135
136 136 p1.Resolve(100);
137 137
138 138 Assert.AreEqual(p3.Join(), "100");
139 139 }
140 140
141 141 [TestMethod]
142 142 public void ChainFailTest() {
143 143 var p1 = new Promise<int>();
144 144
145 145 var p3 = p1.Chain(x => {
146 146 var p2 = new Promise<string>();
147 147 p2.Reject(new Exception("DIE!!!"));
148 148 return p2;
149 149 });
150 150
151 151 p1.Resolve(100);
152 152
153 153 Assert.IsTrue(p3.IsResolved);
154 154 }
155 155
156 156 [TestMethod]
157 157 public void PoolTest() {
158 158 var pid = Thread.CurrentThread.ManagedThreadId;
159 159 var p = AsyncPool.Invoke(() => Thread.CurrentThread.ManagedThreadId);
160 160
161 161 Assert.AreNotEqual(pid, p.Join());
162 162 }
163 163
164 164 [TestMethod]
165 165 public void WorkerPoolSizeTest() {
166 166 var pool = new WorkerPool(5, 10, 1);
167 167
168 168 Assert.AreEqual(5, pool.PoolSize);
169 169
170 170 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
171 171 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
172 172 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
173 173
174 174 Assert.AreEqual(5, pool.PoolSize);
175 175
176 176 for (int i = 0; i < 100; i++)
177 177 pool.Invoke(() => { Thread.Sleep(100000000); return 10; });
178 178 Thread.Sleep(200);
179 179 Assert.AreEqual(10, pool.PoolSize);
180 180
181 181 pool.Dispose();
182 182 }
183 183
184 184 [TestMethod]
185 185 public void WorkerPoolCorrectTest() {
186 186 var pool = new WorkerPool(0,1000,100);
187 187
188 188 const int iterations = 1000;
189 189 int pending = iterations;
190 190 var stop = new ManualResetEvent(false);
191 191
192 192 var count = 0;
193 193 for (int i = 0; i < iterations; i++) {
194 194 pool
195 195 .Invoke(() => 1)
196 196 .Then(x => Interlocked.Add(ref count, x))
197 197 .Then(x => Math.Log10(x))
198 198 .On(() => {
199 199 Interlocked.Decrement(ref pending);
200 200 if (pending == 0)
201 201 stop.Set();
202 202 }, PromiseEventType.All);
203 203 }
204 204
205 205 stop.WaitOne();
206 206
207 207 Assert.AreEqual(iterations, count);
208 208 Console.WriteLine("Max threads: {0}", pool.MaxRunningThreads);
209 209 pool.Dispose();
210 210
211 211 }
212 212
213 213 [TestMethod]
214 214 public void WorkerPoolDisposeTest() {
215 215 var pool = new WorkerPool(5, 20);
216 216 Assert.AreEqual(5, pool.PoolSize);
217 217 pool.Dispose();
218 218 Thread.Sleep(500);
219 219 Assert.AreEqual(0, pool.PoolSize);
220 220 pool.Dispose();
221 221 }
222 222
223 223 [TestMethod]
224 224 public void MTQueueTest() {
225 225 var queue = new MTQueue<int>();
226 226 int res;
227 227
228 228 queue.Enqueue(10);
229 229 Assert.IsTrue(queue.TryDequeue(out res));
230 230 Assert.AreEqual(10, res);
231 231 Assert.IsFalse(queue.TryDequeue(out res));
232 232
233 233 for (int i = 0; i < 1000; i++)
234 234 queue.Enqueue(i);
235 235
236 236 for (int i = 0; i < 1000; i++) {
237 237 queue.TryDequeue(out res);
238 238 Assert.AreEqual(i, res);
239 239 }
240 240
241 241 int writers = 0;
242 242 int readers = 0;
243 243 var stop = new ManualResetEvent(false);
244 244 int total = 0;
245 245
246 246 const int itemsPerWriter = 10000;
247 247 const int writersCount = 10;
248 248
249 249 for (int i = 0; i < writersCount; i++) {
250 250 Interlocked.Increment(ref writers);
251 251 AsyncPool
252 252 .RunThread(() => {
253 253 for (int ii = 0; ii < itemsPerWriter; ii++) {
254 254 queue.Enqueue(1);
255 255 }
256 256 return 1;
257 257 })
258 258 .On(() => Interlocked.Decrement(ref writers), PromiseEventType.All);
259 259 }
260 260
261 261 for (int i = 0; i < 10; i++) {
262 262 Interlocked.Increment(ref readers);
263 263 AsyncPool
264 264 .RunThread(() => {
265 265 int t;
266 266 do {
267 267 while (queue.TryDequeue(out t))
268 268 Interlocked.Add(ref total, t);
269 269 } while (writers > 0);
270 270 return 1;
271 271 })
272 272 .On(() => {
273 273 Interlocked.Decrement(ref readers);
274 274 if (readers == 0)
275 275 stop.Set();
276 276 }, PromiseEventType.All);
277 277 }
278 278
279 279 stop.WaitOne();
280 280
281 281 Assert.AreEqual(100000, total);
282 282 }
283 283
284 284 [TestMethod]
285 285 public void AsyncQueueTest() {
286 286 var queue = new AsyncQueue<int>();
287 287 int res;
288 288
289 289 queue.Enqueue(10);
290 290 Assert.IsTrue(queue.TryDequeue(out res));
291 291 Assert.AreEqual(10, res);
292 292 Assert.IsFalse(queue.TryDequeue(out res));
293 293
294 294 for (int i = 0; i < 1000; i++)
295 295 queue.Enqueue(i);
296 296
297 297 for (int i = 0; i < 1000; i++) {
298 298 queue.TryDequeue(out res);
299 299 Assert.AreEqual(i, res);
300 300 }
301 301
302 302 const int count = 10000000;
303 303
304 304 int res1 = 0, res2 = 0;
305 305 var t1 = Environment.TickCount;
306 306
307 307 AsyncPool.RunThread(
308 308 () => {
309 309 for (var i = 0; i < count; i++)
310 310 queue.Enqueue(1);
311 311 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
312 312 },
313 313 () => {
314 314 for (var i = 0; i < count; i++)
315 315 queue.Enqueue(2);
316 316 Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1);
317 317 },
318 318 () => {
319 319 int temp;
320 320 int i = 0;
321 321 while (i < count)
322 322 if (queue.TryDequeue(out temp)) {
323 323 i++;
324 324 res1 += temp;
325 325 }
326 326 Console.WriteLine("done reader #1: {0} ms", Environment.TickCount - t1);
327 327 },
328 328 () => {
329 329 int temp;
330 330 int i = 0;
331 331 while (i < count)
332 332 if (queue.TryDequeue(out temp)) {
333 333 i++;
334 334 res2 += temp;
335 335 }
336 336 Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1);
337 337 }
338 338 )
339 .Bundle()
339 .PromiseAll()
340 340 .Join();
341 341
342 342 Assert.AreEqual(count * 3, res1 + res2);
343 343
344 344 Console.WriteLine(
345 345 "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}",
346 346 Environment.TickCount - t1,
347 347 res1,
348 348 res2,
349 349 res1 + res2,
350 350 count
351 351 );
352 352 }
353 353
354 354 [TestMethod]
355 355 public void AsyncQueueBatchTest() {
356 356 var queue = new AsyncQueue<int>();
357 357
358 358 const int wBatch = 29;
359 359 const int wCount = 400000;
360 360 const int total = wBatch * wCount * 2;
361 361 const int summ = wBatch * wCount * 3;
362 362
363 363 int r1 = 0, r2 = 0;
364 364 const int rBatch = 111;
365 365 int read = 0;
366 366
367 367 var t1 = Environment.TickCount;
368 368
369 369 AsyncPool.RunThread(
370 370 () => {
371 371 var buffer = new int[wBatch];
372 372 for(int i = 0; i<wBatch; i++)
373 373 buffer[i] = 1;
374 374
375 375 for(int i =0; i < wCount; i++)
376 376 queue.EnqueueRange(buffer,0,wBatch);
377 377 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
378 378 },
379 379 () => {
380 380 var buffer = new int[wBatch];
381 381 for(int i = 0; i<wBatch; i++)
382 382 buffer[i] = 2;
383 383
384 384 for(int i =0; i < wCount; i++)
385 385 queue.EnqueueRange(buffer,0,wBatch);
386 386 Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1);
387 387 },
388 388 () => {
389 389 var buffer = new int[rBatch];
390 390
391 391 while(read < total) {
392 392 int actual;
393 393 if (queue.TryDequeueRange(buffer,0,rBatch,out actual)) {
394 394 for(int i=0; i< actual; i++)
395 395 r1 += buffer[i];
396 396 Interlocked.Add(ref read, actual);
397 397 }
398 398 }
399 399
400 400 Console.WriteLine("done reader #1: {0} ms", Environment.TickCount - t1);
401 401 },
402 402 () => {
403 403 var buffer = new int[rBatch];
404 404
405 405 while(read < total) {
406 406 int actual;
407 407 if (queue.TryDequeueRange(buffer,0,rBatch,out actual)) {
408 408 for(int i=0; i< actual; i++)
409 409 r2 += buffer[i];
410 410 Interlocked.Add(ref read, actual);
411 411 }
412 412 }
413 413
414 414 Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1);
415 415 }
416 416 )
417 .Bundle()
417 .PromiseAll()
418 418 .Join();
419 419
420 420 Assert.AreEqual(summ , r1 + r2);
421 421
422 422 Console.WriteLine(
423 423 "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}",
424 424 Environment.TickCount - t1,
425 425 r1,
426 426 r2,
427 427 r1 + r2,
428 428 total
429 429 );
430 430 }
431 431
432 432 [TestMethod]
433 433 public void AsyncQueueChunkDequeueTest() {
434 434 var queue = new AsyncQueue<int>();
435 435
436 436 const int wBatch = 31;
437 437 const int wCount = 200000;
438 438 const int total = wBatch * wCount * 3;
439 439 const int summ = wBatch * wCount * 6;
440 440
441 441 int r1 = 0, r2 = 0;
442 442 const int rBatch = 1024;
443 443 int read = 0;
444 444
445 445 var t1 = Environment.TickCount;
446 446
447 447 AsyncPool.RunThread(
448 448 () => {
449 449 var buffer = new int[wBatch];
450 450 for(int i = 0; i<wBatch; i++)
451 451 buffer[i] = 1;
452 452
453 453 for(int i =0; i < wCount; i++)
454 454 queue.EnqueueRange(buffer,0,wBatch);
455 455 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
456 456 },
457 457 () => {
458 458 var buffer = new int[wBatch];
459 459 for(int i = 0; i<wBatch; i++)
460 460 buffer[i] = 2;
461 461
462 462 for(int i =0; i < wCount; i++)
463 463 queue.EnqueueRange(buffer,0,wBatch);
464 464 Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1);
465 465 },
466 466 () => {
467 467 var buffer = new int[wBatch];
468 468 for(int i = 0; i<wBatch; i++)
469 469 buffer[i] = 3;
470 470
471 471 for(int i =0; i < wCount; i++)
472 472 queue.EnqueueRange(buffer,0,wBatch);
473 473 Console.WriteLine("done writer #3: {0} ms", Environment.TickCount - t1);
474 474 },
475 475 () => {
476 476 var buffer = new int[rBatch];
477 477 int count = 1;
478 478 double avgchunk = 0;
479 479 while(read < total) {
480 480 int actual;
481 481 if (queue.TryDequeueChunk(buffer,0,rBatch,out actual)) {
482 482 for(int i=0; i< actual; i++)
483 483 r2 += buffer[i];
484 484 Interlocked.Add(ref read, actual);
485 485 avgchunk = avgchunk*(count-1)/count + actual/(double)count;
486 486 count ++;
487 487 }
488 488 }
489 489
490 490 Console.WriteLine("done reader #2: {0} ms, avg chunk size: {1}", Environment.TickCount - t1, avgchunk);
491 491 }
492 492 )
493 .Bundle()
493 .PromiseAll()
494 494 .Join();
495 495
496 496 Assert.AreEqual(summ , r1 + r2);
497 497
498 498 Console.WriteLine(
499 499 "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}",
500 500 Environment.TickCount - t1,
501 501 r1,
502 502 r2,
503 503 r1 + r2,
504 504 total
505 505 );
506 506 }
507 507
508 508 [TestMethod]
509 509 public void AsyncQueueDrainTest() {
510 510 var queue = new AsyncQueue<int>();
511 511
512 512 const int wBatch = 11;
513 513 const int wCount = 200000;
514 514 const int total = wBatch * wCount * 3;
515 515 const int summ = wBatch * wCount * 3;
516 516
517 517 int r1 = 0, r2 = 0;
518 518 const int rBatch = 11;
519 519 int read = 0;
520 520
521 521 var t1 = Environment.TickCount;
522 522
523 523 AsyncPool.RunThread(
524 524 () => {
525 525 var buffer = new int[wBatch];
526 526 for(int i = 0; i<wBatch; i++)
527 527 buffer[i] = 1;
528 528
529 529 for(int i =0; i < wCount; i++)
530 530 queue.EnqueueRange(buffer,0,wBatch);
531 531 Console.WriteLine("done writer #1: {0} ms", Environment.TickCount - t1);
532 532 },
533 533 () => {
534 534 for(int i =0; i < wCount * wBatch; i++)
535 535 queue.Enqueue(1);
536 536 Console.WriteLine("done writer #2: {0} ms", Environment.TickCount - t1);
537 537 },
538 538 () => {
539 539 var buffer = new int[wBatch];
540 540 for(int i = 0; i<wBatch; i++)
541 541 buffer[i] = 1;
542 542
543 543 for(int i =0; i < wCount; i++)
544 544 queue.EnqueueRange(buffer,0,wBatch);
545 545 Console.WriteLine("done writer #3: {0} ms", Environment.TickCount - t1);
546 546 },
547 547 /*() => {
548 548 int temp;
549 549 int count = 0;
550 550 while (read < total)
551 551 if (queue.TryDequeue(out temp)) {
552 552 count++;
553 553 r1 += temp;
554 554 Interlocked.Increment(ref read);
555 555 }
556 556 Console.WriteLine("done reader #1: {0} ms, {1} count", Environment.TickCount - t1, count);
557 557 },*/
558 558 /*() => {
559 559 var buffer = new int[rBatch];
560 560 var count = 0;
561 561 while(read < total) {
562 562 int actual;
563 563 if (queue.TryDequeueRange(buffer,0,rBatch,out actual)) {
564 564 for(int i=0; i< actual; i++)
565 565 r1 += buffer[i];
566 566 Interlocked.Add(ref read, actual);
567 567 count += actual;
568 568 }
569 569 }
570 570
571 571 Console.WriteLine("done reader #1: {0} ms, {1} items", Environment.TickCount - t1, count);
572 572 },*/
573 573 () => {
574 574 var count = 0;
575 575 while(read < total) {
576 576 var buffer = queue.Drain();
577 577 for(int i=0; i< buffer.Length; i++)
578 578 r1 += buffer[i];
579 579 Interlocked.Add(ref read, buffer.Length);
580 580 count += buffer.Length;
581 581 }
582 582 Console.WriteLine("done reader #1: {0} ms, {1} items", Environment.TickCount - t1, count);
583 583 },
584 584 () => {
585 585 var count = 0;
586 586 while(read < total) {
587 587 var buffer = queue.Drain();
588 588 for(int i=0; i< buffer.Length; i++)
589 589 r2 += buffer[i];
590 590 Interlocked.Add(ref read, buffer.Length);
591 591 count += buffer.Length;
592 592 }
593 593 Console.WriteLine("done reader #2: {0} ms, {1} items", Environment.TickCount - t1, count);
594 594 }
595 595 )
596 .Bundle()
596 .PromiseAll()
597 597 .Join();
598 598
599 599 Assert.AreEqual(summ , r1 + r2);
600 600
601 601 Console.WriteLine(
602 602 "done: {0} ms, summ#1: {1}, summ#2: {2}, total: {3}, count: {4}",
603 603 Environment.TickCount - t1,
604 604 r1,
605 605 r2,
606 606 r1 + r2,
607 607 total
608 608 );
609 609 }
610 610
611 611 [TestMethod]
612 612 public void ParallelMapTest() {
613 613
614 614 const int count = 100000;
615 615
616 616 var args = new double[count];
617 617 var rand = new Random();
618 618
619 619 for (int i = 0; i < count; i++)
620 620 args[i] = rand.NextDouble();
621 621
622 622 var t = Environment.TickCount;
623 623 var res = args.ParallelMap(x => Math.Sin(x*x), 4).Join();
624 624
625 625 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
626 626
627 627 t = Environment.TickCount;
628 628 for (int i = 0; i < count; i++)
629 629 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
630 630 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
631 631 }
632 632
633 633 [TestMethod]
634 634 public void ChainedMapTest() {
635 635
636 636 using (var pool = new WorkerPool()) {
637 637 const int count = 10000;
638 638
639 639 var args = new double[count];
640 640 var rand = new Random();
641 641
642 642 for (int i = 0; i < count; i++)
643 643 args[i] = rand.NextDouble();
644 644
645 645 var t = Environment.TickCount;
646 646 var res = args
647 647 .ChainedMap(
648 648 // Analysis disable once AccessToDisposedClosure
649 649 x => pool.Invoke(
650 650 () => Math.Sin(x * x)
651 651 ),
652 652 4
653 653 )
654 654 .Join();
655 655
656 656 Console.WriteLine("Map complete in {0} ms", Environment.TickCount - t);
657 657
658 658 t = Environment.TickCount;
659 659 for (int i = 0; i < count; i++)
660 660 Assert.AreEqual(Math.Sin(args[i] * args[i]), res[i]);
661 661 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
662 662 Console.WriteLine("Max workers: {0}", pool.MaxRunningThreads);
663 663 }
664 664 }
665 665
666 666 [TestMethod]
667 667 public void ParallelForEachTest() {
668 668
669 669 const int count = 100000;
670 670
671 671 var args = new int[count];
672 672 var rand = new Random();
673 673
674 674 for (int i = 0; i < count; i++)
675 675 args[i] = (int)(rand.NextDouble() * 100);
676 676
677 677 int result = 0;
678 678
679 679 var t = Environment.TickCount;
680 680 args.ParallelForEach(x => Interlocked.Add(ref result, x), 4).Join();
681 681
682 682 Console.WriteLine("Iteration complete in {0} ms, result: {1}", Environment.TickCount - t, result);
683 683
684 684 int result2 = 0;
685 685
686 686 t = Environment.TickCount;
687 687 for (int i = 0; i < count; i++)
688 688 result2 += args[i];
689 689 Assert.AreEqual(result2, result);
690 690 Console.WriteLine("Verified in {0} ms", Environment.TickCount - t);
691 691 }
692 692
693 693 [TestMethod]
694 694 public void ComplexCase1Test() {
695 695 var flags = new bool[3];
696 696
697 697 // op1 (aync 200ms) => op2 (async 200ms) => op3 (sync map)
698 698
699 699 var step1 = PromiseHelper
700 700 .Sleep(200, "Alan")
701 701 .On(() => flags[0] = true, PromiseEventType.Cancelled);
702 702 var p = step1
703 703 .Chain(x =>
704 704 PromiseHelper
705 705 .Sleep(200, "Hi, " + x)
706 706 .Then(y => y)
707 707 .On(() => flags[1] = true, PromiseEventType.Cancelled)
708 708 )
709 709 .On(() => flags[2] = true, PromiseEventType.Cancelled);
710 710 step1.Join();
711 711 p.Cancel();
712 712 try {
713 713 Assert.AreEqual(p.Join(), "Hi, Alan");
714 714 Assert.Fail("Shouldn't get here");
715 715 } catch (OperationCanceledException) {
716 716 }
717 717
718 718 Assert.IsFalse(flags[0]);
719 719 Assert.IsTrue(flags[1]);
720 720 Assert.IsTrue(flags[2]);
721 721 }
722 722
723 723 [TestMethod]
724 724 public void ChainedCancel1Test() {
725 725 // при отмене сцепленной асинхронной операции все обещание должно
726 726 // завершаться ошибкой OperationCanceledException
727 727 var p = PromiseHelper
728 728 .Sleep(1, "Hi, HAL!")
729 729 .Then(x => {
730 730 // запускаем две асинхронные операции
731 731 var result = PromiseHelper.Sleep(1000, "HEM ENABLED!!!");
732 732 // вторая операция отменяет первую до завершения
733 733 PromiseHelper
734 734 .Sleep(100, "HAL, STOP!")
735 735 .Then(result.Cancel);
736 736 return result;
737 737 });
738 738 try {
739 739 p.Join();
740 740 } catch (TargetInvocationException err) {
741 741 Assert.IsTrue(err.InnerException is OperationCanceledException);
742 742 }
743 743 }
744 744
745 745 [TestMethod]
746 746 public void ChainedCancel2Test() {
747 747 // при отмене цепочки обещаний, вложенные операции также должны отменяться
748 748 var pSurvive = new Promise<bool>();
749 749 var hemStarted = new Signal();
750 750 var p = PromiseHelper
751 751 .Sleep(1, "Hi, HAL!")
752 752 .Chain(() => {
753 753 hemStarted.Set();
754 754 // запускаем две асинхронные операции
755 755 var result = PromiseHelper
756 756 .Sleep(2000, "HEM ENABLED!!!")
757 757 .Then(() => pSurvive.Resolve(false));
758 758
759 759 result
760 760 .On(() => pSurvive.Resolve(true), PromiseEventType.Cancelled);
761 761
762 762 return result;
763 763 });
764 764
765 765 hemStarted.Wait();
766 766 p.Cancel();
767 767
768 768 try {
769 769 p.Join();
770 770 Assert.Fail();
771 771 } catch (OperationCanceledException) {
772 772 }
773 773 Assert.IsTrue(pSurvive.Join());
774 774 }
775 775
776 776 [TestMethod]
777 777 public void SharedLockTest() {
778 778 var l = new SharedLock();
779 779 int shared = 0;
780 780 int exclusive = 0;
781 781 var s1 = new Signal();
782 782 var log = new AsyncQueue<string>();
783 783
784 784 try {
785 785 AsyncPool.RunThread(
786 786 () => {
787 787 log.Enqueue("Reader #1 started");
788 788 try {
789 789 l.LockShared();
790 790 log.Enqueue("Reader #1 lock got");
791 791 if (Interlocked.Increment(ref shared) == 2)
792 792 s1.Set();
793 793 s1.Wait();
794 794 log.Enqueue("Reader #1 finished");
795 795 Interlocked.Decrement(ref shared);
796 796 } finally {
797 797 l.Release();
798 798 log.Enqueue("Reader #1 lock released");
799 799 }
800 800 },
801 801 () => {
802 802 log.Enqueue("Reader #2 started");
803 803
804 804 try {
805 805 l.LockShared();
806 806 log.Enqueue("Reader #2 lock got");
807 807
808 808 if (Interlocked.Increment(ref shared) == 2)
809 809 s1.Set();
810 810 s1.Wait();
811 811 log.Enqueue("Reader #2 upgrading to writer");
812 812 Interlocked.Decrement(ref shared);
813 813 l.Upgrade();
814 814 log.Enqueue("Reader #2 upgraded");
815 815
816 816 Assert.AreEqual(1, Interlocked.Increment(ref exclusive));
817 817 Assert.AreEqual(0, shared);
818 818 log.Enqueue("Reader #2 finished");
819 819 Interlocked.Decrement(ref exclusive);
820 820 } finally {
821 821 l.Release();
822 822 log.Enqueue("Reader #2 lock released");
823 823 }
824 824 },
825 825 () => {
826 826 log.Enqueue("Writer #1 started");
827 827 try {
828 828 l.LockExclusive();
829 829 log.Enqueue("Writer #1 got the lock");
830 830 Assert.AreEqual(1, Interlocked.Increment(ref exclusive));
831 831 Interlocked.Decrement(ref exclusive);
832 832 log.Enqueue("Writer #1 is finished");
833 833 } finally {
834 834 l.Release();
835 835 log.Enqueue("Writer #1 lock released");
836 836 }
837 837 }
838 ).Bundle().Join(1000);
838 ).PromiseAll().Join(1000);
839 839 log.Enqueue("Done");
840 840 } catch(Exception error) {
841 841 log.Enqueue(error.Message);
842 842 throw;
843 843 } finally {
844 844 foreach (var m in log)
845 845 Console.WriteLine(m);
846 846 }
847 847 }
848 848
849 849 #if NET_4_5
850 850
851 851 [TestMethod]
852 852 public async void TaskInteropTest() {
853 853 var promise = new Promise<int>();
854 854 promise.Resolve(10);
855 855 var res = await promise;
856 856
857 857 Assert.AreEqual(10, res);
858 858 }
859 859
860 860 #endif
861 861 }
862 862 }
863 863
@@ -1,52 +1,55
1 <?xml version="1.0" encoding="utf-8"?>
1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProductVersion>8.0.30703</ProductVersion>
7 7 <SchemaVersion>2.0</SchemaVersion>
8 8 <ProjectGuid>{4D364996-7ECD-4193-8F90-F223FFEA49DA}</ProjectGuid>
9 9 <OutputType>Library</OutputType>
10 10 <RootNamespace>Implab.Format.Test</RootNamespace>
11 11 <AssemblyName>Implab.Format.Test</AssemblyName>
12 12 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
13 13 <ReleaseVersion>0.2</ReleaseVersion>
14 14 </PropertyGroup>
15 15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
16 16 <DebugSymbols>true</DebugSymbols>
17 17 <DebugType>full</DebugType>
18 18 <Optimize>false</Optimize>
19 19 <OutputPath>bin\Debug</OutputPath>
20 20 <DefineConstants>DEBUG;</DefineConstants>
21 21 <ErrorReport>prompt</ErrorReport>
22 22 <WarningLevel>4</WarningLevel>
23 23 <ConsolePause>false</ConsolePause>
24 24 </PropertyGroup>
25 25 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 26 <DebugType>full</DebugType>
27 27 <Optimize>true</Optimize>
28 28 <OutputPath>bin\Release</OutputPath>
29 29 <ErrorReport>prompt</ErrorReport>
30 30 <WarningLevel>4</WarningLevel>
31 31 <ConsolePause>false</ConsolePause>
32 32 </PropertyGroup>
33 33 <ItemGroup>
34 34 <Reference Include="System" />
35 35 <Reference Include="nunit.framework">
36 36 <HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
37 37 </Reference>
38 38 </ItemGroup>
39 39 <ItemGroup>
40 40 <Compile Include="JsonTests.cs" />
41 41 </ItemGroup>
42 42 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
43 43 <ItemGroup>
44 44 <ProjectReference Include="..\..\Implab\Implab.csproj">
45 45 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
46 46 <Name>Implab</Name>
47 47 </ProjectReference>
48 48 </ItemGroup>
49 49 <ItemGroup>
50 50 <None Include="packages.config" />
51 51 </ItemGroup>
52 <ItemGroup>
53 <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
54 </ItemGroup>
52 55 </Project> No newline at end of file
@@ -1,75 +1,81
1 <?xml version="1.0" encoding="utf-8"?>
1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProductVersion>8.0.30703</ProductVersion>
7 7 <SchemaVersion>2.0</SchemaVersion>
8 8 <ProjectGuid>{2BD05F84-E067-4B87-9477-FDC2676A21C6}</ProjectGuid>
9 9 <OutputType>Library</OutputType>
10 10 <RootNamespace>Implab.Test</RootNamespace>
11 11 <AssemblyName>Implab.Test</AssemblyName>
12 12 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
13 13 <ReleaseVersion>0.2</ReleaseVersion>
14 14 </PropertyGroup>
15 15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
16 16 <DebugSymbols>true</DebugSymbols>
17 17 <DebugType>full</DebugType>
18 18 <Optimize>false</Optimize>
19 19 <OutputPath>bin\Debug</OutputPath>
20 20 <DefineConstants>DEBUG;MONO</DefineConstants>
21 21 <ErrorReport>prompt</ErrorReport>
22 22 <WarningLevel>4</WarningLevel>
23 23 <ConsolePause>false</ConsolePause>
24 24 </PropertyGroup>
25 25 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26 26 <Optimize>true</Optimize>
27 27 <OutputPath>bin\Release</OutputPath>
28 28 <ErrorReport>prompt</ErrorReport>
29 29 <WarningLevel>4</WarningLevel>
30 30 <ConsolePause>false</ConsolePause>
31 31 <DefineConstants>MONO</DefineConstants>
32 32 </PropertyGroup>
33 33 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
34 34 <DebugSymbols>true</DebugSymbols>
35 35 <DebugType>full</DebugType>
36 36 <Optimize>false</Optimize>
37 37 <OutputPath>bin\Debug</OutputPath>
38 38 <DefineConstants>DEBUG;TRACE;NET_4_5;MONO</DefineConstants>
39 39 <ErrorReport>prompt</ErrorReport>
40 40 <WarningLevel>4</WarningLevel>
41 41 <ConsolePause>false</ConsolePause>
42 42 </PropertyGroup>
43 43 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
44 44 <Optimize>true</Optimize>
45 45 <OutputPath>bin\Release</OutputPath>
46 46 <DefineConstants>NET_4_5;MONO</DefineConstants>
47 47 <ErrorReport>prompt</ErrorReport>
48 48 <WarningLevel>4</WarningLevel>
49 49 <ConsolePause>false</ConsolePause>
50 50 </PropertyGroup>
51 51 <ItemGroup>
52 <Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
53 <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
54 <Private>True</Private>
55 </Reference>
52 56 <Reference Include="System" />
53 <Reference Include="nunit.framework" />
54 57 </ItemGroup>
55 58 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
56 59 <ItemGroup>
57 60 <Compile Include="AsyncTests.cs" />
58 61 <Compile Include="PromiseHelper.cs" />
59 62 <Compile Include="Properties\AssemblyInfo.cs" />
60 63 <Compile Include="CancelationTests.cs" />
61 64 <Compile Include="RunnableComponentTests.cs" />
62 65 <Compile Include="PollingComponentTests.cs" />
63 66 <Compile Include="Mock\MockRunnableComponent.cs" />
64 67 <Compile Include="Mock\MockPollingComponent.cs" />
65 68 </ItemGroup>
66 69 <ItemGroup>
67 70 <ProjectReference Include="..\Implab\Implab.csproj">
68 71 <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
69 72 <Name>Implab</Name>
70 73 </ProjectReference>
71 74 </ItemGroup>
72 75 <ItemGroup>
73 <Folder Include="Mock\" />
76 <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
77 </ItemGroup>
78 <ItemGroup>
79 <None Include="packages.config" />
74 80 </ItemGroup>
75 81 </Project> No newline at end of file
@@ -1,71 +1,71
1 1 using System;
2 2 using Implab.Components;
3 3
4 4 namespace Implab.Test.Mock {
5 5 class MockPollingComponent : PollingComponent {
6 6 public MockPollingComponent(TimeSpan interval, Func<Func<ICancellationToken, IPromise>, IPromise> dispatcher, bool initialized) : base(interval, dispatcher, initialized) {
7 7 }
8 8
9 9 public Action MockInit {
10 10 get;
11 11 set;
12 12 }
13 13
14 14 public Action<Exception> MockOnError {
15 15 get;
16 16 set;
17 17 }
18 18
19 19 public Action<Exception> MockOnCancel {
20 20 get;
21 21 set;
22 22 }
23 23
24 24 public Func<IPromise> MockStart {
25 25 get;
26 26 set;
27 27 }
28 28
29 29 public Func<IPromise> MockStop {
30 30 get;
31 31 set;
32 32 }
33 33
34 34 public Func<ICancellationToken, IPromise> MockTick {
35 35 get;
36 36 set;
37 37 }
38 38
39 39 protected override IPromise OnStart() {
40 40 return MockStart != null ? Safe.Run(MockStart).Chain(base.OnStart) : Safe.Run(base.OnStart);
41 41 }
42 42
43 43 protected override IPromise OnStop() {
44 44 return MockStop != null ? Safe.Run(MockStop).Chain(base.OnStop) : Safe.Run(base.OnStop);
45 45 }
46 46
47 47 protected override void OnInitialize() {
48 48 if (MockInit != null)
49 49 MockInit();
50 50 }
51 51
52 52 protected override IPromise OnTick(ICancellationToken cancellationToken) {
53 return MockTick != null ? Safe.Run(() => MockTick(cancellationToken)) : Promise.SUCCESS;
53 return MockTick != null ? Safe.Run(() => MockTick(cancellationToken)) : Promise.Success;
54 54 }
55 55
56 56 protected override void OnTickCancel(Exception error) {
57 57 if (MockOnCancel != null)
58 58 MockOnCancel(error);
59 59 }
60 60
61 61 protected override void OnTickError(Exception error) {
62 62 if (MockOnError != null)
63 63 MockOnError(error);
64 64 }
65 65
66 66 public void CallComponentFail(Exception error) {
67 67 Fail(error);
68 68 }
69 69 }
70 70 }
71 71
@@ -1,38 +1,52
1 1 using System;
2 2 using Implab.Components;
3 3
4 4 namespace Implab.Test.Mock {
5 5 class MockRunnableComponent : RunnableComponent {
6 6 public MockRunnableComponent(bool initialized) : base(initialized) {
7 7 }
8 8
9 public MockRunnableComponent(bool initialized, bool reusable) : base(initialized, reusable) {
10 }
11
9 12 public Action MockInit {
10 13 get;
11 14 set;
12 15 }
13 16
14 17 public Func<IPromise> MockStart {
15 18 get;
16 19 set;
17 20 }
18 21
19 22 public Func<IPromise> MockStop {
20 23 get;
21 24 set;
22 25 }
23 26
27 public Action<bool, Exception> MockDispose {
28 get;
29 set;
30 }
31
24 32 protected override IPromise OnStart() {
25 33 return MockStart != null ? Safe.Run(MockStart).Chain(base.OnStart) : Safe.Run(base.OnStart);
26 34 }
27 35
28 36 protected override IPromise OnStop() {
29 37 return MockStop != null ? Safe.Run(MockStop).Chain(base.OnStop) : Safe.Run(base.OnStop);
30 38 }
31 39
32 40 protected override void OnInitialize() {
33 41 if (MockInit != null)
34 42 MockInit();
35 43 }
44
45 protected override void Dispose(bool disposing, Exception lastError) {
46 if (MockDispose != null)
47 MockDispose(disposing, lastError);
48 base.Dispose(disposing, lastError);
49 }
36 50 }
37 51 }
38 52
@@ -1,230 +1,230
1 1 using System;
2 2 using System.Reflection;
3 3 using System.Threading;
4 4 using Implab.Parallels;
5 5 using Implab.Components;
6 6 using Implab.Test.Mock;
7 7
8 8 #if MONO
9 9
10 10 using NUnit.Framework;
11 11 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
12 12 using TestMethodAttribute = NUnit.Framework.TestAttribute;
13 13 using AssertFailedException = NUnit.Framework.AssertionException;
14 14 #else
15 15
16 16 using Microsoft.VisualStudio.TestTools.UnitTesting;
17 17
18 18 #endif
19 19
20 20 namespace Implab.Test {
21 21 [TestClass]
22 22 public class PollingComponentTests {
23 23 static void ShouldThrow(Action action) {
24 24 try {
25 25 action();
26 26 Assert.Fail();
27 27 } catch (AssertFailedException) {
28 28 throw;
29 29 } catch {
30 30 }
31 31 }
32 32
33 33 [TestMethod]
34 34 public void NormalFlowTest() {
35 35 var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, false);
36 36
37 37 Assert.AreEqual(ExecutionState.Created, comp.State);
38 38
39 comp.Init();
39 comp.Initialize();
40 40
41 41 Assert.AreEqual(ExecutionState.Ready, comp.State);
42 42
43 43 comp.Start().Join(1000);
44 44
45 45 Assert.AreEqual(ExecutionState.Running, comp.State);
46 46
47 47 comp.Stop().Join(1000);
48 48
49 49 Assert.AreEqual(ExecutionState.Disposed, comp.State);
50 50
51 51 }
52 52
53 53 [TestMethod]
54 54 public void ShouldStartTicks() {
55 55 var signal = new Signal();
56 56
57 57 var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
58 58 comp.MockTick = ct => {
59 59 signal.Set();
60 return Promise.SUCCESS;
60 return Promise.Success;
61 61 };
62 62
63 63 comp.Start().Join(1000);
64 64 signal.Wait(1000);
65 65 comp.Stop().Join(1000);
66 66 }
67 67
68 68 [TestMethod]
69 69 public void StopShouldWaitForTickToComplete() {
70 70 var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
71 71 var signal = new Signal();
72 72 var promise = new Promise();
73 73
74 74 // timer should tick once
75 75 comp.MockTick = ct => {
76 76 signal.Set();
77 77 return promise;
78 78 };
79 79
80 80 // start timer
81 81 comp.Start().Join(1000);
82 82
83 83 signal.Wait(); // wait for tick
84 84
85 85 // try to stop component
86 86 var stopping = comp.Stop();
87 87
88 88 Assert.AreEqual(ExecutionState.Stopping, comp.State);
89 89 ShouldThrow(() => stopping.Join(100));
90 90 Assert.AreEqual(ExecutionState.Stopping, comp.State);
91 91
92 92 // complete operation
93 93 promise.Resolve();
94 94
95 95 // the component should stop normally
96 96 stopping.Join(1000);
97 97
98 98 Assert.AreEqual(ExecutionState.Disposed, comp.State);
99 99 }
100 100
101 101 [TestMethod]
102 102 public void ShouldRecoverAfterTickError() {
103 103 var ticks = 0;
104 104
105 105 var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
106 106 var signal = new Signal(); // will signal when timer fires 10 times
107 107
108 108 comp.MockTick = ct => {
109 109 ticks++;
110 110 if (ticks == 10)
111 111 signal.Set();
112 112 // each time handler dies
113 113 throw new Exception("tainted handler");
114 114 };
115 115
116 116 comp.Start();
117 117
118 118 signal.Wait(1000);
119 119
120 120 comp.Stop().Join(1000);
121 121
122 122 Assert.AreEqual(ExecutionState.Disposed, comp.State);
123 123 }
124 124
125 125 [TestMethod]
126 126 public void StopCancelHandlerOnStop() {
127 127 var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
128 128 var started = new Signal();
129 129 bool cancelled = false;
130 130
131 131 // timer should tick once
132 132 comp.MockTick = ct => {
133 133 started.Set();
134 134
135 135 while(!ct.IsCancellationRequested) {
136 136 Thread.Sleep(1);
137 137 }
138 138
139 139 cancelled = true;
140 140
141 141 throw new OperationCanceledException();
142 142 };
143 143
144 144 // start timer
145 145 comp.Start().Join(1000);
146 146
147 147 started.Wait(); // wait for tick
148 148
149 149 // try to stop component
150 150 comp.Stop().Join(1000);
151 151
152 152 Assert.AreEqual(true, cancelled);
153 153
154 154 Assert.AreEqual(ExecutionState.Disposed, comp.State);
155 155 }
156 156
157 157 [TestMethod]
158 158 public void FailTickOnStopShouldBeIgnored() {
159 159 var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
160 160 var started = new Signal();
161 161 var finish = new Signal();
162 162
163 163 // timer should tick once
164 164 comp.MockTick = ct => {
165 165 started.Set();
166 166 finish.Wait();
167 167 // component is in stopping state here
168 168 throw new Exception("Die, die, die!!!");
169 169 };
170 170
171 171
172 172 comp.MockOnError = comp.CallComponentFail;
173 173
174 174 // start timer
175 175 comp.Start().Join(1000);
176 176
177 177 started.Wait(); // wait for tick
178 178
179 179 // try to stop component
180 180 var stopping = comp.Stop();
181 181
182 182 // the component is in stopping state but it is waiting for the tick handler to complete
183 183 finish.Set(); // signal the tick handler to finish
184 184
185 185 // tick handler should stop rather soon
186 186 stopping.Join(1000);
187 187
188 188 // validate the component is disposed
189 189 Assert.AreEqual(ExecutionState.Disposed, comp.State);
190 190 }
191 191
192 192 [TestMethod]
193 193 public void FailTickShouldFailComponent() {
194 194 var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
195 195 var started = new Signal();
196 196 var finish = new Signal();
197 197
198 198 // timer should tick once
199 199 comp.MockTick = ct => {
200 200 started.Set();
201 201 throw new Exception("Die, die, die!!!");
202 202 };
203 203
204 204
205 205 comp.MockOnError = err => {
206 206 comp.CallComponentFail(err);
207 207 finish.Set();
208 208 };
209 209
210 210 // start timer
211 211 comp.Start().Join(1000);
212 212
213 213 started.Wait(); // wait for tick
214 214
215 215 finish.Wait();
216 216
217 217 // try to stop component
218 218 ShouldThrow(() => comp.Stop());
219 219
220 220 Assert.AreEqual(ExecutionState.Failed, comp.State);
221 221 Assert.IsNotNull(comp.LastError);
222 222 Assert.AreEqual("Die, die, die!!!", comp.LastError.Message);
223 223
224 224 comp.Dispose();
225 225 Assert.AreEqual(ExecutionState.Disposed, comp.State);
226 226 }
227 227
228 228 }
229 229 }
230 230
@@ -1,165 +1,230
1 1 using System;
2 2 using System.Reflection;
3 3 using System.Threading;
4 4 using Implab.Parallels;
5 5 using Implab.Components;
6 6 using Implab.Test.Mock;
7 7
8 8 #if MONO
9 9
10 10 using NUnit.Framework;
11 11 using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
12 12 using TestMethodAttribute = NUnit.Framework.TestAttribute;
13 13 using AssertFailedException = NUnit.Framework.AssertionException;
14 14 #else
15 15
16 16 using Microsoft.VisualStudio.TestTools.UnitTesting;
17 17
18 18 #endif
19 19
20 20 namespace Implab.Test {
21 21 [TestClass]
22 22 public class RunnableComponentTests {
23 23
24 24 static void ShouldThrow(Action action) {
25 25 try {
26 26 action();
27 27 Assert.Fail();
28 28 } catch (AssertFailedException) {
29 29 throw;
30 30 } catch {
31 31 }
32 32 }
33 33
34 34
35 35
36 36 [TestMethod]
37 37 public void NormalFlowTest() {
38 38 var comp = new MockRunnableComponent(false);
39 39
40 40 Assert.AreEqual(ExecutionState.Created, comp.State);
41 41
42 comp.Init();
42 comp.Initialize();
43 43
44 44 Assert.AreEqual(ExecutionState.Ready, comp.State);
45 45
46 46 comp.Start().Join(1000);
47 47
48 48 Assert.AreEqual(ExecutionState.Running, comp.State);
49 49
50 50 comp.Stop().Join(1000);
51 51
52 52 Assert.AreEqual(ExecutionState.Disposed, comp.State);
53 53
54 54 }
55 55
56 56 [TestMethod]
57 57 public void InitFailTest() {
58 58 var comp = new MockRunnableComponent(false) {
59 59 MockInit = () => {
60 60 throw new Exception("BAD");
61 61 }
62 62 };
63 63
64 64 ShouldThrow(() => comp.Start());
65 65 ShouldThrow(() => comp.Stop());
66 66 Assert.AreEqual(ExecutionState.Created, comp.State);
67 67
68 ShouldThrow(comp.Init);
68 ShouldThrow(comp.Initialize);
69 69
70 70 Assert.AreEqual(ExecutionState.Failed, comp.State);
71 71
72 72 ShouldThrow(() => comp.Start());
73 73 ShouldThrow(() => comp.Stop());
74 74 Assert.AreEqual(ExecutionState.Failed, comp.State);
75 75
76 76 comp.Dispose();
77 77 Assert.AreEqual(ExecutionState.Disposed, comp.State);
78 78 }
79 79
80 80 [TestMethod]
81 81 public void DisposedTest() {
82 82
83 83 var comp = new MockRunnableComponent(false);
84 84 comp.Dispose();
85 85
86 86 ShouldThrow(() => comp.Start());
87 87 ShouldThrow(() => comp.Stop());
88 ShouldThrow(comp.Init);
88 ShouldThrow(comp.Initialize);
89
90 Assert.AreEqual(ExecutionState.Disposed, comp.State);
91 }
92
93 [TestMethod]
94 public void ShouldCallDisposeOnStop() {
95 var comp = new MockRunnableComponent(true);
96
97 bool disposed = false;
98 comp.MockDispose = (disposing, error) => {
99 disposed = true;
100 };
101
102 comp.Start().Join(1000);
103 comp.Stop().Join(1000);
104
105 ShouldThrow(() => comp.Start());
106 ShouldThrow(() => comp.Stop());
107 ShouldThrow(comp.Initialize);
89 108
90 109 Assert.AreEqual(ExecutionState.Disposed, comp.State);
110 Assert.IsTrue(disposed);
111 }
112
113 [TestMethod]
114 public void ShouldNotCallDisposeOnStop() {
115 var comp = new MockRunnableComponent(true, true);
116
117 bool disposed = false;
118 comp.MockDispose = (disposing, error) => {
119 disposed = true;
120 };
121
122 comp.Start().Join(1000);
123 comp.Stop().Join(1000);
124
125 Assert.AreEqual(ExecutionState.Ready, comp.State);
126 Assert.IsFalse(disposed);
127 }
128
129 [TestMethod]
130 public void SelfDisposeOnStop() {
131 var comp = new MockRunnableComponent(true, true);
132
133 bool disposed = false;
134 Exception lastError = null;
135 comp.MockDispose = (disposing, error) => {
136 disposed = true;
137 lastError = error;
138 };
139
140 comp.Start().Join(1000);
141 comp.Stop().Join(1000);
142
143 Assert.AreEqual(ExecutionState.Ready, comp.State);
144 Assert.IsFalse(disposed);
145
146 comp.MockStop = () => {
147 comp.Dispose();
148 return Promise.Success;
149 };
150
151 comp.Start().Join(1000);
152 comp.Stop().Join(1000);
153
154 Assert.AreEqual(ExecutionState.Disposed, comp.State);
155 Assert.IsTrue(disposed);
91 156 }
92 157
93 158 [TestMethod]
94 159 public void StartCancelTest() {
95 160 var comp = new MockRunnableComponent(true) {
96 161 MockStart = () => PromiseHelper.Sleep(100000, 0)
97 162 };
98 163
99 164 var p = comp.Start();
100 165 Assert.AreEqual(ExecutionState.Starting, comp.State);
101 166 p.Cancel();
102 167 ShouldThrow(() => p.Join(1000));
103 168 Assert.AreEqual(ExecutionState.Failed, comp.State);
104 169
105 170 Assert.IsTrue(comp.LastError is OperationCanceledException);
106 171
107 172 comp.Dispose();
108 173 }
109 174
110 175 [TestMethod]
111 176 public void StartStopTest() {
112 177 var stop = new Signal();
113 178 var comp = new MockRunnableComponent(true) {
114 179 MockStart = () => PromiseHelper.Sleep(100000, 0),
115 180 MockStop = () => AsyncPool.RunThread(stop.Wait)
116 181 };
117 182
118 183 var p1 = comp.Start();
119 184 var p2 = comp.Stop();
120 185 // should enter stopping state
121 186
122 187 ShouldThrow(p1.Join);
123 188 Assert.IsTrue(p1.IsCancelled);
124 189 Assert.AreEqual(ExecutionState.Stopping, comp.State);
125 190
126 191 stop.Set();
127 192 p2.Join(1000);
128 193 Assert.AreEqual(ExecutionState.Disposed, comp.State);
129 194 }
130 195
131 196 [TestMethod]
132 197 public void StartStopFailTest() {
133 198 var comp = new MockRunnableComponent(true) {
134 199 MockStart = () => PromiseHelper.Sleep(100000, 0).Then(null,null,x => { throw new Exception("I'm dead"); })
135 200 };
136 201
137 202 comp.Start();
138 203 var p = comp.Stop();
139 204 // if Start fails to cancel, should fail to stop
140 205 ShouldThrow(() => p.Join(1000));
141 206 Assert.AreEqual(ExecutionState.Failed, comp.State);
142 207 Assert.IsNotNull(comp.LastError);
143 208 Assert.AreEqual("I'm dead", comp.LastError.Message);
144 209 }
145 210
146 211 [TestMethod]
147 212 public void StopCancelTest() {
148 213 var comp = new MockRunnableComponent(true) {
149 214 MockStop = () => PromiseHelper.Sleep(100000, 0)
150 215 };
151 216
152 217 comp.Start();
153 218 var p = comp.Stop();
154 219 Assert.AreEqual(ExecutionState.Stopping, comp.State);
155 220 p.Cancel();
156 221 ShouldThrow(() => p.Join(1000));
157 222 Assert.AreEqual(ExecutionState.Failed, comp.State);
158 223 Assert.IsTrue(comp.LastError is OperationCanceledException);
159 224
160 225 comp.Dispose();
161 226 }
162 227
163 228 }
164 229 }
165 230
@@ -1,33 +1,41
1 1 using System;
2 2
3 3 namespace Implab.Automaton {
4 4 public struct AutomatonTransition : IEquatable<AutomatonTransition> {
5 5 public readonly int s1;
6 6 public readonly int s2;
7 7 public readonly int edge;
8 8
9 9 public AutomatonTransition(int s1, int s2, int edge) {
10 10 this.s1 = s1;
11 11 this.s2 = s2;
12 12 this.edge = edge;
13 13 }
14 14
15 15
16 16 #region IEquatable implementation
17 17 public bool Equals(AutomatonTransition other) {
18 18 return other.s1 == s1 && other.s2 == s2 && other.edge == edge ;
19 19 }
20 20 #endregion
21 21
22 22 public override bool Equals(object obj) {
23 23 if (obj is AutomatonTransition)
24 24 return Equals((AutomatonTransition)obj);
25 25 return base.Equals(obj);
26 26 }
27 27
28 28 public override int GetHashCode() {
29 29 return s1 + s2 + edge;
30 30 }
31
32 public static bool operator == (AutomatonTransition rv, AutomatonTransition lv) {
33 return rv.Equals(lv);
34 }
35
36 public static bool operator !=(AutomatonTransition rv, AutomatonTransition lv) {
37 return rv.Equals(lv);
38 }
31 39 }
32 40 }
33 41
@@ -1,21 +1,21
1 1 using System;
2 2
3 3 namespace Implab.Components {
4 4 /// <summary>
5 5 /// Initializable components are created and initialized in two steps, first we have create the component,
6 /// then we have to complete it's creation by calling an <see cref="Init()"/> method. All parameters needed
7 /// to complete the initialization must be passed before the calling <see cref="Init()"/>
6 /// then we have to complete it's creation by calling an <see cref="Initialize()"/> method. All parameters needed
7 /// to complete the initialization must be passed before the calling <see cref="Initialize()"/>
8 8 /// </summary>
9 9 public interface IInitializable {
10 10 /// <summary>
11 11 /// Completes initialization.
12 12 /// </summary>
13 13 /// <remarks>
14 14 /// Normally virtual methods shouldn't be called from the constructor, due to the incomplete object state, but
15 /// they can be called from this method. This method is also usefull when we constructing a complex grpah
15 /// they can be called from this method. This method is aьуерщlso usefull when we constructing a complex grpah
16 16 /// of components where cyclic references may take place.
17 17 /// </remarks>
18 void Init();
18 void Initialize();
19 19 }
20 20 }
21 21
@@ -1,20 +1,22
1 1 using System;
2 2
3 3 namespace Implab.Components {
4 4 public interface IRunnable {
5 5 /// <summary>
6 6 /// Starts this instance.
7 7 /// </summary>
8 8 IPromise Start();
9 9
10 10 /// <summary>
11 11 /// Stops this instance. After the instance is stopped it can't be started again, stopping should be treated as gracefull and async dispose.
12 12 /// </summary>
13 13 IPromise Stop();
14 14
15 15 ExecutionState State { get; }
16 16
17 event EventHandler<StateChangeEventArgs> StateChanged;
18
17 19 Exception LastError { get; }
18 20 }
19 21 }
20 22
@@ -1,155 +1,155
1 1 using System;
2 2 using System.Threading;
3 3 using Implab.Diagnostics;
4 4
5 5 namespace Implab.Components {
6 6 public class PollingComponent : RunnableComponent {
7 7 readonly Timer m_timer;
8 8 readonly Func<Func<ICancellationToken, IPromise>, IPromise> m_dispatcher;
9 9 readonly TimeSpan m_interval;
10 10
11 11 readonly object m_lock = new object();
12 12
13 13 ActionTask m_pending;
14 14
15 15 protected PollingComponent(TimeSpan interval, Func<Func<ICancellationToken, IPromise>, IPromise> dispatcher, bool initialized) : base(initialized) {
16 16 m_timer = new Timer(OnInternalTick);
17 17
18 18 m_interval = interval;
19 19 m_dispatcher = dispatcher;
20 20 }
21 21
22 22 protected override IPromise OnStart() {
23 23 m_timer.Change(TimeSpan.Zero, m_interval);
24 24
25 25 return base.OnStart();
26 26 }
27 27
28 28 void OnInternalTick(object state) {
29 29 if (StartTick()) {
30 30 try {
31 31 if (m_dispatcher != null) {
32 32 var result = m_dispatcher(OnTick);
33 33 m_pending.CancellationRequested(result.Cancel);
34 34 AwaitTick(result);
35 35 } else {
36 36 AwaitTick(OnTick(m_pending));
37 37 }
38 38 } catch (Exception error) {
39 39 HandleTickError(error);
40 40 }
41 41 }
42 42 }
43 43
44 44 /// <summary>
45 45 /// Checks wheather there is no running handler in the component and marks that the handler is starting.
46 46 /// </summary>
47 47 /// <returns>boolean value, true - the new tick handler may be invoked, false - a tick handler is already running or a component isn't running.</returns>
48 48 /// <remarks>
49 49 /// If the component is stopping no new handlers can be run. Every successful call to this method must be completed with either AwaitTick or HandleTickError handlers.
50 50 /// </remarks>
51 51 protected virtual bool StartTick() {
52 52 lock (m_lock) {
53 53 if (State != ExecutionState.Running || m_pending != null)
54 54 return false;
55 55 // actually the component may be in a not running state here (stopping, disposed or what ever),
56 56 // but OnStop method also locks on the same object and will handle operation in m_pending
57 57 m_pending = new ActionTask(
58 58 () => {
59 59 // only one operation is running, it's safe to assing m_pending from it
60 60 m_pending = null;
61 61 },
62 62 ex => {
63 63 try {
64 64 OnTickError(ex);
65 65 // Analysis disable once EmptyGeneralCatchClause
66 66 } catch {
67 67 } finally {
68 68 m_pending = null;
69 69 }
70 70 // suppress error
71 71 },
72 72 ex => {
73 73 try {
74 74 OnTickCancel(ex);
75 75 // Analysis disable once EmptyGeneralCatchClause
76 76 } catch {
77 77 } finally {
78 78 m_pending = null;
79 79 }
80 80 // supress cancellation
81 81 },
82 82 false
83 83 );
84 84 return true;
85 85 }
86 86 }
87 87
88 88 /// <summary>
89 89 /// Awaits the tick.
90 90 /// </summary>
91 91 /// <param name="tick">Tick.</param>
92 92 /// <remarks>
93 93 /// This method is called only after StartTick method and m_pending will hold the promise which should be fulfilled.
94 94 /// </remarks>
95 95 void AwaitTick(IPromise tick) {
96 96 if (tick == null) {
97 97 m_pending.Resolve();
98 98 } else {
99 99 tick.On(
100 100 m_pending.Resolve,
101 101 m_pending.Reject,
102 102 m_pending.CancelOperation
103 103 );
104 104 }
105 105 }
106 106
107 107 /// <summary>
108 108 /// Handles the tick error.
109 109 /// </summary>
110 110 /// <remarks>
111 111 /// This method is called only after StartTick method and m_pending will hold the promise which should be fulfilled.
112 112 /// </remarks>
113 113 void HandleTickError(Exception error) {
114 114 m_pending.Reject(error);
115 115 }
116 116
117 117 protected virtual void OnTickError(Exception error) {
118 118 }
119 119
120 120 protected virtual void OnTickCancel(Exception error) {
121 121 }
122 122
123 123 /// <summary>
124 124 /// Invoked when the timer ticks, use this method to implement your logic
125 125 /// </summary>
126 126 protected virtual IPromise OnTick(ICancellationToken cancellationToken) {
127 return Promise.SUCCESS;
127 return Promise.Success;
128 128 }
129 129
130 130 protected override IPromise OnStop() {
131 131 m_timer.Change(-1, -1);
132 132
133 133 // the component is in the stopping state
134 134 lock (m_lock) {
135 135 // after this lock no more pending operations could be created
136 136 var pending = m_pending;
137 137 // m_pending could be fulfilled and set to null already
138 138 if (pending != null) {
139 139 pending.Cancel();
140 140 return pending.Then(base.OnStop);
141 141 }
142 142 }
143 143
144 144 return base.OnStop();
145 145 }
146 146
147 147 protected override void Dispose(bool disposing, Exception lastError) {
148 148 if (disposing)
149 149 Safe.Dispose(m_timer);
150 150
151 151 base.Dispose(disposing, lastError);
152 152 }
153 153 }
154 154 }
155 155
@@ -1,296 +1,375
1 1 using System;
2 2
3 3 namespace Implab.Components {
4 4 public abstract class RunnableComponent : IDisposable, IRunnable, IInitializable {
5 5 enum Commands {
6 6 Ok = 0,
7 7 Fail,
8 8 Init,
9 9 Start,
10 10 Stop,
11 11 Dispose,
12 Last = Dispose
12 Reset,
13 Last = Reset
13 14 }
14 15
15 16 class StateMachine {
16 17 static readonly ExecutionState[,] _transitions;
17 18
18 19 static StateMachine() {
19 20 _transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1];
20 21
21 22 Edge(ExecutionState.Created, ExecutionState.Initializing, Commands.Init);
22 23 Edge(ExecutionState.Created, ExecutionState.Disposed, Commands.Dispose);
23 24
24 25 Edge(ExecutionState.Initializing, ExecutionState.Ready, Commands.Ok);
25 26 Edge(ExecutionState.Initializing, ExecutionState.Failed, Commands.Fail);
26 27
27 28 Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start);
28 29 Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose);
29 30
30 31 Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok);
31 32 Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail);
32 33 Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop);
33 34 Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose);
34 35
35 36 Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail);
36 37 Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop);
37 38 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose);
38 39
39 40 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail);
40 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
41 Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok);
42 Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose);
41 43
42 44 Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose);
45 Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset);
43 46 }
44 47
45 48 static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
46 49 _transitions[(int)s1, (int)cmd] = s2;
47 50 }
48 51
49 52 public ExecutionState State {
50 53 get;
51 54 private set;
52 55 }
53 56
54 57 public StateMachine(ExecutionState initial) {
55 58 State = initial;
56 59 }
57 60
58 61 public bool Move(Commands cmd) {
59 62 var next = _transitions[(int)State, (int)cmd];
60 63 if (next == ExecutionState.Undefined)
61 64 return false;
62 65 State = next;
63 66 return true;
64 67 }
65 68 }
66 69
67 70 IPromise m_pending;
68 71 Exception m_lastError;
69 72
70 73 readonly StateMachine m_stateMachine;
74 readonly bool m_reusable;
75 public event EventHandler<StateChangeEventArgs> StateChanged;
71 76
72 protected RunnableComponent(bool initialized) {
77 /// <summary>
78 /// Initializes component state.
79 /// </summary>
80 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param>
81 /// <param name="reusable">If set, the component may start after it has been stopped, otherwise the component is disposed after being stopped.</param>
82 protected RunnableComponent(bool initialized, bool reusable) {
73 83 m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created);
84 m_reusable = reusable;
74 85 DisposeTimeout = 10000;
75 86 }
76 87
77 88 /// <summary>
89 /// Initializes component state. The component created with this constructor is not reusable, i.e. it will be disposed after stop.
90 /// </summary>
91 /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param>
92 protected RunnableComponent(bool initialized) : this(initialized, false) {
93 }
94
95 /// <summary>
78 96 /// Gets or sets the timeout to wait for the pending operation to complete. If the pending operation doesn't finish than the component will be disposed anyway.
79 97 /// </summary>
80 98 protected int DisposeTimeout {
81 99 get;
82 100 set;
83 101 }
84 102
85 103 void ThrowInvalidCommand(Commands cmd) {
86 104 if (m_stateMachine.State == ExecutionState.Disposed)
87 105 throw new ObjectDisposedException(ToString());
88 106
89 throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
107 throw new InvalidOperationException(String.Format("Command {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
108 }
109
110 bool MoveIfInState(Commands cmd, IPromise pending, Exception error, ExecutionState state) {
111 ExecutionState prev, current;
112 lock (m_stateMachine) {
113 if (m_stateMachine.State != state)
114 return false;
115
116 prev = m_stateMachine.State;
117 if (!m_stateMachine.Move(cmd))
118 ThrowInvalidCommand(cmd);
119 current = m_stateMachine.State;
120
121 m_pending = pending;
122 m_lastError = error;
123 }
124 if (prev != current)
125 OnStateChanged(prev, current, error);
126 return true;
90 127 }
91 128
92 void Move(Commands cmd) {
129 bool MoveIfPending(Commands cmd, IPromise pending, Exception error, IPromise expected) {
130 ExecutionState prev, current;
131 lock (m_stateMachine) {
132 if (m_pending != expected)
133 return false;
134 prev = m_stateMachine.State;
93 135 if (!m_stateMachine.Move(cmd))
94 136 ThrowInvalidCommand(cmd);
137 current = m_stateMachine.State;
138 m_pending = pending;
139 m_lastError = error;
140 }
141 if (prev != current)
142 OnStateChanged(prev, current, error);
143 return true;
144 }
145
146 IPromise Move(Commands cmd, IPromise pending, Exception error) {
147 ExecutionState prev, current;
148 IPromise ret;
149 lock (m_stateMachine) {
150 prev = m_stateMachine.State;
151 if (!m_stateMachine.Move(cmd))
152 ThrowInvalidCommand(cmd);
153 current = m_stateMachine.State;
154
155 ret = m_pending;
156 m_pending = pending;
157 m_lastError = error;
158
159 }
160 if(prev != current)
161 OnStateChanged(prev, current, error);
162 return ret;
163 }
164
165 protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) {
166 var h = StateChanged;
167 if (h != null)
168 h(this, new StateChangeEventArgs {
169 State = current,
170 LastError = error
171 });
95 172 }
96 173
97 174 /// <summary>
98 175 /// Moves the component from running to failed state.
99 176 /// </summary>
100 177 /// <param name="error">The exception which is describing the error.</param>
101 /// <returns>Returns true if the component is set to the failed state, false - otherwise.
102 /// This method works only for the running state, in any other state it will return false.</returns>
103 178 protected bool Fail(Exception error) {
104 lock (m_stateMachine) {
105 if(m_stateMachine.State == ExecutionState.Running) {
106 m_stateMachine.Move(Commands.Fail);
107 m_lastError = error;
108 return true;
109 }
110 }
111 return false;
179 return MoveIfInState(Commands.Fail, null, error, ExecutionState.Running);
112 180 }
113 181
114 void Invoke(Commands cmd, Action action) {
115 lock (m_stateMachine)
116 Move(cmd);
182 /// <summary>
183 /// Tries to reset <see cref="ExecutionState.Failed"/> state to <see cref="ExecutionState.Ready"/>.
184 /// </summary>
185 /// <returns>True if component is reset to <see cref="ExecutionState.Ready"/>, false if the componet wasn't
186 /// in <see cref="ExecutionState.Failed"/> state.</returns>
187 /// <remarks>
188 /// This method checks the current state of the component and if it's in <see cref="ExecutionState.Failed"/>
189 /// moves component to <see cref="ExecutionState.Initializing"/>.
190 /// The <see cref="OnResetState()"/> is called and if this method completes succesfully the component moved
191 /// to <see cref="ExecutionState.Ready"/> state, otherwise the component is moved to <see cref="ExecutionState.Failed"/>
192 /// state. If <see cref="OnResetState()"/> throws an exception it will be propagated by this method to the caller.
193 /// </remarks>
194 protected bool ResetState() {
195 if (!MoveIfInState(Commands.Reset, null, null, ExecutionState.Failed))
196 return false;
117 197
118 198 try {
119 action();
120 lock(m_stateMachine)
121 Move(Commands.Ok);
122
199 OnResetState();
200 Move(Commands.Ok, null, null);
201 return true;
123 202 } catch (Exception err) {
124 lock (m_stateMachine) {
125 Move(Commands.Fail);
126 m_lastError = err;
127 }
203 Move(Commands.Fail, null, err);
128 204 throw;
129 205 }
130 206 }
131 207
208 /// <summary>
209 /// This method is called by <see cref="ResetState"/> to reinitialize component in the failed state.
210 /// </summary>
211 /// <remarks>
212 /// Default implementation throws <see cref="NotImplementedException"/> which will cause the component
213 /// fail to reset it's state and it left in <see cref="ExecutionState.Failed"/> state.
214 /// If this method doesn't throw exceptions the component is moved to <see cref="ExecutionState.Ready"/> state.
215 /// </remarks>
216 protected virtual void OnResetState() {
217 throw new NotImplementedException();
218 }
219
132 220 IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) {
133 221 IPromise promise = null;
134 222 IPromise prev;
135 223
136 224 var task = new ActionChainTask(action, null, null, true);
137 225
138 lock (m_stateMachine) {
139 Move(cmd);
140
141 prev = m_pending;
142
143 226 Action<Exception> errorOrCancel = e => {
144 227 if (e == null)
145 228 e = new OperationCanceledException();
146
147 lock (m_stateMachine) {
148 if (m_pending == promise) {
149 Move(Commands.Fail);
150 m_pending = null;
151 m_lastError = e;
152 }
153 }
229 MoveIfPending(Commands.Fail, null, e, promise);
154 230 throw new PromiseTransientException(e);
155 231 };
156 232
157 233 promise = task.Then(
158 () => {
159 lock(m_stateMachine) {
160 if (m_pending == promise) {
161 Move(Commands.Ok);
162 m_pending = null;
163 }
164 }
165 },
234 () => MoveIfPending(Commands.Ok, null, null, promise),
166 235 errorOrCancel,
167 236 errorOrCancel
168 237 );
169 238
170 m_pending = promise;
171 }
239 prev = Move(cmd, promise, null);
172 240
173 241 if (prev == null)
174 242 task.Resolve();
175 243 else
176 244 chain(prev, task);
177 245
178 246 return promise;
179 247 }
180 248
181 249
182 250 #region IInitializable implementation
183 251
184 public void Init() {
185 Invoke(Commands.Init, OnInitialize);
252 public void Initialize() {
253 Move(Commands.Init, null, null);
254
255 try {
256 OnInitialize();
257 Move(Commands.Ok, null, null);
258 } catch (Exception err) {
259 Move(Commands.Fail, null, err);
260 throw;
261 }
186 262 }
187 263
188 264 protected virtual void OnInitialize() {
189 265 }
190 266
191 267 #endregion
192 268
193 269 #region IRunnable implementation
194 270
195 271 public IPromise Start() {
196 272 return InvokeAsync(Commands.Start, OnStart, null);
197 273 }
198 274
199 275 protected virtual IPromise OnStart() {
200 return Promise.SUCCESS;
276 return Promise.Success;
201 277 }
202 278
203 279 public IPromise Stop() {
204 return InvokeAsync(Commands.Stop, OnStop, StopPending).Then(Dispose);
280 var pending = InvokeAsync(Commands.Stop, OnStop, StopPending);
281 return m_reusable ? pending : pending.Then(Dispose);
205 282 }
206 283
207 284 protected virtual IPromise OnStop() {
208 return Promise.SUCCESS;
285 return Promise.Success;
209 286 }
210 287
211 288 /// <summary>
212 289 /// Stops the current operation if one exists.
213 290 /// </summary>
214 291 /// <param name="current">Current.</param>
215 292 /// <param name="stop">Stop.</param>
216 293 protected virtual void StopPending(IPromise current, IDeferred stop) {
217 294 if (current == null) {
218 295 stop.Resolve();
219 296 } else {
220 297 // связваем текущую операцию с операцией остановки
221 298 current.On(
222 299 stop.Resolve, // если текущая операция заверщилась, то можно начинать остановку
223 300 stop.Reject, // если текущая операция дала ошибку - то все плохо, нельзя продолжать
224 301 e => stop.Resolve() // если текущая отменилась, то можно начинать остановку
225 302 );
226 303 // посылаем текущей операции сигнал остановки
227 304 current.Cancel();
228 305 }
229 306 }
230 307
231 308 public ExecutionState State {
232 309 get {
233 310 return m_stateMachine.State;
234 311 }
235 312 }
236 313
237 314 public Exception LastError {
238 315 get {
239 316 return m_lastError;
240 317 }
241 318 }
242 319
243 320 #endregion
244 321
245 322 #region IDisposable implementation
246 323
247 324 /// <summary>
248 325 /// Releases all resource used by the <see cref="Implab.Components.RunnableComponent"/> object.
249 326 /// </summary>
250 327 /// <remarks>
251 328 /// <para>Will not try to stop the component, it will just release all resources.
252 329 /// To cleanup the component gracefully use <see cref="Stop()"/> method.</para>
253 330 /// <para>
254 331 /// In normal cases the <see cref="Dispose()"/> method shouldn't be called, the call to the <see cref="Stop()"/>
255 332 /// method is sufficient to cleanup the component. Call <see cref="Dispose()"/> only to cleanup after errors,
256 333 /// especially if <see cref="Stop"/> method is failed. Using this method insted of <see cref="Stop()"/> may
257 334 /// lead to the data loss by the component.
258 335 /// </para></remarks>
259 336 public void Dispose() {
260 337 IPromise pending;
338
261 339 lock (m_stateMachine) {
262 340 if (m_stateMachine.State == ExecutionState.Disposed)
263 341 return;
264
265 Move(Commands.Dispose);
342 pending = Move(Commands.Dispose, null, null);
343 }
266 344
267 345 GC.SuppressFinalize(this);
268
269 pending = m_pending;
270 m_pending = null;
271 }
272 346 if (pending != null) {
273 347 pending.Cancel();
274 348 pending.Timeout(DisposeTimeout).On(
275 349 () => Dispose(true, null),
276 350 err => Dispose(true, err),
277 351 reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason))
278 352 );
279 353 } else {
280 Dispose(true, m_lastError);
354 Dispose(true, null);
281 355 }
282 356 }
283 357
284 358 ~RunnableComponent() {
285 359 Dispose(false, null);
286 360 }
287 361
288 362 #endregion
289 363
364 /// <summary>
365 /// Releases all resources used by the component, called automatically, override this method to implement your cleanup.
366 /// </summary>
367 /// <param name="disposing">true if this method is called during normal dispose process.</param>
368 /// <param name="lastError">The last error which occured during the component stop.</param>
290 369 protected virtual void Dispose(bool disposing, Exception lastError) {
291 370
292 371 }
293 372
294 373 }
295 374 }
296 375
@@ -1,46 +1,47
1 1 using System;
2 2 using System.IO;
3 3 using System.Text;
4 4
5 5 namespace Implab.Diagnostics {
6 6 public class TextFileListener: ListenerBase {
7 7 readonly TextWriter m_textWriter;
8 readonly object m_lock = new object();
8 9
9 10 public TextFileListener(string fileName) {
10 11 m_textWriter = File.CreateText(fileName);
11 12
12 13 m_textWriter.WriteLine("LOG {0}", DateTime.Now);
13 14 }
14 15
15 16 #region implemented abstract members of ListenerBase
16 17
17 18 public override void Write(LogEventArgs args, object entry) {
18 19 var msg = new StringBuilder();
19 20 for (int i = 0; i < args.Operation.Level; i++)
20 21 msg.Append(" ");
21 22 msg.AppendFormat("[{0}]:{1}: {2}", args.ThreadId, args.Channel, entry);
22 23
23 lock (m_textWriter) {
24 lock (m_lock) {
24 25 if (!IsDisposed) {
25 26 // тут гарантировано еще не освобожден m_textWriter
26 27 m_textWriter.WriteLine(msg);
27 28 m_textWriter.Flush();
28 29 }
29 30 }
30 31 }
31 32
32 33 #endregion
33 34
34 35 protected override void Dispose(bool disposing) {
35 36 base.Dispose(disposing);
36 37 if (disposing) {
37 38 // IsDisposed = true
38 lock (m_textWriter) {
39 lock (m_lock) {
39 40 Safe.Dispose(m_textWriter);
40 41 }
41 42 }
42 43 }
43 44
44 45
45 46 }
46 47 }
@@ -1,278 +1,274
1 1 <?xml version="1.0" encoding="utf-8"?>
2 2 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3 3 <PropertyGroup>
4 4 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5 5 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6 6 <ProjectGuid>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</ProjectGuid>
7 7 <OutputType>Library</OutputType>
8 8 <RootNamespace>Implab</RootNamespace>
9 9 <AssemblyName>Implab</AssemblyName>
10 10 <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
11 11 <ReleaseVersion>0.2</ReleaseVersion>
12 12 <ProductVersion>8.0.30703</ProductVersion>
13 13 <SchemaVersion>2.0</SchemaVersion>
14 14 </PropertyGroup>
15 15 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
16 16 <DebugSymbols>true</DebugSymbols>
17 17 <DebugType>full</DebugType>
18 18 <Optimize>false</Optimize>
19 19 <OutputPath>bin\Debug</OutputPath>
20 20 <DefineConstants>TRACE;DEBUG;</DefineConstants>
21 21 <ErrorReport>prompt</ErrorReport>
22 22 <WarningLevel>4</WarningLevel>
23 23 <ConsolePause>false</ConsolePause>
24 24 <RunCodeAnalysis>true</RunCodeAnalysis>
25 25 </PropertyGroup>
26 26 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
27 27 <DebugType>full</DebugType>
28 28 <Optimize>true</Optimize>
29 29 <OutputPath>bin\Release</OutputPath>
30 30 <ErrorReport>prompt</ErrorReport>
31 31 <WarningLevel>4</WarningLevel>
32 32 <ConsolePause>false</ConsolePause>
33 33 </PropertyGroup>
34 34 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
35 35 <DebugSymbols>true</DebugSymbols>
36 36 <DebugType>full</DebugType>
37 37 <Optimize>false</Optimize>
38 38 <OutputPath>bin\Debug</OutputPath>
39 39 <DefineConstants>TRACE;DEBUG;NET_4_5</DefineConstants>
40 40 <ErrorReport>prompt</ErrorReport>
41 41 <WarningLevel>4</WarningLevel>
42 42 <RunCodeAnalysis>true</RunCodeAnalysis>
43 43 <ConsolePause>false</ConsolePause>
44 44 </PropertyGroup>
45 45 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
46 46 <Optimize>true</Optimize>
47 47 <OutputPath>bin\Release</OutputPath>
48 48 <ErrorReport>prompt</ErrorReport>
49 49 <WarningLevel>4</WarningLevel>
50 50 <ConsolePause>false</ConsolePause>
51 51 <DefineConstants>NET_4_5</DefineConstants>
52 52 </PropertyGroup>
53 53 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMono|AnyCPU' ">
54 54 <DebugSymbols>true</DebugSymbols>
55 55 <DebugType>full</DebugType>
56 56 <Optimize>false</Optimize>
57 57 <OutputPath>bin\Debug</OutputPath>
58 58 <DefineConstants>TRACE;DEBUG;NET_4_5;MONO</DefineConstants>
59 59 <ErrorReport>prompt</ErrorReport>
60 60 <WarningLevel>4</WarningLevel>
61 61 <RunCodeAnalysis>true</RunCodeAnalysis>
62 62 <ConsolePause>false</ConsolePause>
63 63 </PropertyGroup>
64 64 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMono|AnyCPU' ">
65 65 <Optimize>true</Optimize>
66 66 <OutputPath>bin\Release</OutputPath>
67 67 <DefineConstants>NET_4_5;MONO;</DefineConstants>
68 68 <ErrorReport>prompt</ErrorReport>
69 69 <WarningLevel>4</WarningLevel>
70 70 <ConsolePause>false</ConsolePause>
71 71 </PropertyGroup>
72 72 <ItemGroup>
73 73 <Reference Include="System" />
74 74 <Reference Include="System.Xml" />
75 75 <Reference Include="mscorlib" />
76 76 </ItemGroup>
77 77 <ItemGroup>
78 <Compile Include="Components\StateChangeEventArgs.cs" />
78 79 <Compile Include="CustomEqualityComparer.cs" />
79 80 <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
80 81 <Compile Include="Diagnostics\LogChannel.cs" />
81 82 <Compile Include="Diagnostics\LogicalOperation.cs" />
82 83 <Compile Include="Diagnostics\TextFileListener.cs" />
83 84 <Compile Include="Diagnostics\TraceLog.cs" />
84 85 <Compile Include="Diagnostics\TraceEvent.cs" />
85 86 <Compile Include="Diagnostics\TraceEventType.cs" />
86 87 <Compile Include="ICancellable.cs" />
87 88 <Compile Include="IProgressHandler.cs" />
88 89 <Compile Include="IProgressNotifier.cs" />
89 90 <Compile Include="IPromiseT.cs" />
90 91 <Compile Include="IPromise.cs" />
91 92 <Compile Include="IServiceLocator.cs" />
92 93 <Compile Include="ITaskController.cs" />
93 94 <Compile Include="Parallels\DispatchPool.cs" />
94 95 <Compile Include="Parallels\ArrayTraits.cs" />
95 96 <Compile Include="Parallels\MTQueue.cs" />
96 97 <Compile Include="Parallels\WorkerPool.cs" />
97 98 <Compile Include="ProgressInitEventArgs.cs" />
98 99 <Compile Include="Properties\AssemblyInfo.cs" />
99 100 <Compile Include="Parallels\AsyncPool.cs" />
100 101 <Compile Include="Safe.cs" />
101 102 <Compile Include="ValueEventArgs.cs" />
102 103 <Compile Include="PromiseExtensions.cs" />
103 104 <Compile Include="SyncContextPromise.cs" />
104 105 <Compile Include="Diagnostics\OperationContext.cs" />
105 106 <Compile Include="Diagnostics\TraceContext.cs" />
106 107 <Compile Include="Diagnostics\LogEventArgs.cs" />
107 108 <Compile Include="Diagnostics\LogEventArgsT.cs" />
108 109 <Compile Include="Diagnostics\Extensions.cs" />
109 110 <Compile Include="PromiseEventType.cs" />
110 111 <Compile Include="Parallels\AsyncQueue.cs" />
111 112 <Compile Include="PromiseT.cs" />
112 113 <Compile Include="IDeferred.cs" />
113 114 <Compile Include="IDeferredT.cs" />
114 115 <Compile Include="Promise.cs" />
115 116 <Compile Include="PromiseTransientException.cs" />
116 117 <Compile Include="Parallels\Signal.cs" />
117 118 <Compile Include="Parallels\SharedLock.cs" />
118 119 <Compile Include="Diagnostics\ILogWriter.cs" />
119 120 <Compile Include="Diagnostics\ListenerBase.cs" />
120 121 <Compile Include="Parallels\BlockingQueue.cs" />
121 122 <Compile Include="AbstractEvent.cs" />
122 123 <Compile Include="AbstractPromise.cs" />
123 124 <Compile Include="AbstractPromiseT.cs" />
124 125 <Compile Include="FuncTask.cs" />
125 126 <Compile Include="FuncTaskBase.cs" />
126 127 <Compile Include="FuncTaskT.cs" />
127 128 <Compile Include="ActionChainTaskBase.cs" />
128 129 <Compile Include="ActionChainTask.cs" />
129 130 <Compile Include="ActionChainTaskT.cs" />
130 131 <Compile Include="FuncChainTaskBase.cs" />
131 132 <Compile Include="FuncChainTask.cs" />
132 133 <Compile Include="FuncChainTaskT.cs" />
133 134 <Compile Include="ActionTaskBase.cs" />
134 135 <Compile Include="ActionTask.cs" />
135 136 <Compile Include="ActionTaskT.cs" />
136 137 <Compile Include="ICancellationToken.cs" />
137 138 <Compile Include="SuccessPromise.cs" />
138 139 <Compile Include="SuccessPromiseT.cs" />
139 140 <Compile Include="PromiseAwaiterT.cs" />
140 141 <Compile Include="PromiseAwaiter.cs" />
141 142 <Compile Include="Components\ComponentContainer.cs" />
142 143 <Compile Include="Components\Disposable.cs" />
143 144 <Compile Include="Components\DisposablePool.cs" />
144 145 <Compile Include="Components\ObjectPool.cs" />
145 146 <Compile Include="Components\ServiceLocator.cs" />
146 147 <Compile Include="Components\IInitializable.cs" />
147 148 <Compile Include="TaskController.cs" />
148 149 <Compile Include="Components\App.cs" />
149 150 <Compile Include="Components\IRunnable.cs" />
150 151 <Compile Include="Components\ExecutionState.cs" />
151 152 <Compile Include="Components\RunnableComponent.cs" />
152 153 <Compile Include="Components\IFactory.cs" />
153 154 <Compile Include="Automaton\IAlphabet.cs" />
154 155 <Compile Include="Automaton\ParserException.cs" />
155 156 <Compile Include="Automaton\IndexedAlphabetBase.cs" />
156 157 <Compile Include="Automaton\IAlphabetBuilder.cs" />
157 158 <Compile Include="Automaton\RegularExpressions\AltToken.cs" />
158 159 <Compile Include="Automaton\RegularExpressions\BinaryToken.cs" />
159 160 <Compile Include="Automaton\RegularExpressions\CatToken.cs" />
160 161 <Compile Include="Automaton\RegularExpressions\StarToken.cs" />
161 162 <Compile Include="Automaton\RegularExpressions\SymbolToken.cs" />
162 163 <Compile Include="Automaton\RegularExpressions\EmptyToken.cs" />
163 164 <Compile Include="Automaton\RegularExpressions\Token.cs" />
164 165 <Compile Include="Automaton\RegularExpressions\IVisitor.cs" />
165 166 <Compile Include="Automaton\AutomatonTransition.cs" />
166 167 <Compile Include="Formats\JSON\JSONElementContext.cs" />
167 168 <Compile Include="Formats\JSON\JSONElementType.cs" />
168 169 <Compile Include="Formats\JSON\JSONGrammar.cs" />
169 170 <Compile Include="Formats\JSON\JSONParser.cs" />
170 171 <Compile Include="Formats\JSON\JSONScanner.cs" />
171 172 <Compile Include="Formats\JSON\JsonTokenType.cs" />
172 173 <Compile Include="Formats\JSON\JSONWriter.cs" />
173 174 <Compile Include="Formats\JSON\JSONXmlReader.cs" />
174 175 <Compile Include="Formats\JSON\JSONXmlReaderOptions.cs" />
175 176 <Compile Include="Formats\JSON\StringTranslator.cs" />
176 177 <Compile Include="Automaton\MapAlphabet.cs" />
177 178 <Compile Include="Formats\CharAlphabet.cs" />
178 179 <Compile Include="Formats\ByteAlphabet.cs" />
179 180 <Compile Include="Automaton\IDFATable.cs" />
180 181 <Compile Include="Automaton\IDFATableBuilder.cs" />
181 182 <Compile Include="Automaton\DFATable.cs" />
182 183 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitor.cs" />
183 184 <Compile Include="Automaton\RegularExpressions\ITaggedDFABuilder.cs" />
184 185 <Compile Include="Formats\TextScanner.cs" />
185 186 <Compile Include="Formats\StringScanner.cs" />
186 187 <Compile Include="Formats\ReaderScanner.cs" />
187 188 <Compile Include="Formats\ScannerContext.cs" />
188 189 <Compile Include="Formats\Grammar.cs" />
189 190 <Compile Include="Automaton\RegularExpressions\EndTokenT.cs" />
190 191 <Compile Include="Automaton\RegularExpressions\EndToken.cs" />
191 192 <Compile Include="Automaton\RegularExpressions\RegularExpressionVisitorT.cs" />
192 193 <Compile Include="Automaton\AutomatonConst.cs" />
193 194 <Compile Include="Automaton\RegularExpressions\RegularDFA.cs" />
194 195 <Compile Include="Components\LazyAndWeak.cs" />
195 196 <Compile Include="AbstractTask.cs" />
196 197 <Compile Include="AbstractTaskT.cs" />
197 198 <Compile Include="FailedPromise.cs" />
198 199 <Compile Include="FailedPromiseT.cs" />
199 200 <Compile Include="Components\PollingComponent.cs" />
200 201 </ItemGroup>
201 202 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
202 203 <ItemGroup />
203 204 <ProjectExtensions>
204 205 <MonoDevelop>
205 206 <Properties>
206 207 <Policies>
207 208 <CSharpFormattingPolicy IndentSwitchBody="True" NamespaceBraceStyle="EndOfLine" ClassBraceStyle="EndOfLine" InterfaceBraceStyle="EndOfLine" StructBraceStyle="EndOfLine" EnumBraceStyle="EndOfLine" MethodBraceStyle="EndOfLine" ConstructorBraceStyle="EndOfLine" DestructorBraceStyle="EndOfLine" BeforeMethodDeclarationParentheses="False" BeforeMethodCallParentheses="False" BeforeConstructorDeclarationParentheses="False" NewLineBeforeConstructorInitializerColon="NewLine" NewLineAfterConstructorInitializerColon="SameLine" BeforeIndexerDeclarationBracket="False" BeforeDelegateDeclarationParentheses="False" NewParentheses="False" SpacesBeforeBrackets="False" inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
208 209 <TextStylePolicy FileWidth="120" EolMarker="Unix" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
209 210 <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" />
210 211 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="application/xml" />
211 212 <XmlFormattingPolicy inheritsSet="Mono" inheritsScope="application/xml" scope="application/xml" />
212 213 <TextStylePolicy FileWidth="120" TabsToSpaces="False" inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/plain" />
213 214 <NameConventionPolicy>
214 215 <Rules>
215 216 <NamingRule Name="Namespaces" AffectedEntity="Namespace" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
216 217 <NamingRule Name="Types" AffectedEntity="Class, Struct, Enum, Delegate" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
217 218 <NamingRule Name="Interfaces" AffectedEntity="Interface" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
218 219 <RequiredPrefixes>
219 220 <String>I</String>
220 221 </RequiredPrefixes>
221 222 </NamingRule>
222 223 <NamingRule Name="Attributes" AffectedEntity="CustomAttributes" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
223 224 <RequiredSuffixes>
224 225 <String>Attribute</String>
225 226 </RequiredSuffixes>
226 227 </NamingRule>
227 228 <NamingRule Name="Event Arguments" AffectedEntity="CustomEventArgs" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
228 229 <RequiredSuffixes>
229 230 <String>EventArgs</String>
230 231 </RequiredSuffixes>
231 232 </NamingRule>
232 233 <NamingRule Name="Exceptions" AffectedEntity="CustomExceptions" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
233 234 <RequiredSuffixes>
234 235 <String>Exception</String>
235 236 </RequiredSuffixes>
236 237 </NamingRule>
237 238 <NamingRule Name="Methods" AffectedEntity="Methods" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
238 239 <NamingRule Name="Static Readonly Fields" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Protected, Public" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True" />
239 240 <NamingRule Name="Fields (Non Private)" AffectedEntity="Field" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
240 241 <NamingRule Name="ReadOnly Fields (Non Private)" AffectedEntity="ReadonlyField" VisibilityMask="Internal, Public" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False" />
241 242 <NamingRule Name="Fields (Private)" AffectedEntity="Field, ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
242 243 <RequiredPrefixes>
243 244 <String>m_</String>
244 245 </RequiredPrefixes>
245 246 </NamingRule>
246 247 <NamingRule Name="Static Fields (Private)" AffectedEntity="Field" VisibilityMask="Private" NamingStyle="CamelCase" IncludeInstanceMembers="False" IncludeStaticEntities="True">
247 248 <RequiredPrefixes>
248 249 <String>_</String>
249 250 </RequiredPrefixes>
250 251 </NamingRule>
251 252 <NamingRule Name="ReadOnly Fields (Private)" AffectedEntity="ReadonlyField" VisibilityMask="Private, Protected" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="False">
252 253 <RequiredPrefixes>
253 254 <String>m_</String>
254 255 </RequiredPrefixes>
255 256 </NamingRule>
256 257 <NamingRule Name="Constant Fields" AffectedEntity="ConstantField" VisibilityMask="VisibilityMask" NamingStyle="AllUpper" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
257 258 <NamingRule Name="Properties" AffectedEntity="Property" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
258 259 <NamingRule Name="Events" AffectedEntity="Event" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
259 260 <NamingRule Name="Enum Members" AffectedEntity="EnumMember" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
260 261 <NamingRule Name="Parameters" AffectedEntity="Parameter, LocalVariable" VisibilityMask="VisibilityMask" NamingStyle="CamelCase" IncludeInstanceMembers="True" IncludeStaticEntities="True" />
261 262 <NamingRule Name="Type Parameters" AffectedEntity="TypeParameter" VisibilityMask="VisibilityMask" NamingStyle="PascalCase" IncludeInstanceMembers="True" IncludeStaticEntities="True">
262 263 <RequiredPrefixes>
263 264 <String>T</String>
264 265 </RequiredPrefixes>
265 266 </NamingRule>
266 267 </Rules>
267 268 </NameConventionPolicy>
268 269 </Policies>
269 270 </Properties>
270 271 </MonoDevelop>
271 272 </ProjectExtensions>
272 <ItemGroup>
273 <Folder Include="Components\" />
274 <Folder Include="Automaton\RegularExpressions\" />
275 <Folder Include="Formats\" />
276 <Folder Include="Formats\JSON\" />
277 </ItemGroup>
273 <ItemGroup />
278 274 </Project> No newline at end of file
@@ -1,22 +1,25
1 1 using System;
2 2 using Implab.Parallels;
3 3
4 4 namespace Implab {
5 5 public class Promise : AbstractPromise, IDeferred {
6 public static readonly Promise SUCCESS;
6 public static readonly IPromise Success;
7 7
8 8 static Promise() {
9 SUCCESS = new Promise();
10 SUCCESS.Resolve();
9 Success = new SuccessPromise();
11 10 }
12 11
13 12 public void Resolve() {
14 13 SetResult();
15 14 }
16 15
17 16 public void Reject(Exception error) {
18 17 SetError(error);
19 18 }
19
20 public static IPromise FromException(Exception exception) {
21 return new FailedPromise(exception);
22 }
20 23 }
21 24 }
22 25
@@ -1,289 +1,300
1 1 using System.Threading;
2 2 using System;
3 3 using Implab.Diagnostics;
4 4 using System.Collections.Generic;
5 using System.Linq;
5 6
6 7 namespace Implab {
7 8 public static class PromiseExtensions {
8 9 public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) {
9 10 Safe.ArgumentNotNull(that, "that");
10 11 var context = SynchronizationContext.Current;
11 12 if (context == null)
12 13 return that;
13 14
14 15 var p = new SyncContextPromise<T>(context);
15 16 p.CancellationRequested(that.Cancel);
16 17
17 18 that.On(
18 19 p.Resolve,
19 20 p.Reject,
20 21 p.CancelOperation
21 22 );
22 23 return p;
23 24 }
24 25
25 26 public static IPromise<T> DispatchToContext<T>(this IPromise<T> that, SynchronizationContext context) {
26 27 Safe.ArgumentNotNull(that, "that");
27 28 Safe.ArgumentNotNull(context, "context");
28 29
29 30 var p = new SyncContextPromise<T>(context);
30 31 p.CancellationRequested(that.Cancel);
31 32
32 33 that.On(
33 34 p.Resolve,
34 35 p.Reject,
35 36 p.CancelOperation
36 37 );
37 38 return p;
38 39 }
39 40
40 41 /// <summary>
41 42 /// Ensures the dispatched.
42 43 /// </summary>
43 44 /// <returns>The dispatched.</returns>
44 45 /// <param name="that">That.</param>
45 46 /// <param name="head">Head.</param>
46 47 /// <param name="cleanup">Cleanup.</param>
47 48 /// <typeparam name="TPromise">The 1st type parameter.</typeparam>
48 49 /// <typeparam name="T">The 2nd type parameter.</typeparam>
49 50 public static TPromise EnsureDispatched<TPromise,T>(this TPromise that, IPromise<T> head, Action<T> cleanup) where TPromise : IPromise{
50 51 Safe.ArgumentNotNull(that, "that");
51 52 Safe.ArgumentNotNull(head, "head");
52 53
53 54 that.On(() => head.On(cleanup), PromiseEventType.Cancelled);
54 55
55 56 return that;
56 57 }
57 58
58 59 public static AsyncCallback AsyncCallback<T>(this Promise<T> that, Func<IAsyncResult,T> callback) {
59 60 Safe.ArgumentNotNull(that, "that");
60 61 Safe.ArgumentNotNull(callback, "callback");
61 62 var op = TraceContext.Instance.CurrentOperation;
62 63 return ar => {
63 64 TraceContext.Instance.EnterLogicalOperation(op,false);
64 65 try {
65 66 that.Resolve(callback(ar));
66 67 } catch (Exception err) {
67 68 that.Reject(err);
68 69 } finally {
69 70 TraceContext.Instance.Leave();
70 71 }
71 72 };
72 73 }
73 74
74 75 static void CancelByTimeoutCallback(object cookie) {
75 76 ((ICancellable)cookie).Cancel(new TimeoutException());
76 77 }
77 78
78 79 /// <summary>
79 80 /// Cancells promise after the specified timeout is elapsed.
80 81 /// </summary>
81 82 /// <param name="that">The promise to cancel on timeout.</param>
82 83 /// <param name="milliseconds">The timeout in milliseconds.</param>
83 84 /// <typeparam name="TPromise">The 1st type parameter.</typeparam>
84 85 public static TPromise Timeout<TPromise>(this TPromise that, int milliseconds) where TPromise : IPromise {
85 86 Safe.ArgumentNotNull(that, "that");
86 87 var timer = new Timer(CancelByTimeoutCallback, that, milliseconds, -1);
87 88 that.On(timer.Dispose, PromiseEventType.All);
88 89 return that;
89 90 }
90 91
91 public static IPromise Bundle(this ICollection<IPromise> that) {
92 public static IPromise PromiseAll(this IEnumerable<IPromise> that) {
93 Safe.ArgumentNotNull(that, "that");
94 return PromiseAll(that.ToList());
95 }
96
97 public static IPromise<T[]> PromiseAll<T>(this IEnumerable<IPromise<T>> that) {
98 Safe.ArgumentNotNull(that, "that");
99 return PromiseAll(that.ToList());
100 }
101
102 public static IPromise PromiseAll(this ICollection<IPromise> that) {
92 103 Safe.ArgumentNotNull(that, "that");
93 104
94 105 int count = that.Count;
95 106 int errors = 0;
96 107 var medium = new Promise();
97 108
98 109 if (count == 0) {
99 110 medium.Resolve();
100 111 return medium;
101 112 }
102 113
103 114 medium.On(() => {
104 115 foreach(var p2 in that)
105 116 p2.Cancel();
106 117 }, PromiseEventType.ErrorOrCancel);
107 118
108 119 foreach (var p in that)
109 120 p.On(
110 121 () => {
111 122 if (Interlocked.Decrement(ref count) == 0)
112 123 medium.Resolve();
113 124 },
114 125 error => {
115 126 if (Interlocked.Increment(ref errors) == 1)
116 127 medium.Reject(
117 128 new Exception("The dependency promise is failed", error)
118 129 );
119 130 },
120 131 reason => {
121 132 if (Interlocked.Increment(ref errors) == 1)
122 133 medium.Cancel(
123 134 new Exception("The dependency promise is cancelled")
124 135 );
125 136 }
126 137 );
127 138
128 139 return medium;
129 140 }
130 141
131 public static IPromise<T[]> Bundle<T>(this ICollection<IPromise<T>> that) {
142 public static IPromise<T[]> PromiseAll<T>(this ICollection<IPromise<T>> that) {
132 143 Safe.ArgumentNotNull(that, "that");
133 144
134 145 int count = that.Count;
135 146 int errors = 0;
136 147 var medium = new Promise<T[]>();
137 148 var results = new T[that.Count];
138 149
139 150 medium.On(() => {
140 151 foreach(var p2 in that)
141 152 p2.Cancel();
142 153 }, PromiseEventType.ErrorOrCancel);
143 154
144 155 int i = 0;
145 156 foreach (var p in that) {
146 157 var idx = i;
147 158 p.On(
148 159 x => {
149 160 results[idx] = x;
150 161 if (Interlocked.Decrement(ref count) == 0)
151 162 medium.Resolve(results);
152 163 },
153 164 error => {
154 165 if (Interlocked.Increment(ref errors) == 1)
155 166 medium.Reject(
156 167 new Exception("The dependency promise is failed", error)
157 168 );
158 169 },
159 170 reason => {
160 171 if (Interlocked.Increment(ref errors) == 1)
161 172 medium.Cancel(
162 173 new Exception("The dependency promise is cancelled", reason)
163 174 );
164 175 }
165 176 );
166 177 i++;
167 178 }
168 179
169 180 return medium;
170 181 }
171 182
172 183 public static IPromise Then(this IPromise that, Action success, Action<Exception> error, Action<Exception> cancel) {
173 184 Safe.ArgumentNotNull(that, "that");
174 185
175 186 var d = new ActionTask(success, error, cancel, false);
176 187 that.On(d.Resolve, d.Reject, d.CancelOperation);
177 188 d.CancellationRequested(that.Cancel);
178 189 return d;
179 190 }
180 191
181 192 public static IPromise Then(this IPromise that, Action success, Action<Exception> error) {
182 193 return Then(that, success, error, null);
183 194 }
184 195
185 196 public static IPromise Then(this IPromise that, Action success) {
186 197 return Then(that, success, null, null);
187 198 }
188 199
189 200 public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error, Func<Exception, T> cancel) {
190 201 Safe.ArgumentNotNull(that, "that");
191 202
192 203 var d = new FuncTask<T>(success, error, cancel, false);
193 204 that.On(d.Resolve, d.Reject, d.CancelOperation);
194 205 d.CancellationRequested(that.Cancel);
195 206 return d;
196 207 }
197 208
198 209 public static IPromise<T> Then<T>(this IPromise that, Func<T> success, Func<Exception, T> error) {
199 210 return Then(that, success, error, null);
200 211 }
201 212
202 213 public static IPromise<T> Then<T>(this IPromise that, Func<T> success) {
203 214 return Then(that, success, null, null);
204 215 }
205 216
206 217 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error, Func<Exception, T2> cancel) {
207 218 Safe.ArgumentNotNull(that, "that");
208 219 var d = new FuncTask<T,T2>(success, error, cancel, false);
209 220 that.On(d.Resolve, d.Reject, d.CancelOperation);
210 221 d.CancellationRequested(that.Cancel);
211 222 return d;
212 223 }
213 224
214 225 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success, Func<Exception, T2> error) {
215 226 return Then(that, success, error, null);
216 227 }
217 228
218 229 public static IPromise<T2> Then<T, T2>(this IPromise<T> that, Func<T, T2> success) {
219 230 return Then(that, success, null, null);
220 231 }
221 232
222 233 #region chain traits
223 234 public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception,IPromise> error, Func<Exception,IPromise> cancel) {
224 235 Safe.ArgumentNotNull(that, "that");
225 236
226 237 var d = new ActionChainTask(success, error, cancel, false);
227 238 that.On(d.Resolve, d.Reject, d.CancelOperation);
228 239 d.CancellationRequested(that.Cancel);
229 240 return d;
230 241 }
231 242
232 243 public static IPromise Chain(this IPromise that, Func<IPromise> success, Func<Exception,IPromise> error) {
233 244 return Chain(that, success, error, null);
234 245 }
235 246
236 247 public static IPromise Chain(this IPromise that, Func<IPromise> success) {
237 248 return Chain(that, success, null, null);
238 249 }
239 250
240 251 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error, Func<Exception, IPromise<T>> cancel) {
241 252 Safe.ArgumentNotNull(that, "that");
242 253
243 254 var d = new FuncChainTask<T>(success, error, cancel, false);
244 255 that.On(d.Resolve, d.Reject, d.CancelOperation);
245 256 if (success != null)
246 257 d.CancellationRequested(that.Cancel);
247 258 return d;
248 259 }
249 260
250 261 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success, Func<Exception, IPromise<T>> error) {
251 262 return Chain(that, success, error, null);
252 263 }
253 264
254 265 public static IPromise<T> Chain<T>(this IPromise that, Func<IPromise<T>> success) {
255 266 return Chain(that, success, null, null);
256 267 }
257 268
258 269 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error, Func<Exception, IPromise<T2>> cancel) {
259 270 Safe.ArgumentNotNull(that, "that");
260 271 var d = new FuncChainTask<T,T2>(success, error, cancel, false);
261 272 that.On(d.Resolve, d.Reject, d.CancelOperation);
262 273 if (success != null)
263 274 d.CancellationRequested(that.Cancel);
264 275 return d;
265 276 }
266 277
267 278 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success, Func<Exception, IPromise<T2>> error) {
268 279 return Chain(that, success, error, null);
269 280 }
270 281
271 282 public static IPromise<T2> Chain<T, T2>(this IPromise<T> that, Func<T, IPromise<T2>> success) {
272 283 return Chain(that, success, null, null);
273 284 }
274 285
275 286 #endregion
276 287
277 288
278 289 #if NET_4_5
279 290
280 291 public static PromiseAwaiter<T> GetAwaiter<T>(this IPromise<T> that) {
281 292 Safe.ArgumentNotNull(that, "that");
282 293
283 294 return new PromiseAwaiter<T>(that);
284 295 }
285 296
286 297 #endif
287 298 }
288 299 }
289 300
@@ -1,114 +1,114
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Text.RegularExpressions;
6 6 using System.Diagnostics;
7 7
8 8 namespace Implab
9 9 {
10 10 public static class Safe
11 11 {
12 12 public static void ArgumentAssert(bool condition, string paramName) {
13 13 if (!condition)
14 14 throw new ArgumentException("The parameter is invalid", paramName);
15 15 }
16 16
17 17 public static void ArgumentMatch(string value, string paramName, Regex rx) {
18 18 if (rx == null)
19 19 throw new ArgumentNullException("rx");
20 20 if (!rx.IsMatch(value))
21 21 throw new ArgumentException(String.Format("The prameter value must match {0}", rx), paramName);
22 22 }
23 23
24 24 public static void ArgumentNotEmpty(string value, string paramName) {
25 25 if (String.IsNullOrEmpty(value))
26 26 throw new ArgumentException("The parameter can't be empty", paramName);
27 27 }
28 28
29 29 public static void ArgumentNotEmpty<T>(T[] value, string paramName) {
30 30 if (value == null || value.Length == 0)
31 31 throw new ArgumentException("The array must be not emty", paramName);
32 32 }
33 33
34 34 public static void ArgumentNotNull(object value, string paramName) {
35 35 if (value == null)
36 36 throw new ArgumentNullException(paramName);
37 37 }
38 38
39 39 public static void ArgumentInRange(int value, int min, int max, string paramName) {
40 40 if (value < min || value > max)
41 41 throw new ArgumentOutOfRangeException(paramName);
42 42 }
43 43
44 44 public static void ArgumentOfType(object value, Type type, string paramName) {
45 45 if (!type.IsInstanceOfType(value))
46 46 throw new ArgumentException(String.Format("The parameter must be of type {0}", type), paramName);
47 47 }
48 48
49 49 public static void Dispose(params IDisposable[] objects) {
50 50 foreach (var d in objects)
51 51 if (d != null)
52 52 d.Dispose();
53 53 }
54 54
55 55 public static void Dispose(params object[] objects) {
56 56 foreach (var obj in objects) {
57 57 var d = obj as IDisposable;
58 58 if (d != null)
59 59 d.Dispose();
60 60 }
61 61 }
62 62
63 63 public static void Dispose(object obj) {
64 64 var d = obj as IDisposable;
65 65 if (d != null)
66 66 d.Dispose();
67 67 }
68 68
69 69 [DebuggerStepThrough]
70 70 public static IPromise<T> Run<T>(Func<T> action) {
71 71 ArgumentNotNull(action, "action");
72 72
73 73 try {
74 74 return Promise<T>.FromResult(action());
75 75 } catch (Exception err) {
76 76 return Promise<T>.FromException(err);
77 77 }
78 78 }
79 79
80 80 [DebuggerStepThrough]
81 81 public static IPromise Run(Action action) {
82 82 ArgumentNotNull(action, "action");
83 83
84 84 try {
85 85 action();
86 return Promise.SUCCESS;
86 return Promise.Success;
87 87 } catch (Exception err) {
88 88 return new FailedPromise(err);
89 89 }
90 90 }
91 91
92 92 [DebuggerStepThrough]
93 93 public static IPromise Run(Func<IPromise> action) {
94 94 ArgumentNotNull(action, "action");
95 95
96 96 try {
97 97 return action() ?? new FailedPromise(new Exception("The action returned null"));
98 98 } catch (Exception err) {
99 99 return new FailedPromise(err);
100 100 }
101 101 }
102 102
103 103 [DebuggerStepThrough]
104 104 public static IPromise<T> Run<T>(Func<IPromise<T>> action) {
105 105 ArgumentNotNull(action, "action");
106 106
107 107 try {
108 108 return action() ?? Promise<T>.FromException(new Exception("The action returned null"));
109 109 } catch (Exception err) {
110 110 return Promise<T>.FromException(err);
111 111 }
112 112 }
113 113 }
114 114 }
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now