@@ -9,7 +9,7 @@ namespace
9
9
// Checks that cancellation propagation works.
10
10
//
11
11
12
- IAsyncAction Action ()
12
+ IAsyncAction Action (handle* wait )
13
13
{
14
14
// Do an extra co_await before the resume_on_signal
15
15
// so that there is a race window where we can try to cancel
@@ -18,11 +18,12 @@ namespace
18
18
19
19
auto cancel = co_await get_cancellation_token ();
20
20
cancel.enable_propagation ();
21
+ SetEvent (wait->get ());
21
22
co_await resume_on_signal (GetCurrentProcess ()); // never wakes
22
23
REQUIRE (false );
23
24
}
24
25
25
- IAsyncActionWithProgress<int > ActionWithProgress ()
26
+ IAsyncActionWithProgress<int > ActionWithProgress (handle* wait )
26
27
{
27
28
// Do an extra co_await before the resume_on_signal
28
29
// so that there is a race window where we can try to cancel
@@ -31,11 +32,12 @@ namespace
31
32
32
33
auto cancel = co_await get_cancellation_token ();
33
34
cancel.enable_propagation ();
35
+ SetEvent (wait->get ());
34
36
co_await resume_on_signal (GetCurrentProcess ()); // never wakes
35
37
REQUIRE (false );
36
38
}
37
39
38
- IAsyncOperation<int > Operation ()
40
+ IAsyncOperation<int > Operation (handle* wait )
39
41
{
40
42
// Do an extra co_await before the resume_on_signal
41
43
// so that there is a race window where we can try to cancel
@@ -44,12 +46,13 @@ namespace
44
46
45
47
auto cancel = co_await get_cancellation_token ();
46
48
cancel.enable_propagation ();
49
+ SetEvent (wait->get ());
47
50
co_await resume_on_signal (GetCurrentProcess ()); // never wakes
48
51
REQUIRE (false );
49
52
co_return 1 ;
50
53
}
51
54
52
- IAsyncOperationWithProgress<int , int > OperationWithProgress ()
55
+ IAsyncOperationWithProgress<int , int > OperationWithProgress (handle* wait )
53
56
{
54
57
// Do an extra co_await before the resume_on_signal
55
58
// so that there is a race window where we can try to cancel
@@ -58,13 +61,14 @@ namespace
58
61
59
62
auto cancel = co_await get_cancellation_token ();
60
63
cancel.enable_propagation ();
64
+ SetEvent (wait->get ());
61
65
co_await resume_on_signal (GetCurrentProcess ()); // never wakes
62
66
REQUIRE (false );
63
67
co_return 1 ;
64
68
}
65
69
66
70
// Checking cancellation propagation for resume_after.
67
- IAsyncAction DelayAction ()
71
+ IAsyncAction DelayAction (handle* wait )
68
72
{
69
73
// Do an extra co_await before the resume_on_signal
70
74
// so that there is a race window where we can try to cancel
@@ -73,6 +77,7 @@ namespace
73
77
74
78
auto cancel = co_await get_cancellation_token ();
75
79
cancel.enable_propagation ();
80
+ SetEvent (wait->get ());
76
81
co_await resume_after (std::chrono::hours (1 )); // effectively sleep forever
77
82
REQUIRE (false );
78
83
}
@@ -83,7 +88,7 @@ namespace
83
88
// carried all the way down, and also lets us verify (via
84
89
// manual debugging) the deep cancellation doesn't cause us to
85
90
// blow up the stack on deeply nested cancellation.
86
- IAsyncAction ActionAction (int depth)
91
+ IAsyncAction ActionAction (handle* wait, int depth)
87
92
{
88
93
// Do an extra co_await before the resume_on_signal
89
94
// so that there is a race window where we can try to cancel
@@ -94,20 +99,21 @@ namespace
94
99
cancel.enable_propagation ();
95
100
if (depth > 0 )
96
101
{
97
- co_await ActionAction (depth - 1 );
102
+ co_await ActionAction (wait, depth - 1 );
98
103
}
99
104
else
100
105
{
101
- co_await Action ();
106
+ co_await Action (wait );
102
107
}
103
108
REQUIRE (false );
104
109
}
105
110
106
111
template <typename F>
107
- void Check (F make)
112
+ void Check (F make, bool should_wait )
108
113
{
109
114
handle completed{ CreateEvent (nullptr , true , false , nullptr ) };
110
- auto async = make ();
115
+ handle wait{ CreateEvent (nullptr , true , false , nullptr ) };
116
+ auto async = make (&wait);
111
117
REQUIRE (async.Status () == AsyncStatus::Started);
112
118
113
119
async.Completed ([&](auto && sender, AsyncStatus status)
@@ -117,28 +123,47 @@ namespace
117
123
SetEvent (completed.get ());
118
124
});
119
125
126
+ if (should_wait)
127
+ {
128
+ REQUIRE (WaitForSingleObject (wait.get (), IsDebuggerPresent () ? INFINITE : 2000 ) == WAIT_OBJECT_0);
129
+ }
120
130
async.Cancel ();
121
131
122
132
// Wait indefinitely if a debugger is present, to make it easier to debug this test.
123
- REQUIRE (WaitForSingleObject (completed.get (), IsDebuggerPresent () ? INFINITE : 1000 ) == WAIT_OBJECT_0);
133
+ REQUIRE (WaitForSingleObject (completed.get (), IsDebuggerPresent () ? INFINITE : 2000 ) == WAIT_OBJECT_0);
124
134
125
135
REQUIRE (async.Status () == AsyncStatus::Canceled);
126
136
REQUIRE (async.ErrorCode () == HRESULT_FROM_WIN32 (ERROR_CANCELLED));
127
137
REQUIRE_THROWS_AS (async.GetResults (), hresult_canceled);
128
138
}
139
+
140
+ struct wait_t
141
+ {
142
+ static constexpr bool value = true ;
143
+ };
144
+
145
+ struct no_wait_t
146
+ {
147
+ static constexpr bool value = false ;
148
+ };
129
149
}
130
150
131
151
#if defined(__clang__) && defined(_MSC_VER)
132
152
// FIXME: Test is known to segfault when built with Clang.
133
- TEST_CASE (" async_propagate_cancel" , " [.clang-crash]" )
153
+ TEMPLATE_TEST_CASE (" async_propagate_cancel" , " [.clang-crash]" , wait_t , no_wait_t )
134
154
#else
135
- TEST_CASE (" async_propagate_cancel" )
155
+ TEMPLATE_TEST_CASE (" async_propagate_cancel" , " " , wait_t , no_wait_t )
136
156
#endif
137
157
{
138
- Check (Action);
139
- Check (ActionWithProgress);
140
- Check (Operation);
141
- Check (OperationWithProgress);
142
- Check (DelayAction);
143
- Check ([] { return ActionAction (10 ); });
158
+ #define CHECK_CASE (a ) \
159
+ SECTION (" CHECK_CASE(" #a " )" ) { \
160
+ Check (a, TestType::value); \
161
+ }
162
+
163
+ CHECK_CASE (Action);
164
+ CHECK_CASE (ActionWithProgress);
165
+ CHECK_CASE (Operation);
166
+ CHECK_CASE (OperationWithProgress);
167
+ CHECK_CASE (DelayAction);
168
+ CHECK_CASE ([](handle* wait) { return ActionAction (wait, 10 ); });
144
169
}
0 commit comments