TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Steve Gerbino
4 : //
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)
7 : //
8 : // Official repository: https://github.com/cppalliance/capy
9 : //
10 :
11 : #ifndef BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP
12 : #define BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP
13 :
14 : #include <coroutine>
15 : #include <boost/capy/ex/io_env.hpp>
16 :
17 : #include <type_traits>
18 :
19 : namespace boost {
20 : namespace capy {
21 : namespace detail {
22 :
23 : /** Perform symmetric transfer, working around an MSVC codegen bug.
24 :
25 : MSVC stores the `std::coroutine_handle<>` returned from
26 : `await_suspend` in a hidden `__$ReturnUdt$` variable located
27 : on the coroutine frame. When another thread resumes or destroys
28 : the frame between the store and the read-back for the
29 : symmetric-transfer tail-call, the read hits freed memory.
30 :
31 : This occurs in two scenarios:
32 :
33 : @li `await_suspend` calls `h.destroy()` then returns a handle
34 : (e.g. `when_all_runner` and `when_any_runner` final_suspend).
35 : The return value is written to the now-destroyed frame.
36 :
37 : @li `await_suspend` hands the continuation to another thread
38 : via `executor::dispatch()`, which may resume the parent.
39 : The parent can destroy this frame before the runtime reads
40 : `__$ReturnUdt$` (e.g. `dispatch_trampoline` final_suspend).
41 :
42 : On MSVC this function calls `h.resume()` on the current stack
43 : and returns `void`, causing unconditional suspension. The
44 : trade-off is O(n) stack growth instead of O(1) tail-calls.
45 :
46 : On other compilers the handle is returned directly for proper
47 : symmetric transfer.
48 :
49 : Callers must use `auto` return type on their `await_suspend`
50 : so the return type adapts per platform.
51 :
52 : @param h The coroutine handle to transfer to.
53 : */
54 : #ifdef _MSC_VER
55 : inline void symmetric_transfer(std::coroutine_handle<> h) noexcept
56 : {
57 : h.resume();
58 : }
59 : #else
60 : inline std::coroutine_handle<>
61 HIT 2878 : symmetric_transfer(std::coroutine_handle<> h) noexcept
62 : {
63 2878 : return h;
64 : }
65 : #endif
66 :
67 : // Helper to normalize await_suspend return types to std::coroutine_handle<>
68 : template<typename Awaitable>
69 7 : std::coroutine_handle<> call_await_suspend(
70 : Awaitable* a,
71 : std::coroutine_handle<> h,
72 : io_env const* env)
73 : {
74 : using R = decltype(a->await_suspend(h, env));
75 : if constexpr (std::is_void_v<R>)
76 : {
77 MIS 0 : a->await_suspend(h, env);
78 0 : return std::noop_coroutine();
79 : }
80 : else if constexpr (std::is_same_v<R, bool>)
81 : {
82 : if(a->await_suspend(h, env))
83 : return std::noop_coroutine();
84 : return h;
85 : }
86 : else
87 : {
88 HIT 7 : return a->await_suspend(h, env);
89 : }
90 : }
91 :
92 : } // namespace detail
93 : } // namespace capy
94 : } // namespace boost
95 :
96 : #endif
|