66.67% Lines (4/6) 100.00% Functions (2/2)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2026 Steve Gerbino 3   // Copyright (c) 2026 Steve Gerbino
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/capy 8   // Official repository: https://github.com/cppalliance/capy
9   // 9   //
10   10  
11   #ifndef BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP 11   #ifndef BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP
12   #define BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP 12   #define BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP
13   13  
14   #include <coroutine> 14   #include <coroutine>
15   #include <boost/capy/ex/io_env.hpp> 15   #include <boost/capy/ex/io_env.hpp>
16   16  
17   #include <type_traits> 17   #include <type_traits>
18   18  
19   namespace boost { 19   namespace boost {
20   namespace capy { 20   namespace capy {
21   namespace detail { 21   namespace detail {
22   22  
23   /** Perform symmetric transfer, working around an MSVC codegen bug. 23   /** Perform symmetric transfer, working around an MSVC codegen bug.
24   24  
25   MSVC stores the `std::coroutine_handle<>` returned from 25   MSVC stores the `std::coroutine_handle<>` returned from
26   `await_suspend` in a hidden `__$ReturnUdt$` variable located 26   `await_suspend` in a hidden `__$ReturnUdt$` variable located
27   on the coroutine frame. When another thread resumes or destroys 27   on the coroutine frame. When another thread resumes or destroys
28   the frame between the store and the read-back for the 28   the frame between the store and the read-back for the
29   symmetric-transfer tail-call, the read hits freed memory. 29   symmetric-transfer tail-call, the read hits freed memory.
30   30  
31   This occurs in two scenarios: 31   This occurs in two scenarios:
32   32  
33   @li `await_suspend` calls `h.destroy()` then returns a handle 33   @li `await_suspend` calls `h.destroy()` then returns a handle
34   (e.g. `when_all_runner` and `when_any_runner` final_suspend). 34   (e.g. `when_all_runner` and `when_any_runner` final_suspend).
35   The return value is written to the now-destroyed frame. 35   The return value is written to the now-destroyed frame.
36   36  
37   @li `await_suspend` hands the continuation to another thread 37   @li `await_suspend` hands the continuation to another thread
38 - via `executor::dispatch()`, which may resume the parent. 38 + via an executor handoff (e.g. `post()` or `dispatch()`),
39 - The parent can destroy this frame before the runtime reads 39 + which may resume the parent. The parent can destroy this
40 - `__$ReturnUdt$` (e.g. `dispatch_trampoline` final_suspend). 40 + frame before the runtime reads `__$ReturnUdt$` (e.g.
  41 + `boundary_trampoline` final_suspend).
41   42  
42   On MSVC this function calls `h.resume()` on the current stack 43   On MSVC this function calls `h.resume()` on the current stack
43   and returns `void`, causing unconditional suspension. The 44   and returns `void`, causing unconditional suspension. The
44   trade-off is O(n) stack growth instead of O(1) tail-calls. 45   trade-off is O(n) stack growth instead of O(1) tail-calls.
45   46  
46   On other compilers the handle is returned directly for proper 47   On other compilers the handle is returned directly for proper
47   symmetric transfer. 48   symmetric transfer.
48   49  
49   Callers must use `auto` return type on their `await_suspend` 50   Callers must use `auto` return type on their `await_suspend`
50   so the return type adapts per platform. 51   so the return type adapts per platform.
51   52  
52   @param h The coroutine handle to transfer to. 53   @param h The coroutine handle to transfer to.
53   */ 54   */
54   #if BOOST_CAPY_WORKAROUND(_MSC_VER, >= 1) 55   #if BOOST_CAPY_WORKAROUND(_MSC_VER, >= 1)
55   inline void symmetric_transfer(std::coroutine_handle<> h) noexcept 56   inline void symmetric_transfer(std::coroutine_handle<> h) noexcept
56   { 57   {
57   // safe_resume is not needed here: the calling coroutine is 58   // safe_resume is not needed here: the calling coroutine is
58   // about to suspend unconditionally. When it later resumes, 59   // about to suspend unconditionally. When it later resumes,
59   // await_resume restores TLS from the promise's environment. 60   // await_resume restores TLS from the promise's environment.
60   h.resume(); 61   h.resume();
61   } 62   }
62   #else 63   #else
63   inline std::coroutine_handle<> 64   inline std::coroutine_handle<>
HITCBC 64   2961 symmetric_transfer(std::coroutine_handle<> h) noexcept 65   2971 symmetric_transfer(std::coroutine_handle<> h) noexcept
65   { 66   {
HITCBC 66   2961 return h; 67   2971 return h;
67   } 68   }
68   #endif 69   #endif
69   70  
70   // Helper to normalize await_suspend return types to std::coroutine_handle<> 71   // Helper to normalize await_suspend return types to std::coroutine_handle<>
71   template<typename Awaitable> 72   template<typename Awaitable>
HITCBC 72   7 std::coroutine_handle<> call_await_suspend( 73   7 std::coroutine_handle<> call_await_suspend(
73   Awaitable* a, 74   Awaitable* a,
74   std::coroutine_handle<> h, 75   std::coroutine_handle<> h,
75   io_env const* env) 76   io_env const* env)
76   { 77   {
77   using R = decltype(a->await_suspend(h, env)); 78   using R = decltype(a->await_suspend(h, env));
78   if constexpr (std::is_void_v<R>) 79   if constexpr (std::is_void_v<R>)
79   { 80   {
MISUBC 80   a->await_suspend(h, env); 81   a->await_suspend(h, env);
MISUBC 81   return std::noop_coroutine(); 82   return std::noop_coroutine();
82   } 83   }
83   else if constexpr (std::is_same_v<R, bool>) 84   else if constexpr (std::is_same_v<R, bool>)
84   { 85   {
85   if(a->await_suspend(h, env)) 86   if(a->await_suspend(h, env))
86   return std::noop_coroutine(); 87   return std::noop_coroutine();
87   return h; 88   return h;
88   } 89   }
89   else 90   else
90   { 91   {
HITCBC 91   7 return a->await_suspend(h, env); 92   7 return a->await_suspend(h, env);
92   } 93   }
93   } 94   }
94   95  
95   } // namespace detail 96   } // namespace detail
96   } // namespace capy 97   } // namespace capy
97   } // namespace boost 98   } // namespace boost
98   99  
99   #endif 100   #endif