1 /***
2 * Copyright (C) Microsoft. All rights reserved.
3 * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4 *
5 * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6 *
7 * Basic tests for PPLX operations
8 *
9 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
10 ****/
11 #include "stdafx.h"
12
13 using namespace ::pplx;
14 using namespace ::tests::common::utilities;
15
16 namespace tests
17 {
18 namespace functional
19 {
20 namespace PPLX
21 {
IsTrue(bool condition,const wchar_t *,...)22 static void IsTrue(bool condition, const wchar_t*, ...) { VERIFY_IS_TRUE(condition); }
23
IsFalse(bool condition,...)24 static void IsFalse(bool condition, ...) { VERIFY_IS_TRUE(condition == false); }
25
LogFailure(const wchar_t * msg,...)26 static void LogFailure(const wchar_t* msg, ...)
27 {
28 wprintf(L"%s", msg);
29 VERIFY_IS_TRUE(false);
30 }
31
32 namespace helpers
33 {
FibSerial(int n)34 static int FibSerial(int n)
35 {
36 if (n < 2) return n;
37
38 return FibSerial(n - 1) + FibSerial(n - 2);
39 }
40
DoRandomParallelWork()41 static void DoRandomParallelWork()
42 {
43 int param = (rand() % 8) + 20;
44 // Calculate fib in serial
45 volatile int val = FibSerial(param);
46 val;
47 }
48
49 template<typename _EX, typename _T>
VerifyException(task<_T> & task)50 bool VerifyException(task<_T>& task)
51 {
52 bool gotException = true;
53 bool wrongException = false;
54
55 try
56 {
57 task.get();
58 gotException = false;
59 }
60 catch (const _EX&)
61 {
62 }
63 catch (...)
64 {
65 wrongException = true;
66 }
67
68 return (gotException && !wrongException);
69 }
70
71 template<typename _T>
VerifyNoException(task<_T> & task)72 bool VerifyNoException(task<_T>& task)
73 {
74 try
75 {
76 task.get();
77 }
78 catch (...)
79 {
80 return false;
81 }
82 return true;
83 }
84
85 template<typename _T>
VerifyCanceled(task<_T> & task)86 bool VerifyCanceled(task<_T>& task)
87 {
88 try
89 {
90 task.get();
91 }
92 catch (task_canceled&)
93 {
94 return true;
95 }
96 catch (...)
97 {
98 return false;
99 }
100 return false;
101 }
102
103 template<typename _T>
ObserveException(task<_T> & task)104 void ObserveException(task<_T>& task)
105 {
106 try
107 {
108 task.get();
109 }
110 catch (...)
111 {
112 }
113 }
114
115 template<typename Iter>
ObserveAllExceptions(Iter begin,Iter end)116 void ObserveAllExceptions(Iter begin, Iter end)
117 {
118 typedef typename std::iterator_traits<Iter>::value_type::result_type TaskType;
119 for (auto it = begin; it != end; ++it)
120 {
121 ObserveException(*it);
122 }
123 }
124 } // namespace helpers
125
SUITE(pplxtask_tests)126 SUITE(pplxtask_tests)
127 {
128 TEST(TestCancellationTokenRegression)
129 {
130 for (int i = 0; i < 500; i++)
131 {
132 task_completion_event<void> tce;
133 task<void> starter(tce);
134
135 cancellation_token_source ct;
136
137 task<int> t1 = starter.then([]() -> int { return 47; }, ct.get_token());
138
139 task<int> t2([]() -> int { return 82; });
140
141 task<int> t3([]() -> int { return 147; });
142
143 auto t4 = (t1 && t2 && t3).then([=](std::vector<int> vec) -> int { return vec[0] + vec[1] + vec[3]; });
144
145 ct.cancel();
146
147 tce.set();
148 // this should not hang
149 task_status t4Status = t4.wait();
150 IsTrue(t4Status == canceled,
151 L"operator && did not properly cancel. Expected: %d, Actual: %d",
152 canceled,
153 t4Status);
154 }
155 }
156 TEST(TestTasks_basic)
157 {
158 {
159 task<int> t1([]() -> int { return 47; });
160
161 auto t2 = t1.then([=](int i) -> float {
162 IsTrue(i == 47,
163 L"Continuation did not recieve the correct value from ancestor. Expected: 47, Actual: %d",
164 i);
165 return (float)i / 2;
166 });
167
168 float t2Result = t2.get();
169 IsTrue(t2Result == 23.5,
170 L"Continuation task did not produce the correct result. Expected: 23.5, Actual: %f",
171 t2Result);
172
173 task_status t2Status = t2.wait();
174 IsTrue(t2Status == completed,
175 L"Continuation task was not in completed state. Expected: %d, Actual: %d",
176 completed,
177 t2Status);
178
179 task<int> t3([]() -> int { return 0; });
180
181 IsTrue(t1 == t1, L"task operator== resulted false on equivalent tasks");
182 IsFalse(t1 != t1, L"task operator!= resulted true on equivalent tasks");
183 IsFalse(t1 == t3, L"task operator== resulted true on different tasks");
184 IsTrue(t1 != t3, L"task operator!= resulted false on different tasks");
185
186 t3.wait();
187 }
188 }
189
190 TEST(TestTasks_default_construction)
191 {
192 // Test that default constructed task<T> properly throw exceptions
193 {
194 task<int> t1;
195
196 try
197 {
198 t1.wait();
199 LogFailure(L"t1.wait() should have thrown an exception");
200 }
201 catch (invalid_operation)
202 {
203 }
204
205 try
206 {
207 t1.get();
208 LogFailure(L"t1.get() should have thrown an exception");
209 }
210 catch (invalid_operation)
211 {
212 }
213
214 try
215 {
216 t1.then([](int i) { return i; });
217
218 LogFailure(L"t1.then() should have thrown an exception");
219 }
220 catch (invalid_operation)
221 {
222 }
223 }
224 }
225
226 TEST(TestTasks_void_tasks)
227 {
228 // Test void tasks
229 {
230 int value = 0;
231 task<void> t1([&value]() { value = 147; });
232
233 auto t2 = t1.then([&]() {
234 IsTrue(value == 147,
235 L"void continuation did not recieve the correct value from ancestor. Expected: 147, Actual: %d",
236 value);
237 value++;
238 });
239
240 IsTrue(t2.wait() == completed, L"void task was not in completed state.");
241
242 IsTrue(value == 148, L"void tasks did not properly execute. Expected: 148, Actual: %d", value);
243
244 task<void> t3([]() {});
245
246 IsTrue(t1 == t1, L"task operator== resulted false on equivalent tasks");
247 IsFalse(t1 != t1, L"task operator!= resulted true on equivalent tasks");
248 IsFalse(t1 == t3, L"task operator== resulted true on different tasks");
249 IsTrue(t1 != t3, L"task operator!= resulted false on different tasks");
250 }
251 }
252
253 TEST(TestTasks_void_tasks_default_construction)
254 {
255 // Test that default constructed task<void> properly throw exceptions
256 {
257 task<void> t1;
258
259 try
260 {
261 t1.wait();
262 LogFailure(L"t1.wait() should have thrown an exception");
263 }
264 catch (invalid_operation)
265 {
266 }
267
268 try
269 {
270 t1.get();
271 LogFailure(L"t1.get() should have thrown an exception");
272 }
273 catch (invalid_operation)
274 {
275 }
276
277 try
278 {
279 t1.then([]() {});
280 LogFailure(L"t1.contiue_with() should have thrown an exception");
281 }
282 catch (invalid_operation)
283 {
284 }
285 }
286 }
287
288 TEST(TestTasks_movable_then)
289 {
290 #ifndef _MSC_VER
291 // create movable only type
292 struct A
293 {
294 A() = default;
295 A(A&&) = default;
296 A& operator=(A&&) = default;
297
298 // explicitly delete copy functions
299 A(const A&) = delete;
300 A& operator=(const A&) = delete;
301
302 char operator()(int) { return 'c'; }
303 } a;
304
305 task<int> task = create_task([] { return 2; });
306 auto f = task.then(std::move(a));
307
308 IsTrue(f.get() == 'c', L".then should be able to work with movable functors");
309 #endif // _MSC_VER
310 }
311
312 TEST(TestTasks_constant_this)
313 {
314 #ifdef _MSC_VER
315 #if _MSC_VER < 1700
316 // Dev10 compiler gives an error => .then(func) where func = int!
317 #else
318 {
319 // Test constant 'this' pointer in member functions then(), wait() and get(),
320 // so that they can be used in Lambda.
321 task<int> t1([]() -> int { return 0; });
322
323 auto func = [t1]() -> int {
324 t1.then([](int last) -> int { return last; });
325 t1.wait();
326 return t1.get();
327 };
328
329 IsTrue(func() == 0, L"Tasks should be able to used inside a Lambda.");
330 }
331 #endif // _MSC_VER < 1700
332 #endif // _MSC_VER
333 }
334
335 TEST(TestTasks_fire_and_forget)
336 {
337 // Test Fire-and-forget behavior
338 extensibility::event_t evt;
339 bool flag = false;
340 {
341 task<int> t1([&flag, &evt]() -> int {
342 flag = true;
343 evt.set();
344 return 0;
345 });
346 }
347
348 evt.wait();
349 IsTrue(flag == true, L"Fire-and-forget task did not properly execute.");
350 }
351 TEST(TestTasks_create_task)
352 {
353 // test create task
354 task<int> t1 = create_task([]() -> int { return 4; });
355 IsTrue(t1.get() == 4, L"create_task for simple task did not properly execute.");
356 IsTrue(create_task(t1).get() == 4, L"create_task from a task task did not properly execute.");
357 task<void> t2 = create_task([]() {});
358 task<int> t3 = create_task([]() -> task<int> { return create_task([]() -> int { return 4; }); });
359 IsTrue(t3.get() == 4, L"create_task for task unwrapping did not properly execute.");
360 }
361
362 TEST(TestTaskCompletionEvents_basic)
363 {
364 task_completion_event<int> tce;
365 task<int> completion(tce);
366 auto completion2 = create_task(tce);
367
368 task<void> setEvent([=]() { tce.set(50); });
369
370 int result = completion.get();
371 IsTrue(result == 50, L"Task Completion Event did not get the right result. Expected: 50, Actual: %d", result);
372 IsTrue(completion2.get() == 50,
373 L"create_task didn't construct correct task for task_completion_event, Expected: 50, Actual: %d",
374 result);
375 }
376
377 TEST(TestTaskCompletionEvents_basic2)
378 {
379 task_completion_event<void> tce;
380 task<void> completion(tce);
381 auto completion2 = create_task(tce);
382
383 task<void> setEvent([=]() { tce.set(); });
384
385 // this should not hang, because of the set of tce
386 completion.wait();
387 completion2.wait();
388 }
389
390 TEST(TestTaskCompletionEvents_set_exception_basic)
391 {
392 task_completion_event<void> tce;
393 task<void> t(tce);
394 tce.set_exception(42);
395
396 t.then([=](task<void> p) {
397 try
398 {
399 p.get();
400 IsTrue(false, L"Exception not propagated to task t when calling set_exception.");
401 }
402 catch (int n)
403 {
404 IsTrue(n == 42, L"%ws:%u:bad exception value", __FILE__, __LINE__);
405 }
406 })
407 .wait();
408 }
409
410 TEST(TestTaskCompletionEvents_set_exception_multiple)
411 {
412 task_completion_event<void> tce;
413 task<void> t(tce);
414 tce.set_exception(42);
415
416 t.then([=](task<void> p) {
417 try
418 {
419 p.get();
420 IsTrue(false, L"Exception not propagated to task t's first continuation when calling set_exception.");
421 }
422 catch (int n)
423 {
424 IsTrue(n == 42, L"%ws:%u:bad exception value", __FILE__, __LINE__);
425 }
426 })
427 .wait();
428
429 t.then([=](task<void> p) {
430 try
431 {
432 p.get();
433 IsTrue(false, L"Exception not propagated to task t's second continuation when calling set_exception.");
434 }
435 catch (int n)
436 {
437 IsTrue(n == 42, L"%ws:%u:bad exception value", __FILE__, __LINE__);
438 }
439 })
440 .wait();
441 }
442
443 TEST(TestTaskCompletionEvents_set_exception_struct)
444 {
445 #if defined(_MSC_VER) && _MSC_VER < 1700
446 // The Dev10 compiler hits an ICE with this code
447 #else
448 struct s
449 {
450 };
451
452 task_completion_event<void> tce;
453 task<void> t(tce);
454 tce.set_exception(s());
455 t.then([=](task<void> p) {
456 try
457 {
458 p.get();
459 IsTrue(false, L"Exception not caught.");
460 }
461 catch (s)
462 {
463 // Do nothing
464 }
465 catch (...)
466 {
467 IsTrue(false, L"%ws:%u:not the right exception", __FILE__, __LINE__);
468 }
469 })
470 .wait();
471 #endif // _MSC_VER < 1700
472 }
473
474 TEST(TestTaskCompletionEvents_multiple_tasks)
475 {
476 task_completion_event<void> tce;
477 task<void> t1(tce);
478 task<void> t2(tce);
479 tce.set_exception(1);
480
481 t1.then([=](task<void> p) {
482 try
483 {
484 p.get();
485 IsTrue(false, L"An exception was not thrown when calling t1.get(). An exception was expected.");
486 }
487 catch (int ex)
488 {
489 IsTrue(ex == 1, L"%ws:%u:wrong exception value", __FILE__, __LINE__);
490 }
491 catch (...)
492 {
493 IsTrue(false, L"%ws:%u:not the right exception", __FILE__, __LINE__);
494 }
495 });
496
497 t2.then([=](task<void> p) {
498 try
499 {
500 p.get();
501 IsTrue(false, L"An exception was not thrown when calling t2.get(). An exception was expected.");
502 }
503 catch (int ex)
504 {
505 IsTrue(ex == 1, L"%ws:%u:wrong exception value", __FILE__, __LINE__);
506 }
507 catch (...)
508 {
509 IsTrue(false, L"%ws:%u:not the right exception", __FILE__, __LINE__);
510 }
511 });
512 }
513
514 TEST(TestTaskCompletionEvents_set_exception_after_set)
515 {
516 task_completion_event<int> tce;
517 task<int> t(tce);
518 tce.set(1);
519 auto result = tce.set_exception(std::current_exception());
520 IsFalse(result, L"set_exception must return false, but did not");
521 t.then([=](task<int> p) {
522 try
523 {
524 int n = p.get();
525 IsTrue(n == 1, L"Value not properly propagated to continuation");
526 }
527 catch (...)
528 {
529 IsTrue(false, L"An exception was unexpectedly thrown in the continuation task");
530 }
531 })
532 .wait();
533 }
534
535 TEST(TestTaskCompletionEvents_set_exception_after_set2)
536 {
537 task_completion_event<int> tce;
538 task<int> t(tce);
539 tce.set_exception(1);
540 auto result = tce.set_exception(2);
541 IsFalse(result, L"set_exception must return false, but did not");
542 t.then([=](task<int> p) {
543 try
544 {
545 p.get();
546 IsTrue(false, L"%ws:%u:expected exception not thrown", __FILE__, __LINE__);
547 }
548 catch (int n)
549 {
550 IsTrue(n == 1, L"%ws:%u:unexpected exception payload", __FILE__, __LINE__);
551 }
552 })
553 .wait();
554 }
555
556 TEST(TestTaskCompletionEvents_set_after_set_exception)
557 {
558 task_completion_event<int> tce;
559 task<int> t(tce);
560 tce.set_exception(42);
561 tce.set(1); // should be no-op
562 t.then([=](task<int> p) {
563 try
564 {
565 p.get();
566 IsTrue(false, L"Exception should have been thrown here.");
567 }
568 catch (int e)
569 {
570 IsTrue(e == 42, L"%ws:%u:not the right exception value", __FILE__, __LINE__);
571 }
572 catch (...)
573 {
574 IsTrue(false, L"%ws:%u:not the right exception", __FILE__, __LINE__);
575 }
576 })
577 .wait();
578 }
579
580 TEST(TestTaskOperators_and_or)
581 {
582 task<int> t1([]() -> int { return 47; });
583
584 task<int> t2([]() -> int { return 82; });
585
586 auto t3 = (t1 && t2).then([=](std::vector<int> vec) -> int {
587 IsTrue(vec.size() == 2,
588 L"operator&& did not produce a correct vector size. Expected: 2, Actual: %d",
589 vec.size());
590 IsTrue(vec[0] == 47, L"operator&& did not produce a correct vector[0]. Expected: 47, Actual: %d", vec[0]);
591 IsTrue(vec[1] == 82, L"operator&& did not produce a correct vector[1]. Expected: 82, Actual: %d", vec[1]);
592 return vec[0] + vec[1];
593 });
594
595 int t3Result = t3.get();
596 IsTrue(t3Result == 129,
597 L"operator&& task did not produce the correct result. Expected: 129, Actual: %d",
598 t3Result);
599 }
600
601 TEST(TestTaskOperators_and_or2)
602 {
603 task<int> t1([]() -> int { return 47; });
604
605 task<int> t2([]() -> int { return 82; });
606
607 task<int> t3([]() -> int { return 147; });
608
609 task<int> t4([]() -> int { return 192; });
610
611 auto t5 = (t1 && t2 && t3 && t4).then([=](std::vector<int> vec) -> int {
612 IsTrue(vec.size() == 4,
613 L"operator&& did not produce a correct vector size. Expected: 4, Actual: %d",
614 vec.size());
615 IsTrue(vec[0] == 47, L"operator&& did not produce a correct vector[0]. Expected: 47, Actual: %d", vec[0]);
616 IsTrue(vec[1] == 82, L"operator&& did not produce a correct vector[1]. Expected: 82, Actual: %d", vec[1]);
617 IsTrue(vec[2] == 147, L"operator&& did not produce a correct vector[2]. Expected: 147, Actual: %d", vec[2]);
618 IsTrue(vec[3] == 192, L"operator&& did not produce a correct vector[3]. Expected: 192, Actual: %d", vec[3]);
619 int count = 0;
620 for (unsigned i = 0; i < vec.size(); i++)
621 count += vec[i];
622 return count;
623 });
624
625 int t5Result = t5.get();
626 IsTrue(t5Result == 468,
627 L"operator&& task did not produce the correct result. Expected: 468, Actual: %d",
628 t5Result);
629 }
630
631 TEST(TestTaskOperators_and_or3)
632 {
633 task<int> t1([]() -> int { return 47; });
634
635 task<int> t2([]() -> int { return 82; });
636
637 task<int> t3([]() -> int { return 147; });
638
639 task<int> t4([]() -> int { return 192; });
640
641 auto t5 = ((t1 && t2) && (t3 && t4)).then([=](std::vector<int> vec) -> int {
642 IsTrue(vec.size() == 4,
643 L"operator&& did not produce a correct vector size. Expected: 4, Actual: %d",
644 vec.size());
645 IsTrue(vec[0] == 47, L"operator&& did not produce a correct vector[0]. Expected: 47, Actual: %d", vec[0]);
646 IsTrue(vec[1] == 82, L"operator&& did not produce a correct vector[1]. Expected: 82, Actual: %d", vec[1]);
647 IsTrue(vec[2] == 147, L"operator&& did not produce a correct vector[2]. Expected: 147, Actual: %d", vec[2]);
648 IsTrue(vec[3] == 192, L"operator&& did not produce a correct vector[3]. Expected: 192, Actual: %d", vec[3]);
649 int count = 0;
650 for (unsigned i = 0; i < vec.size(); i++)
651 count += vec[i];
652 return count;
653 });
654
655 int t5Result = t5.get();
656 IsTrue(t5Result == 468,
657 L"operator&& task did not produce the correct result. Expected: 468, Actual: %d",
658 t5Result);
659 }
660
661 TEST(TestTaskOperators_and_or4)
662 {
663 extensibility::event_t evt;
664
665 task<int> t1([&evt]() -> int {
666 evt.wait();
667 return 47;
668 });
669
670 task<int> t2([]() -> int { return 82; });
671
672 auto t3 = (t1 || t2).then([=](int p) -> int {
673 IsTrue(p == 82, L"operator|| did not get the right result. Expected: 82, Actual: %d", p);
674 return p;
675 });
676
677 t3.wait();
678
679 evt.set();
680 t1.wait();
681 }
682
683 TEST(TestTaskOperators_and_or5)
684 {
685 extensibility::event_t evt;
686
687 task<int> t1([&evt]() -> int {
688 evt.wait();
689 return 47;
690 });
691
692 task<int> t2([&evt]() -> int {
693 evt.wait();
694 return 82;
695 });
696
697 task<int> t3([]() -> int { return 147; });
698
699 task<int> t4([&evt]() -> int {
700 evt.wait();
701 return 192;
702 });
703
704 auto t5 = (t1 || t2 || t3 || t4).then([=](int result) -> int {
705 IsTrue(result == 147, L"operator|| did not produce a correct result. Expected: 147, Actual: %d", result);
706 return result;
707 });
708
709 t5.wait();
710
711 evt.set();
712 t1.wait();
713 t2.wait();
714 t4.wait();
715 }
716
717 TEST(TestTaskOperators_and_or_sequence)
718 {
719 // testing ( t1 && t2 ) || t3, operator&& finishes first
720 extensibility::event_t evt;
721
722 task<int> t1([]() -> int { return 47; });
723
724 task<int> t2([]() -> int { return 82; });
725
726 task<int> t3([&evt]() -> int {
727 evt.wait();
728 return 147;
729 });
730
731 auto t4 = ((t1 && t2) || t3).then([=](std::vector<int> vec) -> int {
732 IsTrue(vec.size() == 2,
733 L"(t1 && t2) || t3 did not produce a correct vector size. Expected: 2, Actual: %d",
734 vec.size());
735 IsTrue(vec[0] == 47,
736 L"(t1 && t2) || t3 did not produce a correct vector[0]. Expected: 47, Actual: %d",
737 vec[0]);
738 IsTrue(vec[1] == 82,
739 L"(t1 && t2) || t3 did not produce a correct vector[1]. Expected: 82, Actual: %d",
740 vec[1]);
741 return vec[0] + vec[1];
742 });
743
744 int t4Result = t4.get();
745 IsTrue(t4.get() == 129,
746 L"(t1 && t2) || t3 task did not produce the correct result. Expected: 129, Actual: %d",
747 t4Result);
748
749 evt.set();
750 t3.wait();
751 }
752
753 TEST(TestTaskOperators_and_or_sequence2)
754 {
755 // testing ( t1 && t2 ) || t3, operator|| finishes first
756 extensibility::event_t evt;
757
758 task<int> t1([&evt]() -> int {
759 evt.wait();
760 return 47;
761 });
762
763 task<int> t2([&evt]() -> int {
764 evt.wait();
765 return 82;
766 });
767
768 task<int> t3([]() -> int { return 147; });
769
770 auto t4 = ((t1 && t2) || t3).then([=](std::vector<int> vec) -> int {
771 IsTrue(vec.size() == 1,
772 L"(t1 && t2) || t3 did not produce a correct vector size. Expected: 1, Actual: %d",
773 vec.size());
774 IsTrue(vec[0] == 147,
775 L"(t1 && t2) || t3 did not produce a correct vector[0]. Expected: 147, Actual: %d",
776 vec[0]);
777 return vec[0];
778 });
779
780 int t4Result = t4.get();
781 IsTrue(t4.get() == 147,
782 L"(t1 && t2) || t3 task did not produce the correct result. Expected: 147, Actual: %d",
783 t4Result);
784
785 evt.set();
786 t1.wait();
787 t2.wait();
788 }
789
790 TEST(TestTaskOperators_and_or_sequence3)
791 {
792 // testing t1 && (t2 || t3)
793 extensibility::event_t evt;
794
795 task<int> t1([]() -> int { return 47; });
796
797 task<int> t2([&evt]() -> int {
798 evt.wait();
799 return 82;
800 });
801
802 task<int> t3([]() -> int { return 147; });
803
804 auto t4 = (t1 && (t2 || t3)).then([=](std::vector<int> vec) -> int {
805 IsTrue(vec.size() == 2,
806 L"t1 && (t2 || t3) did not produce a correct vector size. Expected: 2, Actual: %d",
807 vec.size());
808 IsTrue(vec[0] == 47,
809 L"t1 && (t2 || t3) did not produce a correct vector[0]. Expected: 47, Actual: %d",
810 vec[0]);
811 IsTrue(vec[1] == 147,
812 L"t1 && (t2 || t3) did not produce a correct vector[1]. Expected: 147, Actual: %d",
813 vec[1]);
814 return vec[0] + vec[1];
815 });
816
817 int t4Result = t4.get();
818 IsTrue(t4.get() == 194,
819 L"t1 && (t2 || t3) task did not produce the correct result. Expected: 194 Actual: %d",
820 t4Result);
821
822 evt.set();
823 t2.wait();
824 }
825
826 TEST(TestTaskOperators_cancellation)
827 {
828 task_completion_event<void> tce;
829 task<void> starter(tce);
830
831 cancellation_token_source ct;
832
833 task<int> t1 = starter.then([]() -> int { return 47; }, ct.get_token());
834
835 task<int> t2([]() -> int { return 82; });
836
837 task<int> t3([]() -> int { return 147; });
838
839 auto t4 = (t1 && t2 && t3).then([=](std::vector<int> vec) -> int { return vec[0] + vec[1] + vec[3]; });
840
841 ct.cancel();
842
843 tce.set();
844 // this should not hang
845 task_status t4Status = t4.wait();
846 IsTrue(
847 t4Status == canceled, L"operator && did not properly cancel. Expected: %d, Actual: %d", canceled, t4Status);
848 }
849
850 TEST(TestTaskOperators_cancellation_and)
851 {
852 task_completion_event<void> tce;
853 task<void> starter(tce);
854
855 cancellation_token_source ct;
856
857 task<void> t1 = starter.then([]() -> void {}, ct.get_token());
858
859 task<void> t2([]() -> void {});
860
861 task<void> t3([]() -> void {});
862
863 auto t4 = (t1 && t2 && t3).then([=]() {});
864
865 ct.cancel();
866
867 tce.set();
868 // this should not hang
869 task_status t4Status = t4.wait();
870 IsTrue(
871 t4Status == canceled, L"operator && did not properly cancel. Expected: %d, Actual: %d", canceled, t4Status);
872 }
873
874 TEST(TestTaskOperators_cancellation_or)
875 {
876 task_completion_event<void> tce;
877 task<void> starter(tce);
878
879 cancellation_token_source ct1;
880 cancellation_token_source ct2;
881 cancellation_token_source ct3;
882
883 task<int> t1 = starter.then([]() -> int { return 47; }, ct1.get_token());
884
885 task<int> t2 = starter.then([]() -> int { return 82; }, ct2.get_token());
886
887 task<int> t3 = starter.then([]() -> int { return 147; }, ct3.get_token());
888
889 auto t4 = (t1 || t2 || t3).then([=](int result) -> int { return result; });
890
891 ct1.cancel();
892 ct2.cancel();
893 ct3.cancel();
894
895 tce.set();
896 // this should not hang
897 task_status t4Status = t4.wait();
898 IsTrue(
899 t4Status == canceled, L"operator || did not properly cancel. Expected: %d, Actual: %d", canceled, t4Status);
900 }
901 TEST(TestTaskOperators_cancellation_or2)
902 {
903 task_completion_event<void> tce;
904 task<void> starter(tce);
905
906 cancellation_token_source ct1;
907 cancellation_token_source ct2;
908 cancellation_token_source ct3;
909
910 task<void> t1 = starter.then([]() -> void {}, ct1.get_token());
911
912 task<void> t2 = starter.then([]() -> void {}, ct2.get_token());
913
914 task<void> t3 = starter.then([]() -> void {}, ct3.get_token());
915
916 auto t4 = (t1 || t2 || t3).then([=]() {});
917
918 ct1.cancel();
919 ct2.cancel();
920 ct3.cancel();
921
922 tce.set();
923 // this should not hang
924 task_status t4Status = t4.wait();
925 IsTrue(
926 t4Status == canceled, L"operator || did not properly cancel. Expected: %d, Actual: %d", canceled, t4Status);
927 }
928
929 TEST(TestTaskOperators_cancellation_complex)
930 {
931 extensibility::event_t evt1, evt2;
932 pplx::details::atomic_long n(0);
933
934 cancellation_token_source ct;
935
936 task<void> t1(
937 [&n, &evt1, &evt2]() {
938 pplx::details::atomic_add(n, 1L); // this should execute
939 evt2.set();
940 evt1.wait();
941 },
942 ct.get_token());
943
944 task<void> t2 = t1.then([&n]() {
945 pplx::details::atomic_add(n, 10L); // this should NOT execute
946 });
947
948 task<void> t3 = t1.then([&n]() {
949 pplx::details::atomic_add(n, 100L); // this should NOT execute
950 });
951
952 task<void> t4 = t1.then([&n](task<void> taskResult) {
953 pplx::details::atomic_add(n, 1000L); // this should execute
954 });
955
956 task<void> t5 = t1.then([&n](task<void> taskResult) {
957 try
958 {
959 taskResult.get();
960 pplx::details::atomic_add(n, 10000L); // this should execute
961 }
962 catch (task_canceled&)
963 {
964 pplx::details::atomic_add(n, 100000L); // this should NOT execute
965 }
966 });
967
968 evt2.wait();
969 ct.cancel();
970 evt1.set();
971
972 IsTrue((t2 && t3).wait() == canceled, L"(t1 && t2) was not canceled");
973 IsTrue((t2 || t3 || t4 || t5).wait() == completed, L"(t2 || t3 || t4 || t5) did not complete");
974 IsTrue((t4 && t5).wait() == completed, L"(t4 && t5) did not complete");
975
976 try
977 {
978 t1.get();
979 }
980 catch (task_canceled&)
981 {
982 LogFailure(L"get() on canceled task t1 should not throw a task_canceled exception.");
983 }
984
985 try
986 {
987 t2.get();
988 LogFailure(L"get() on canceled task t2 should throw a task_canceled exception.");
989 }
990 catch (task_canceled&)
991 {
992 }
993
994 try
995 {
996 t3.get();
997 LogFailure(L"get() on canceled task t3 should throw a task_canceled exception.");
998 }
999 catch (task_canceled&)
1000 {
1001 }
1002
1003 try
1004 {
1005 t4.get();
1006 t5.get();
1007 }
1008 catch (...)
1009 {
1010 LogFailure(L"get() on completed tasks threw an exception.");
1011 }
1012 IsTrue(
1013 n == 11001L,
1014 L"The right result was not obtained from the sequence of tasks that executed. Expected: 11001, Actual: %d",
1015 static_cast<long>(n));
1016 }
1017
1018 TEST(TestTaskOperators_cancellation_exception)
1019 {
1020 extensibility::event_t evt1, evt2;
1021 pplx::details::atomic_long n(0);
1022
1023 cancellation_token_source ct;
1024
1025 task<void> t1(
1026 [&n, &evt1, &evt2]() {
1027 evt2.set();
1028 evt1.wait();
1029 },
1030 ct.get_token());
1031
1032 task<void> t2([&n]() { throw 42; });
1033
1034 for (int i = 0; i < 5; ++i)
1035 {
1036 try
1037 {
1038 t2.get();
1039 LogFailure(L"Exception was not received from t2.get()");
1040 }
1041 catch (int x)
1042 {
1043 IsTrue(x == 42, L"Incorrect integer was thrown from t2.get(). Expected: 42, Actual: %d", x);
1044 }
1045 catch (task_canceled&)
1046 {
1047 LogFailure(L"task_canceled was thrown from t2.get() when an integer was expected");
1048 }
1049 }
1050
1051 for (int i = 0; i < 5; ++i)
1052 {
1053 try
1054 {
1055 t2.wait();
1056 LogFailure(L"Exception was not received from t2.wait()");
1057 }
1058 catch (int x)
1059 {
1060 IsTrue(x == 42, L"Incorrect integer was thrown from t2.wait(). Expected: 42, Actual: %d", x);
1061 }
1062 catch (task_canceled&)
1063 {
1064 LogFailure(L"task_canceled was thrown from t2.wait() when an integer was expected");
1065 }
1066 }
1067
1068 task<void> t3 = t1.then([&n]() {
1069 pplx::details::atomic_add(n, 1L); // this should NOT execute,
1070 });
1071
1072 task<void> t4 = t1.then([&n](task<void> taskResult) {
1073 pplx::details::atomic_add(n, 10L); // this should execute
1074 });
1075
1076 task<void> t5 = t2.then([&n]() {
1077 pplx::details::atomic_add(n, 100L); // this should NOT execute
1078 });
1079
1080 task<void> t6 = t2.then([&n](task<void> taskResult) {
1081 pplx::details::atomic_add(n, 1000L); // this should execute
1082 taskResult.get(); // should throw 42
1083 pplx::details::atomic_add(n, 10000L); // this should NOT execute
1084 });
1085
1086 task<void> t7 = t2.then([&n, this](task<void> taskResult) {
1087 try
1088 {
1089 taskResult.get();
1090 pplx::details::atomic_add(n, 100000L); // this should NOT execute
1091 }
1092 catch (int x)
1093 {
1094 IsTrue(
1095 x == 42,
1096 L"Incorrect integer exception was received in t7 from taskresult.get(). Expected: 42, Actual: %d",
1097 x);
1098 pplx::details::atomic_add(n, 1000000L); // this should execute
1099 }
1100 catch (task_canceled)
1101 {
1102 LogFailure(L"task_canceled was thrown by taskResult.get() in t7");
1103 }
1104 catch (...)
1105 {
1106 LogFailure(L"A random exception was thrown by taskResult.get() in t7");
1107 }
1108
1109 throw 96;
1110 });
1111
1112 task<void> t8 = (t6 || t7).then([&n, this](task<void> taskResult) {
1113 try
1114 {
1115 taskResult.get();
1116 pplx::details::atomic_add(n, 1000000L); // this should NOT execute
1117 }
1118 catch (int x)
1119 {
1120 IsTrue((x == 42 || x == 96),
1121 L"Incorrect integer exception was received in t7 from taskresult.get(). Expected: 42 or 96, "
1122 L"Actual: %d",
1123 x);
1124 pplx::details::atomic_add(n, 100000000L); // this should execute
1125 }
1126 catch (task_canceled)
1127 {
1128 LogFailure(L"(t6 || t7) was canceled without an exception");
1129 }
1130 catch (...)
1131 {
1132 LogFailure(L"(t6 || t7) was canceled with an unexpected exception");
1133 }
1134 });
1135
1136 // Cancel t1 now that t2 is guaranteed canceled with an exception
1137 evt2.wait();
1138 ct.cancel();
1139 evt1.set();
1140
1141 try
1142 {
1143 task_status status = (t1 && t2).wait();
1144 IsTrue((status == canceled),
1145 L"(t1 && t2).wait() did not return canceled. Expected: %d, Actual %d",
1146 canceled,
1147 status);
1148 }
1149 catch (int x)
1150 {
1151 IsTrue(x == 42,
1152 L"Incorrect integer exception was received from (t1 && t2).wait(). Expected: 42, Actual: %d",
1153 x);
1154 }
1155
1156 try
1157 {
1158 task_status status = t3.wait();
1159 IsTrue((status == canceled),
1160 L"t3.wait() did not returned canceled. Expected: %d, Actual %d",
1161 canceled,
1162 status);
1163 }
1164 catch (task_canceled&)
1165 {
1166 LogFailure(L"t3.wait() threw task_canceled instead of returning canceled");
1167 }
1168 catch (...)
1169 {
1170 LogFailure(L"t3.wait() threw an unexpected exception");
1171 }
1172
1173 try
1174 {
1175 task_status status = t4.wait();
1176 IsTrue((status == completed),
1177 L"t4.wait() did not returned completed. Expected: %d, Actual %d",
1178 completed,
1179 status);
1180 }
1181 catch (...)
1182 {
1183 LogFailure(L"t4.wait() threw an unexpected exception");
1184 }
1185
1186 try
1187 {
1188 t5.wait();
1189 LogFailure(L"t5.wait() did not throw an exception");
1190 }
1191 catch (int x)
1192 {
1193 IsTrue(x == 42, L"Incorrect integer exception was received from t5.wait(). Expected: 42, Actual: %d", x);
1194 }
1195
1196 // Observe the exceptions from t5, t6 and t7
1197 helpers::ObserveException(t5);
1198 helpers::ObserveException(t6);
1199 helpers::ObserveException(t7);
1200
1201 try
1202 {
1203 (t1 || t6).get();
1204 LogFailure(L"(t1 || t6).get() should throw an exception.");
1205 }
1206 catch (task_canceled&)
1207 {
1208 LogFailure(L"(t1 || t6).get() threw task_canceled when an int was expected.");
1209 }
1210 catch (int x)
1211 {
1212 IsTrue(
1213 (x == 42 || x == 96),
1214 L"Incorrect integer exception was received from (t1 || t6 || t7).get(). Expected: 42 or 96, Actual: %d",
1215 x);
1216 }
1217
1218 t8.wait();
1219
1220 IsTrue(n == 101001010L,
1221 L"The right result was not obtained from the sequence of tasks that executed. Expected 101001010, "
1222 L"actual %d",
1223 101001010,
1224 static_cast<long>(n));
1225 }
1226
1227 TEST(TestTaskOperators_when_all_cancellation)
1228 {
1229 // A task that participates in a 'when all' operation is canceled and then throws an exception. Verify that
1230 // value and task based continuations of the when all task see the exception.
1231 extensibility::event_t evt1, evt2;
1232
1233 cancellation_token_source ct;
1234
1235 task<void> t1(
1236 [&evt1, &evt2]() {
1237 evt2.set();
1238 evt1.wait();
1239 os_utilities::sleep(100);
1240 throw 42;
1241 },
1242 ct.get_token());
1243
1244 task<void> t2([]() { helpers::DoRandomParallelWork(); });
1245
1246 task<void> t3([]() { helpers::DoRandomParallelWork(); });
1247
1248 task<void> whenAllTask = t1 && t2 && t3;
1249
1250 task<void> t4 = whenAllTask.then([this](task<void> t) {
1251 IsFalse(helpers::VerifyCanceled(t), L"%ws:%u:t should be canceled by token", __FILE__, __LINE__);
1252 IsTrue(helpers::VerifyException<int>(t), L"%ws:%u:exception from t is unexpected", __FILE__, __LINE__);
1253 });
1254
1255 task<void> t5 =
1256 whenAllTask.then([this]() { LogFailure(L"%ws:%u:t5 was unexpectedly executed", __FILE__, __LINE__); });
1257
1258 evt2.wait();
1259 ct.cancel();
1260 evt1.set();
1261
1262 IsFalse(helpers::VerifyCanceled(t5), L"%ws:%u:t5 should be canceled", __FILE__, __LINE__);
1263 }
1264
1265 TEST(TestTaskOperators_when_all_cancellation_sequence)
1266 {
1267 // A task that participates in a 'when all' operation throws an exception, but a continuation of the when all
1268 // task is canceled before this point. Ensure that continuation does not get the exception but others do.
1269 extensibility::event_t evt1, evt2;
1270
1271 cancellation_token_source ct;
1272
1273 task<void> t1([&evt1, &evt2]() {
1274 evt2.set();
1275 evt1.wait();
1276 os_utilities::sleep(100);
1277 throw 42;
1278 });
1279
1280 task<void> t2([]() { helpers::DoRandomParallelWork(); });
1281
1282 task<void> t3([]() { helpers::DoRandomParallelWork(); });
1283
1284 task<void> whenAllTask = t1 && t2 && t3;
1285
1286 task<void> t4 = whenAllTask.then([this](task<void> t) {
1287 IsFalse(helpers::VerifyCanceled(t), L"%ws:%u:t was unexpectedly canceled", __FILE__, __LINE__);
1288 IsTrue(helpers::VerifyException<int>(t),
1289 L"%ws:%u:Did not receive the correct exception from t",
1290 __FILE__,
1291 __LINE__);
1292 });
1293
1294 task<void> t5 =
1295 whenAllTask.then([this]() { LogFailure(L"%ws:%u:t5 was unexpectedly executed", __FILE__, __LINE__); });
1296
1297 task<void> t6 = whenAllTask.then(
1298 [this](task<void> t) {
1299 IsTrue(helpers::VerifyCanceled(t), L"%ws:%u:t was not canceled as expected", __FILE__, __LINE__);
1300 },
1301 ct.get_token());
1302
1303 evt2.wait();
1304 ct.cancel();
1305 evt1.set();
1306
1307 IsTrue(helpers::VerifyException<int>(t5),
1308 L"%ws:%u:Did not receive the correct exception from t5",
1309 __FILE__,
1310 __LINE__);
1311 }
1312
1313 TEST(TestTaskOperators_and_cancellation_multiple_tokens)
1314 //
1315 // operator&& with differing tokens:
1316 //
1317 {
1318 cancellation_token_source ct1;
1319 cancellation_token_source ct2;
1320 cancellation_token_source ct3;
1321 cancellation_token_source ct4;
1322
1323 task<int> t1([]() -> int { return 42; }, ct1.get_token());
1324
1325 task<int> t2([]() -> int { return 77; }, ct2.get_token());
1326
1327 task<int> t3([]() -> int { return 92; }, ct3.get_token());
1328
1329 task<int> t4([]() -> int { return 147; }, ct4.get_token());
1330
1331 auto t5 = t1 && t2 && t3 && t4;
1332
1333 extensibility::event_t ev1, ev2;
1334
1335 auto t6 = t5.then([&ev1, &ev2](std::vector<int> iVec) -> int {
1336 ev2.set();
1337 ev1.wait();
1338 return iVec[0] + iVec[1] + iVec[2] + iVec[3];
1339 });
1340
1341 auto t7 = t6.then([](int val) -> int { return val; });
1342
1343 ev2.wait();
1344 ct3.cancel();
1345 ev1.set();
1346 t6.wait();
1347 t7.wait();
1348
1349 bool caughtCanceled = false;
1350
1351 try
1352 {
1353 t7.get();
1354 }
1355 catch (task_canceled&)
1356 {
1357 caughtCanceled = true;
1358 }
1359
1360 IsTrue(caughtCanceled, L"Cancellation token was not joined/inherited on operator&&");
1361 }
1362
1363 struct TestException1
1364 {
1365 };
1366
1367 struct TestException2
1368 {
1369 };
1370
1371 // CodePlex 292
1372 static int ThrowFunc() { throw 42; }
1373
1374 TEST(TestContinuationsWithTask1)
1375 {
1376 int n2 = 0;
1377
1378 task<int> t([&]() -> int { return 10; });
1379
1380 t.then([&](task<int> ti) { n2 = ti.get(); }).wait();
1381
1382 VERIFY_IS_TRUE(n2 == 10);
1383 }
1384
1385 TEST(TestContinuationsWithTask2)
1386 {
1387 int n = 0;
1388
1389 task<void> tt1([]() {});
1390 auto tt2 = tt1.then([&]() -> task<void> {
1391 task<void> tt3([&]() { n = 1; });
1392 return tt3;
1393 });
1394
1395 tt2.get();
1396 VERIFY_IS_TRUE(n == 1);
1397
1398 task<void> tt4 = tt2.then([&]() -> task<void> {
1399 task<void> tt5([&]() { n = 2; });
1400 return tt5;
1401 });
1402 tt4.get();
1403 VERIFY_IS_TRUE(n == 2);
1404 }
1405
1406 TEST(TestContinuationsWithTask3)
1407 {
1408 bool gotException = true;
1409 int n2 = 0;
1410 task<int> t(ThrowFunc);
1411 t.then([&](task<int> ti) {
1412 try
1413 {
1414 ti.get();
1415 gotException = false;
1416 }
1417 catch (int)
1418 {
1419 n2 = 20;
1420 }
1421 })
1422 .wait();
1423
1424 VERIFY_IS_TRUE(gotException);
1425 VERIFY_IS_TRUE(n2 == 20);
1426 }
1427
1428 TEST(TestContinuationsWithTask4)
1429 {
1430 int n2 = 0;
1431
1432 task<int> t([&]() -> int { return 10; });
1433
1434 t.then([&](int n) -> task<int> {
1435 task<int> t2([n]() -> int { return n + 10; });
1436 return t2;
1437 })
1438 .then([&](int n) { n2 = n; })
1439 .wait();
1440
1441 VERIFY_IS_TRUE(n2 == 20);
1442 }
1443
1444 TEST(TestContinuationsWithTask5)
1445 {
1446 int n2 = 0;
1447
1448 task<int> t([&]() -> int { return 10; });
1449
1450 t.then([&](task<int> tn) -> task<int> {
1451 int n = tn.get();
1452 task<int> t2([n]() -> int { return n + 10; });
1453 return t2;
1454 })
1455 .then([&](task<int> n) { n2 = n.get(); })
1456 .wait();
1457
1458 VERIFY_IS_TRUE(n2 == 20);
1459 }
1460
1461 TEST(TestContinuationsWithTask6)
1462 {
1463 pplx::details::atomic_long hit(0);
1464 auto* hitptr = &hit;
1465 task<int> t([]() { return 10; });
1466
1467 auto ot = t.then([hitptr](int n) -> task<int> {
1468 auto hitptr1 = hitptr;
1469 task<int> it([n, hitptr1]() -> int {
1470 os_utilities::sleep(100);
1471 pplx::details::atomic_exchange(*hitptr1, 1L);
1472 return n * 2;
1473 });
1474
1475 return it;
1476 });
1477
1478 int value = ot.get();
1479 VERIFY_IS_TRUE(value == 20 && hit != 0);
1480 }
1481
1482 TEST(TestContinuationsWithTask7)
1483 {
1484 volatile long hit = 0;
1485 volatile long* hitptr = &hit;
1486
1487 task<int> t([]() { return 10; });
1488
1489 auto ot = t.then([hitptr](int n) -> task<int> {
1490 task<int> it([n, hitptr]() -> int { throw TestException1(); });
1491
1492 return it;
1493 });
1494
1495 VERIFY_IS_TRUE(helpers::VerifyException<TestException1>(ot));
1496 }
1497
1498 TEST(TestContinuationsWithTask8)
1499 {
1500 volatile long hit = 0;
1501 volatile long* hitptr = &hit;
1502
1503 task<int> t([]() { return 10; });
1504
1505 auto ot = t.then([hitptr](int n) -> task<int> {
1506 volatile long* hitptr1 = hitptr;
1507 task<int> it([n, hitptr1]() -> int {
1508 os_utilities::sleep(100);
1509 os_utilities::interlocked_exchange(hitptr1, 1);
1510
1511 // This test is needed to disable an optimizer dead-code check that
1512 // winds up generating errors in VS 2010.
1513 if (n == 10) throw TestException2();
1514
1515 return n * 3;
1516 });
1517
1518 return it;
1519 });
1520
1521 VERIFY_IS_TRUE(helpers::VerifyException<TestException2>(ot),
1522 "(7) Inner task exception not propagated out of outer .get()");
1523 VERIFY_IS_TRUE(hit != 0, "(7) Expected inner task hit marker to be set!");
1524 }
1525
1526 TEST(TestContinuationsWithTask9)
1527 {
1528 volatile long hit = 0;
1529 volatile long* hitptr = &hit;
1530 extensibility::event_t e;
1531 task<int> it;
1532
1533 task<int> t([]() { return 10; });
1534
1535 auto ot = t.then([hitptr, &it, &e](int n) -> task<int> {
1536 volatile long* hitptr1 = hitptr;
1537 it = task<int>([hitptr1, n]() -> int {
1538 os_utilities::interlocked_exchange(hitptr1, 1);
1539 // This test is needed to disable an optimizer dead-code check that
1540 // winds up generating errors in VS 2010.
1541 if (n == 10) throw TestException1();
1542 return n * 5;
1543 });
1544
1545 e.set();
1546 os_utilities::sleep(100);
1547 // This test is needed to disable an optimizer dead-code check that
1548 // winds up generating errors in VS 2010.
1549 if (n == 10) throw TestException2();
1550 return it;
1551 });
1552
1553 e.wait();
1554
1555 VERIFY_IS_TRUE(helpers::VerifyException<TestException2>(ot),
1556 "(8) Outer task exception not propagated when inner task also throws");
1557 VERIFY_IS_TRUE(helpers::VerifyException<TestException1>(it),
1558 "(8) Inner task exception not explicitly propgated on pass out / get");
1559 VERIFY_IS_TRUE(hit != 0, "(8) Inner hit marker expected!");
1560 }
1561
1562 TEST(TestContinuationsWithTask10)
1563 {
1564 volatile long hit = 0;
1565
1566 task<int> t([]() { return 10; });
1567
1568 auto ot = t.then([&](int n) -> task<int> {
1569 task<int> it([&, n]() -> int {
1570 os_utilities::sleep(100);
1571 // This test is needed to disable an optimizer dead-code check that
1572 // winds up generating errors in VS 2010.
1573 if (n == 10) throw TestException1();
1574 return n * 6;
1575 });
1576 return it;
1577 });
1578
1579 auto otc = ot.then([&](task<int> itp) {
1580 os_utilities::interlocked_exchange(&hit, 1);
1581 VERIFY_IS_TRUE(helpers::VerifyException<TestException1>(itp),
1582 "(9) Outer task exception handling continuation did not get plumbed inner exception");
1583 });
1584
1585 VERIFY_IS_TRUE(helpers::VerifyException<TestException1>(ot),
1586 "(9) Inner task exception not propagated correctly");
1587 helpers::ObserveException(otc);
1588 VERIFY_IS_TRUE(hit != 0, "(9) Outer task exception handling continuation did not run!");
1589 }
1590
1591 TEST(TestUnwrappingCtors)
1592 {
1593 int res;
1594 {
1595 // take task<int> in the ctor
1596
1597 task<int> ti([]() -> int { return 1; });
1598
1599 // Must unwrap:
1600 task<int> t1(ti);
1601 res = t1.get();
1602 VERIFY_IS_TRUE(res == 1, "unexpected value in TestUnwrappingCtors, location 1");
1603 }
1604
1605 {
1606 // take lambda returning task<int> in the ctor
1607
1608 // Must NOT unwrap:
1609 task<task<int>> t1([]() -> task<int> {
1610 task<int> ti([]() -> int { return 1; });
1611 return ti;
1612 });
1613 res = t1.get().get();
1614 VERIFY_IS_TRUE(res == 1, "unexpected value in TestUnwrappingCtors, location 2");
1615
1616 // Must unwrap:
1617 task<int> t2([]() -> task<int> {
1618 task<int> ti([]() -> int { return 2; });
1619 return ti;
1620 });
1621 res = t2.get();
1622 VERIFY_IS_TRUE(res == 2, "unexpected value in TestUnwrappingCtors, location 3");
1623
1624 res = t2.then([](int n) { return n + 1; }).get();
1625 VERIFY_IS_TRUE(res == 3, "unexpected value in TestUnwrappingCtors, location 4");
1626 }
1627
1628 {
1629 int executed = 0;
1630 // take task<void> in the ctor
1631 task<void> ti([&]() { executed = 1; });
1632
1633 // Must unwrap:
1634 task<void> t1(ti);
1635 t1.wait();
1636 VERIFY_IS_TRUE(executed == 1, "unexpected value in TestUnwrappingCtors, location 5");
1637 }
1638
1639 {
1640 // take lambda returning task<void> in the ctor
1641
1642 int executed = 0;
1643 int* executedPtr = &executed;
1644
1645 // Must NOT unwrap:
1646 task<task<void>> t1([executedPtr]() -> task<void> {
1647 auto executedPtr1 = executedPtr;
1648 task<void> ti([executedPtr1]() { *executedPtr1 = 1; });
1649 return ti;
1650 });
1651 t1.get().get();
1652 VERIFY_IS_TRUE(executed == 1, "unexpected value in TestUnwrappingCtors, location 6");
1653
1654 task<void> t2([]() {});
1655 // Must unwrap:
1656 task<void> t3 = t2.then([executedPtr]() -> task<void> {
1657 auto executedPtr1 = executedPtr;
1658 task<void> ti([executedPtr1]() { *executedPtr1 = 2; });
1659 return ti;
1660 });
1661
1662 t3.wait();
1663 VERIFY_IS_TRUE(executed == 2, "unexpected value in TestUnwrappingCtors, location 7");
1664
1665 // Must unwrap:
1666 task<void> t4([executedPtr]() -> task<void> {
1667 auto executedPtr1 = executedPtr;
1668 task<void> ti([executedPtr1]() { *executedPtr1 = 3; });
1669 return ti;
1670 });
1671 t4.wait();
1672 VERIFY_IS_TRUE(executed == 3, "unexpected value in TestUnwrappingCtors, location 8");
1673
1674 t4.then([&]() { executed++; }).wait();
1675 VERIFY_IS_TRUE(executed == 4, "unexpected value in TestUnwrappingCtors, location 9");
1676 }
1677
1678 {
1679 res = create_task([]() -> task<int> { return create_task([]() -> int { return 1; }); }).get();
1680 VERIFY_IS_TRUE(res == 1, "unexpected value in TestUnwrappingCtors, create_task, location 1");
1681
1682 create_task([]() -> task<void> { return create_task([]() {}); }).wait();
1683 }
1684
1685 {
1686 // BUG TFS: 344954
1687 cancellation_token_source cts, cts2;
1688 cts.cancel(); // Commenting this line out makes the program work!
1689 // Create a task that is always cancelled
1690 auto falseTask = create_task([]() {}, cts.get_token());
1691 cancellation_token ct2 = cts2.get_token();
1692 create_task(
1693 [falseTask]() {
1694 // Task unwrapping!
1695 // This should not crash
1696 return falseTask;
1697 },
1698 ct2)
1699 .then([this, falseTask, ct2](task<void> t) -> task<void> {
1700 VERIFY_IS_TRUE(t.wait() == canceled,
1701 "unexpected value in TestUnwrappingCtors, cancellation token, location 1");
1702 VERIFY_IS_TRUE(!ct2.is_canceled(),
1703 "unexpected value in TestUnwrappingCtors, cancellation token, location 2");
1704 // again, unwrapping in continuation
1705 // this should not crash
1706 return falseTask;
1707 })
1708 .then([this] {
1709 VERIFY_IS_TRUE(false, "unexpected path in TestUnwrappingCtors, cancellation token, location 3");
1710 });
1711 }
1712 }
1713
1714 TEST(TestNestedTasks)
1715 {
1716 {
1717 task<int> rootTask([]() -> int { return 234; });
1718
1719 task<task<int>> resultTask = rootTask.then([](int value) -> task<task<int>> {
1720 return task<task<int>>([=]() -> task<int> {
1721 auto val1 = value;
1722 return task<int>([=]() -> int { return val1 + 22; });
1723 });
1724 });
1725
1726 int n = resultTask.get().get();
1727 VERIFY_IS_TRUE(n == 256, "TestNestedTasks_1");
1728 }
1729
1730 {
1731 // Same for void task
1732 int flag = 1;
1733 int* flagptr = &flag;
1734 task<void> rootTask([&]() { flag++; });
1735
1736 task<task<void>> resultTask = rootTask.then([flagptr]() -> task<task<void>> {
1737 auto flag1 = flagptr;
1738 return task<task<void>>([flag1]() -> task<void> {
1739 auto flag2 = flag1;
1740 return task<void>([flag2]() { ++(flag2[0]); });
1741 });
1742 });
1743
1744 resultTask.get().wait();
1745 VERIFY_IS_TRUE(flag == 3, "TestNestedTasks_2");
1746 }
1747
1748 {
1749 task<int> rootTask([]() -> int { return 234; });
1750
1751 task<task<task<int>>> resultTask = rootTask.then([](int value) -> task<task<task<int>>> {
1752 return task<task<task<int>>>([=]() -> task<task<int>> {
1753 auto v1 = value;
1754 return task<task<int>>([=]() -> task<int> {
1755 auto v2 = v1;
1756 return task<int>([=]() -> int { return v2 + 22; });
1757 });
1758 });
1759 });
1760
1761 int n = resultTask.get().get().get();
1762 VERIFY_IS_TRUE(n == 256, "TestNestedTasks_3");
1763 }
1764
1765 {
1766 task<void> nestedTask;
1767 task<void> unwrap([&]() -> task<void> {
1768 nestedTask = task<void>([]() { cancel_current_task(); });
1769 return nestedTask;
1770 });
1771 task_status st = unwrap.wait();
1772 VERIFY_IS_TRUE(st == canceled, "TestNestedTasks_4");
1773 st = nestedTask.wait();
1774 VERIFY_IS_TRUE(st == canceled, "TestNestedTasks_5 ");
1775 }
1776 }
1777
1778 template<typename Function>
1779 task<void> async_for(int start, int step, int end, Function func)
1780 {
1781 if (start < end)
1782 {
1783 return func(start).then([=]() -> task<void> { return async_for(start + step, step, end, func); });
1784 }
1785 else
1786 {
1787 return task<void>([] {});
1788 }
1789 }
1790
1791 TEST(TestInlineChunker)
1792 {
1793 const int numiter = 1000;
1794 volatile int sum = 0;
1795 async_for(0,
1796 1,
1797 numiter,
1798 [&](int) -> task<void> {
1799 sum++;
1800 return create_task([]() {});
1801 })
1802 .wait();
1803
1804 VERIFY_IS_TRUE(sum == numiter, "TestInlineChunker: async_for did not return correct result.");
1805 }
1806
1807 #if defined(_WIN32) && (_MSC_VER >= 1700) && (_MSC_VER < 1800)
1808
1809 TEST(PPL_Conversions_basic)
1810 {
1811 pplx::task<int> t1([] { return 1; });
1812 concurrency::task<int> t2 = pplx::pplx_task_to_concurrency_task(t1);
1813 int n = t2.get();
1814 VERIFY_ARE_EQUAL(n, 1);
1815
1816 pplx::task<int> t3 = pplx::concurrency_task_to_pplx_task(t2);
1817 int n2 = t3.get();
1818 VERIFY_ARE_EQUAL(n2, 1);
1819 }
1820
1821 TEST(PPL_Conversions_Nested)
1822 {
1823 pplx::task<int> t1([] { return 12; });
1824 pplx::task<int> t2 = pplx::concurrency_task_to_pplx_task(pplx::pplx_task_to_concurrency_task(
1825 pplx::concurrency_task_to_pplx_task(pplx::pplx_task_to_concurrency_task(t1))));
1826 int n = t2.get();
1827 VERIFY_ARE_EQUAL(n, 12);
1828 }
1829
1830 TEST(PPL_Conversions_Exceptions)
1831 {
1832 pplx::task<int> t1(ThrowFunc);
1833 concurrency::task<int> t2 = pplx::pplx_task_to_concurrency_task(t1);
1834 try
1835 {
1836 t2.get();
1837 VERIFY_IS_TRUE(false);
1838 }
1839 catch (int m)
1840 {
1841 VERIFY_ARE_EQUAL(m, 42);
1842 }
1843
1844 pplx::task<int> t3 = pplx::concurrency_task_to_pplx_task(t2);
1845 try
1846 {
1847 t3.get();
1848 VERIFY_IS_TRUE(false);
1849 }
1850 catch (int m)
1851 {
1852 VERIFY_ARE_EQUAL(m, 42);
1853 }
1854 }
1855
1856 TEST(PPL_Conversions_Basic_void)
1857 {
1858 pplx::task<void> t1([] {});
1859 concurrency::task<void> t2 = pplx::pplx_task_to_concurrency_task(t1);
1860 t2.get();
1861
1862 pplx::task<void> t3 = pplx::concurrency_task_to_pplx_task(t2);
1863 t3.get();
1864 }
1865
1866 TEST(PPL_Conversions_Exceptions_void)
1867 {
1868 pplx::task<void> t1([]() { throw 3; });
1869 concurrency::task<void> t2 = pplx::pplx_task_to_concurrency_task(t1);
1870 try
1871 {
1872 t2.get();
1873 VERIFY_IS_TRUE(false);
1874 }
1875 catch (int m)
1876 {
1877 VERIFY_ARE_EQUAL(m, 3);
1878 }
1879
1880 pplx::task<void> t3 = pplx::concurrency_task_to_pplx_task(t2);
1881 try
1882 {
1883 t3.get();
1884 VERIFY_IS_TRUE(false);
1885 }
1886 catch (int m)
1887 {
1888 VERIFY_ARE_EQUAL(m, 3);
1889 }
1890 }
1891
1892 #endif
1893
1894 } // SUITE(pplxtask_tests)
1895
1896 } // namespace PPLX
1897 } // namespace functional
1898 } // namespace tests
1899