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 an executor handoff (e.g. `post()` or `dispatch()`), 38   via an executor handoff (e.g. `post()` or `dispatch()`),
39   which may resume the parent. The parent can destroy this 39   which may resume the parent. The parent can destroy this
40   frame before the runtime reads `__$ReturnUdt$` (e.g. 40   frame before the runtime reads `__$ReturnUdt$` (e.g.
41   `boundary_trampoline` final_suspend). 41   `boundary_trampoline` final_suspend).
42   42  
43   On MSVC this function calls `h.resume()` on the current stack 43   On MSVC this function calls `h.resume()` on the current stack
44   and returns `void`, causing unconditional suspension. The 44   and returns `void`, causing unconditional suspension. The
45   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.
46   46  
47   On other compilers the handle is returned directly for proper 47   On other compilers the handle is returned directly for proper
48   symmetric transfer. 48   symmetric transfer.
49   49  
50   Callers must use `auto` return type on their `await_suspend` 50   Callers must use `auto` return type on their `await_suspend`
51   so the return type adapts per platform. 51   so the return type adapts per platform.
52   52  
53   @param h The coroutine handle to transfer to. 53   @param h The coroutine handle to transfer to.
54   */ 54   */
55   #if BOOST_CAPY_WORKAROUND(_MSC_VER, >= 1) 55   #if BOOST_CAPY_WORKAROUND(_MSC_VER, >= 1)
56   inline void symmetric_transfer(std::coroutine_handle<> h) noexcept 56   inline void symmetric_transfer(std::coroutine_handle<> h) noexcept
57   { 57   {
58   // safe_resume is not needed here: the calling coroutine is 58   // safe_resume is not needed here: the calling coroutine is
59   // about to suspend unconditionally. When it later resumes, 59   // about to suspend unconditionally. When it later resumes,
60   // await_resume restores TLS from the promise's environment. 60   // await_resume restores TLS from the promise's environment.
61   h.resume(); 61   h.resume();
62   } 62   }
63   #else 63   #else
64   inline std::coroutine_handle<> 64   inline std::coroutine_handle<>
HITCBC 65   2971 symmetric_transfer(std::coroutine_handle<> h) noexcept 65   2971 symmetric_transfer(std::coroutine_handle<> h) noexcept
66   { 66   {
HITCBC 67   2971 return h; 67   2971 return h;
68   } 68   }
69   #endif 69   #endif
70   70  
71   // Helper to normalize await_suspend return types to std::coroutine_handle<> 71   // Helper to normalize await_suspend return types to std::coroutine_handle<>
72   template<typename Awaitable> 72   template<typename Awaitable>
HITCBC 73   7 std::coroutine_handle<> call_await_suspend( 73   7 std::coroutine_handle<> call_await_suspend(
74   Awaitable* a, 74   Awaitable* a,
75   std::coroutine_handle<> h, 75   std::coroutine_handle<> h,
76   io_env const* env) 76   io_env const* env)
77   { 77   {
78   using R = decltype(a->await_suspend(h, env)); 78   using R = decltype(a->await_suspend(h, env));
79   if constexpr (std::is_void_v<R>) 79   if constexpr (std::is_void_v<R>)
80   { 80   {
MISUBC 81   a->await_suspend(h, env); 81   a->await_suspend(h, env);
MISUBC 82   return std::noop_coroutine(); 82   return std::noop_coroutine();
83   } 83   }
84   else if constexpr (std::is_same_v<R, bool>) 84   else if constexpr (std::is_same_v<R, bool>)
85   { 85   {
86   if(a->await_suspend(h, env)) 86   if(a->await_suspend(h, env))
87   return std::noop_coroutine(); 87   return std::noop_coroutine();
88   return h; 88   return h;
89   } 89   }
90   else 90   else
91   { 91   {
HITCBC 92   7 return a->await_suspend(h, env); 92   7 return a->await_suspend(h, env);
93   } 93   }
94   } 94   }
95   95  
96   } // namespace detail 96   } // namespace detail
97   } // namespace capy 97   } // namespace capy
98   } // namespace boost 98   } // namespace boost
99   99  
100   #endif 100   #endif