1+ // -----------------------------------------------------------------------------
2+ // 
3+ //  Source code for MIPT masters course on C++
4+ //  Slides: https://sourceforge.net/projects/cpp-lects-rus
5+ //  Licensed after GNU GPL v3
6+ // 
7+ // -----------------------------------------------------------------------------
8+ // 
9+ //  Test for cancellables
10+ // 
11+ // ----------------------------------------------------------------------------
12+ 
13+ #include  < coroutine> 
14+ 
15+ #include  " gtest/gtest.h" 
16+ 
17+ namespace  {
18+ 
19+ struct  suspend_tunable  {
20+   bool  tune_;
21+   suspend_tunable (bool  tune = true ) : tune_(tune) {}
22+   bool  await_ready () const  noexcept  { return  tune_; }
23+   void  await_suspend (std::coroutine_handle<>) const  noexcept  {}
24+   void  await_resume () const  noexcept  {}
25+ };
26+ 
27+ struct  resumable_cancelable  {
28+   struct  promise_type  {
29+     bool  is_cancelled = false ;
30+     using  coro_handle = std::coroutine_handle<promise_type>;
31+     auto  get_return_object () { return  coro_handle::from_promise (*this ); }
32+     auto  initial_suspend () { return  std::suspend_always (); }
33+     auto  final_suspend () noexcept  { return  std::suspend_always (); }
34+     void  return_void () {}
35+     void  unhandled_exception () { std::terminate (); }
36+ 
37+     auto  await_transform (std::suspend_always) {
38+       if  (is_cancelled)
39+         return  suspend_tunable{true };
40+       return  suspend_tunable{false };
41+     }
42+   };
43+ 
44+   using  coro_handle = std::coroutine_handle<promise_type>;
45+   resumable_cancelable (coro_handle handle) : handle_(handle) { assert (handle); }
46+   resumable_cancelable (resumable_cancelable &) = delete ;
47+   resumable_cancelable (resumable_cancelable &&rhs) : handle_(rhs.handle_) {
48+     rhs.handle_  = nullptr ;
49+   }
50+   void  cancel () {
51+     if  (handle_.done ())
52+       return ;
53+     handle_.promise ().is_cancelled  = true ;
54+     handle_.resume ();
55+   }
56+   bool  resume () {
57+     if  (!handle_.done ())
58+       handle_.resume ();
59+     return  !handle_.done ();
60+   }
61+   ~resumable_cancelable () {
62+     if  (handle_)
63+       handle_.destroy ();
64+   }
65+ 
66+ private: 
67+   coro_handle handle_;
68+ };
69+ 
70+ } //  namespace
71+ 
72+ namespace  {
73+ 
74+ int  G = 0 ;
75+ 
76+ resumable_cancelable foo () {
77+   for  (int  i = 0 ; i < 10 ; ++i) {
78+     G += 1 ;
79+     co_await  std::suspend_always{};
80+   }
81+ }
82+ 
83+ } //  namespace
84+ 
85+ TEST (coroutines, cancellable) {
86+   auto  f = foo ();
87+   EXPECT_EQ (G, 0 );
88+   f.resume ();
89+   EXPECT_EQ (G, 1 );
90+   f.resume ();
91+   EXPECT_EQ (G, 2 );
92+   f.resume ();
93+   EXPECT_EQ (G, 3 );
94+   f.cancel ();
95+   EXPECT_EQ (G, 10 );
96+ }
97+ 
98+ //  crutch for clang on godbolt
99+ #if  defined(NOGTESTMAIN)
100+ int  main (int  argc, char  **argv) {
101+   ::testing::InitGoogleTest (&argc, argv);
102+   return  RUN_ALL_TESTS ();
103+ }
104+ #endif 
0 commit comments