libcopp  2.2.0
callable_promise_test.cpp
Go to the documentation of this file.
1 // Copyright 2023 owent
2 
5 
6 #include <cstdio>
7 #include <cstring>
8 #include <iostream>
9 #include <list>
10 #include <string>
11 
12 #include "frame/test_macros.h"
13 
14 #if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
15 
16 struct callable_promise_test_pending_awaitable {
17  bool await_ready() noexcept { return false; }
18 
19  void await_resume() noexcept { detach(); }
20 
21 # if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
22  template <copp::DerivedPromiseBaseType TPROMISE>
23 # else
24  template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<copp::promise_base_type, TPROMISE>::value>>
25 # endif
26  void await_suspend(LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> caller) noexcept {
27  pending.push_back(caller);
28  current = caller;
29  }
30 
31  void detach() noexcept {
32  if (!current) {
33  return;
34  }
35 
36  for (auto iter = pending.begin(); iter != pending.end(); ++iter) {
37  if (*iter == current) {
38  pending.erase(iter);
39  current = nullptr;
40  break;
41  }
42  }
43  }
44 
45  callable_promise_test_pending_awaitable() {}
46  ~callable_promise_test_pending_awaitable() {
47  // detach when destroyed
48  detach();
49  }
50 
51  LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<> current = nullptr;
52 
53  static std::list<LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<>> pending;
54  static void resume_all() {
55  while (!pending.empty()) {
56  pending.front().resume();
57  }
58  }
59 
60  static void resume_some(int max_count) {
61  while (!pending.empty() && max_count-- > 0) {
62  pending.front().resume();
63  }
64  }
65 };
66 std::list<LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<>> callable_promise_test_pending_awaitable::pending;
67 
68 static copp::callable_future<int> callable_func_int_l1(int inout) {
69  CASE_MSG_INFO() << "callable inner future ready int: " << inout << std::endl;
70  co_return inout;
71 }
72 
73 static copp::callable_future<int> callable_func_int_l2(int inout) {
74  CASE_MSG_INFO() << "callable inner future async int: " << inout << std::endl;
75  co_await callable_promise_test_pending_awaitable();
76  CASE_MSG_INFO() << "callable inner future return int: " << inout << std::endl;
77  co_return inout;
78 }
79 
80 static copp::callable_future<int> callable_func_await_int() {
81  auto v = callable_func_int_l1(3);
82  auto u = callable_func_int_l2(11);
83  CASE_MSG_INFO() << "callable await int" << std::endl;
84  int x = (co_await v + co_await u);
85  CASE_MSG_INFO() << "callable return int" << std::endl;
86  co_return x;
87 }
88 
89 CASE_TEST(callable_promise, callable_future_integer_need_resume) {
90  copp::callable_future<int> f = callable_func_await_int();
91  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
92 
93  CASE_EXPECT_FALSE(f.is_ready());
94 
95  callable_promise_test_pending_awaitable::resume_all();
96  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
97  CASE_EXPECT_TRUE(f.is_ready());
98  CASE_EXPECT_EQ(14, f.get_internal_promise().data());
99 }
100 
101 static copp::callable_future<int> callable_func_await_int_ready() {
102  auto v = callable_func_int_l1(33);
103  auto u = callable_func_int_l1(31);
104  CASE_MSG_INFO() << "callable await int no wait" << std::endl;
105  int x = (co_await v + co_await u);
106  CASE_MSG_INFO() << "callable return int no wait" << std::endl;
107  co_return x;
108 }
109 
110 CASE_TEST(callable_promise, callable_future_integer_ready) {
111  copp::callable_future<int> f = callable_func_await_int_ready();
112 
113  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
114  CASE_EXPECT_TRUE(f.is_ready());
115  CASE_EXPECT_EQ(64, f.get_internal_promise().data());
116 }
117 
118 static copp::callable_future<void> callable_func_void_l1() {
119  CASE_MSG_INFO() << "callable inner future ready void" << std::endl;
120  co_return;
121 }
122 
123 static copp::callable_future<void> callable_func_void_l2() {
124  CASE_MSG_INFO() << "callable inner future async void" << std::endl;
125  co_await callable_promise_test_pending_awaitable();
126  CASE_MSG_INFO() << "callable inner future return void" << std::endl;
127  co_return;
128 }
129 
130 static copp::callable_future<void> callable_func_await_void() {
131  CASE_MSG_INFO() << "callable await void" << std::endl;
132  co_await callable_func_void_l1();
133  co_await callable_func_void_l2();
134  CASE_MSG_INFO() << "callable return void" << std::endl;
135  co_return;
136 }
137 
138 CASE_TEST(callable_promise, callable_future_void_need_resume) {
139  copp::callable_future<void> f = callable_func_await_void();
140  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
141 
142  CASE_EXPECT_FALSE(f.is_ready());
143  callable_promise_test_pending_awaitable::resume_all();
144  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
145  CASE_EXPECT_TRUE(f.is_ready());
146 }
147 
148 static copp::callable_future<void> callable_func_await_void_ready() {
149  CASE_MSG_INFO() << "callable await void no wait" << std::endl;
150  co_await callable_func_void_l1();
151  co_await callable_func_void_l1();
152  CASE_MSG_INFO() << "callable return void no wait" << std::endl;
153  co_return;
154 }
155 
156 CASE_TEST(callable_promise, callable_future_void_ready) {
157  copp::callable_future<void> f = callable_func_await_void_ready();
158  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
159  CASE_EXPECT_TRUE(f.is_ready());
160 
161  callable_promise_test_pending_awaitable::resume_all();
162 }
163 
164 static copp::callable_future<int> callable_func_int_await_void() {
165  CASE_MSG_INFO() << "callable int await void: start" << std::endl;
166  co_await callable_func_void_l1();
167  co_await callable_func_void_l2();
168  auto v = callable_func_int_l1(17);
169  auto u = callable_func_int_l2(23);
170  int x = (co_await v + co_await u);
171  CASE_MSG_INFO() << "callable int await void: return" << std::endl;
172  co_return x;
173 }
174 
175 CASE_TEST(callable_promise, callable_future_int_await_void) {
176  copp::callable_future<int> f = callable_func_int_await_void();
177  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
178 
179  CASE_EXPECT_FALSE(f.is_ready());
180  callable_promise_test_pending_awaitable::resume_all();
181  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kDone), static_cast<int>(f.get_status()));
182  CASE_EXPECT_TRUE(f.is_ready());
183  CASE_EXPECT_EQ(40, f.get_internal_promise().data());
184 }
185 
186 static copp::callable_future<int> callable_func_killed_by_caller_l3() {
187  co_await callable_promise_test_pending_awaitable();
188  auto current_status = co_yield copp::callable_future<int>::yield_status();
189  CASE_EXPECT_TRUE(copp::promise_status::kKilled == current_status);
190 
191  // await again and return immdiately
192  // co_await callable_promise_test_pending_awaitable();
193  co_return -static_cast<int>(current_status);
194 }
195 
196 static copp::callable_future<int> callable_func_killed_by_caller_l2() {
197  int result = co_await callable_func_killed_by_caller_l3();
198  co_return result;
199 }
200 
201 static copp::callable_future<int> callable_func_killed_by_caller_l1() {
202  int result = co_await callable_func_killed_by_caller_l2();
203  co_return result;
204 }
205 
206 CASE_TEST(callable_promise, killed_by_caller_resume_waiting) {
207  copp::callable_future<int> f = callable_func_killed_by_caller_l1();
208  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
209 
210  CASE_EXPECT_FALSE(f.is_ready());
211 
212  // Mock to kill by caller
213  f.kill(copp::promise_status::kKilled, true);
214  CASE_EXPECT_TRUE(f.is_ready());
215  CASE_EXPECT_EQ(f.get_internal_promise().data(), -static_cast<int>(copp::promise_status::kKilled));
216 
217  // cleanup
218  callable_promise_test_pending_awaitable::resume_all();
219  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kKilled), static_cast<int>(f.get_status()));
220 }
221 
222 CASE_TEST(callable_promise, killed_by_caller_drop_generator) {
223  copp::callable_future<int> f = callable_func_killed_by_caller_l2();
224  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kRunning), static_cast<int>(f.get_status()));
225 
226  CASE_EXPECT_FALSE(f.is_ready());
227 
228  // Mock to kill by caller
229  f.kill(copp::promise_status::kKilled, true);
230  CASE_EXPECT_TRUE(f.is_ready());
231  CASE_EXPECT_EQ(f.get_internal_promise().data(), -static_cast<int>(copp::promise_status::kKilled));
232 
233  // cleanup
234  callable_promise_test_pending_awaitable::resume_all();
235  CASE_EXPECT_EQ(static_cast<int>(copp::promise_status::kKilled), static_cast<int>(f.get_status()));
236 }
237 
238 static copp::callable_future<int> callable_func_some_any_all_callable_suspend(int value) {
239  co_await callable_promise_test_pending_awaitable();
240  co_return value;
241 }
242 
243 static copp::callable_future<int> callable_func_some_callable_in_container(size_t expect_ready_count,
244  copp::promise_status expect_status) {
245  size_t resume_ready_count = 0;
246 
247  std::vector<copp::callable_future<int>> callables;
248  callables.emplace_back(callable_func_some_any_all_callable_suspend(471));
249  callables.emplace_back(callable_func_some_any_all_callable_suspend(473));
250  callables.emplace_back(callable_func_some_any_all_callable_suspend(477));
251 
252  copp::some_ready<copp::callable_future<int>>::type readys;
253  auto some_result = co_await copp::some(readys, 2, copp::gsl::make_span(callables));
254  CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(some_result));
255 
256  int result = 1;
257  for (auto &ready_callable : readys) {
258  if (ready_callable->is_ready()) {
259  result += ready_callable->get_internal_promise().data();
260  ++resume_ready_count;
261  }
262  }
263 
264  CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
265 
266  // Nothing happend here if we await the callables again.
267  some_result = co_await copp::some(readys, 2, callables);
268  CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(some_result));
269 
270  // If it's killed, await will trigger suspend and resume again, or it will return directly.
271  CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
272 
273  co_return result;
274 }
275 
276 CASE_TEST(callable_promise, finish_some_in_container) {
277  auto f = callable_func_some_callable_in_container(2, copp::promise_status::kDone);
278 
279  CASE_EXPECT_FALSE(f.is_ready());
280 
281  // partly resume
282  callable_promise_test_pending_awaitable::resume_some(1);
283  CASE_EXPECT_FALSE(f.is_ready());
284  callable_promise_test_pending_awaitable::resume_some(1);
285 
286  CASE_EXPECT_TRUE(f.is_ready());
287  CASE_EXPECT_EQ(945, f.get_internal_promise().data());
288 
289  callable_promise_test_pending_awaitable::resume_all();
290 }
291 
292 CASE_TEST(callable_promise, kill_some_in_container) {
293  auto f = callable_func_some_callable_in_container(0, copp::promise_status::kKilled);
294 
295  CASE_EXPECT_FALSE(f.is_ready());
296 
297  // partly resume
298  f.kill();
299 
300  CASE_EXPECT_TRUE(f.is_ready());
301  CASE_EXPECT_EQ(1, f.get_internal_promise().data());
302 
303  callable_promise_test_pending_awaitable::resume_all();
304 }
305 
306 static copp::callable_future<int> callable_func_some_callable_in_initialize_list(size_t expect_ready_count,
307  copp::promise_status expect_status) {
308  size_t resume_ready_count = 0;
309 
310  copp::callable_future<int> callable1 = callable_func_some_any_all_callable_suspend(471);
311  copp::callable_future<int> callable2 = callable_func_some_any_all_callable_suspend(473);
312  copp::callable_future<int> callable3 = callable_func_some_any_all_callable_suspend(477);
313 
314  copp::some_ready<copp::callable_future<int>>::type readys;
315  std::reference_wrapper<copp::callable_future<int>> pending[] = {callable1, callable2, callable3};
316  auto some_result = co_await copp::some(readys, 2, copp::gsl::make_span(pending));
317  CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(some_result));
318 
319  int result = 1;
320  for (auto &ready_callable : readys) {
321  if (ready_callable->is_ready()) {
322  result += ready_callable->get_internal_promise().data();
323  ++resume_ready_count;
324  }
325  }
326 
327  CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
328 
329  co_return result;
330 }
331 
332 CASE_TEST(callable_promise, finish_some_in_initialize_list) {
333  auto f = callable_func_some_callable_in_initialize_list(2, copp::promise_status::kDone);
334 
335  CASE_EXPECT_FALSE(f.is_ready());
336 
337  // partly resume
338  callable_promise_test_pending_awaitable::resume_some(1);
339  CASE_EXPECT_FALSE(f.is_ready());
340  callable_promise_test_pending_awaitable::resume_some(1);
341 
342  CASE_EXPECT_TRUE(f.is_ready());
343  CASE_EXPECT_EQ(945, f.get_internal_promise().data());
344 
345  callable_promise_test_pending_awaitable::resume_all();
346 }
347 
348 static copp::callable_future<int> callable_func_any_callable_in_container(size_t expect_ready_count,
349  copp::promise_status expect_status) {
350  size_t resume_ready_count = 0;
351 
352  std::vector<copp::callable_future<int>> callables;
353  callables.emplace_back(callable_func_some_any_all_callable_suspend(671));
354  callables.emplace_back(callable_func_some_any_all_callable_suspend(673));
355  callables.emplace_back(callable_func_some_any_all_callable_suspend(677));
356 
357  copp::any_ready<copp::callable_future<int>>::type readys;
358  auto any_result = co_await copp::any(readys, copp::gsl::make_span(&callables[0], &callables[0] + callables.size()));
359  CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(any_result));
360 
361  int result = 1;
362  for (auto &ready_callable : readys) {
363  if (ready_callable->is_ready()) {
364  result += ready_callable->get_internal_promise().data();
365  ++resume_ready_count;
366  }
367  }
368 
369  CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
370 
371  // Nothing happend here if we await the callables again.
372  any_result = co_await copp::any(readys, callables);
373  CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(any_result));
374 
375  // If it's killed, await will trigger suspend and resume again, or it will return directly.
376  CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
377 
378  co_return result;
379 }
380 
381 CASE_TEST(callable_promise, finish_any_in_container) {
382  auto f = callable_func_any_callable_in_container(1, copp::promise_status::kDone);
383 
384  CASE_EXPECT_FALSE(f.is_ready());
385 
386  // partly resume
387  callable_promise_test_pending_awaitable::resume_some(1);
388 
389  CASE_EXPECT_TRUE(f.is_ready());
390  CASE_EXPECT_EQ(672, f.get_internal_promise().data());
391 
392  callable_promise_test_pending_awaitable::resume_all();
393 }
394 
395 static copp::callable_future<int> callable_func_all_callable_in_container(size_t expect_ready_count,
396  copp::promise_status expect_status) {
397  size_t resume_ready_count = 0;
398 
399  std::vector<copp::callable_future<int>> callables;
400  callables.emplace_back(callable_func_some_any_all_callable_suspend(671));
401  callables.emplace_back(callable_func_some_any_all_callable_suspend(791));
402  callables.emplace_back(callable_func_some_any_all_callable_suspend(793));
403 
404  copp::all_ready<copp::callable_future<int>>::type readys;
405  auto all_result = co_await copp::all(readys, copp::gsl::make_span(callables.data(), callables.size()));
406  CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(all_result));
407 
408  int result = 1;
409  for (auto &ready_callable : readys) {
410  if (ready_callable->is_ready()) {
411  result += ready_callable->get_internal_promise().data();
412  ++resume_ready_count;
413  }
414  }
415 
416  CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
417 
418  // Nothing happend here if we await the callables again.
419  all_result = co_await copp::all(readys, callables);
420  CASE_EXPECT_EQ(static_cast<int>(expect_status), static_cast<int>(all_result));
421 
422  // If it's killed, await will trigger suspend and resume again, or it will return directly.
423  CASE_EXPECT_EQ(expect_ready_count, resume_ready_count);
424 
425  co_return result;
426 }
427 
428 CASE_TEST(callable_promise, finish_all_in_container) {
429  auto f = callable_func_all_callable_in_container(3, copp::promise_status::kDone);
430 
431  CASE_EXPECT_FALSE(f.is_ready());
432 
433  // partly resume
434  callable_promise_test_pending_awaitable::resume_some(1);
435  CASE_EXPECT_FALSE(f.is_ready());
436 
437  callable_promise_test_pending_awaitable::resume_all();
438 
439  CASE_EXPECT_TRUE(f.is_ready());
440  CASE_EXPECT_EQ(2256, f.get_internal_promise().data());
441 
442  callable_promise_test_pending_awaitable::resume_all();
443 }
444 
445 #else
446 CASE_TEST(callable_promise, disabled) {}
447 #endif
CASE_TEST(callable_promise, disabled)
#define LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE
Definition: coroutine.h:41
constexpr span< TELEMENT > make_span(TELEMENT *ptr, typename span< TELEMENT >::size_type count)
Definition: span.h:255
#define CASE_MSG_INFO()
Definition: test_macros.h:114
#define CASE_EXPECT_FALSE(c)
Definition: test_macros.h:95
#define CASE_EXPECT_EQ(l, r)
Definition: test_macros.h:96
#define CASE_EXPECT_TRUE(c)
Definition: test_macros.h:94