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 : #if BOOST_CAPY_WORKAROUND(_MSC_VER, >= 1)
55 : inline void symmetric_transfer(std::coroutine_handle<> h) noexcept
56 : {
57 : // safe_resume is not needed here: the calling coroutine is
58 : // about to suspend unconditionally. When it later resumes,
59 : // await_resume restores TLS from the promise's environment.
60 : h.resume();
61 : }
62 : #else
63 : inline std::coroutine_handle<>
64 HIT 2961 : symmetric_transfer(std::coroutine_handle<> h) noexcept
65 : {
66 2961 : return h;
67 : }
68 : #endif
69 :
70 : // Helper to normalize await_suspend return types to std::coroutine_handle<>
71 : template<typename Awaitable>
72 7 : std::coroutine_handle<> call_await_suspend(
73 : Awaitable* a,
74 : std::coroutine_handle<> h,
75 : io_env const* env)
76 : {
77 : using R = decltype(a->await_suspend(h, env));
78 : if constexpr (std::is_void_v<R>)
79 : {
80 MIS 0 : a->await_suspend(h, env);
81 0 : return std::noop_coroutine();
82 : }
83 : else if constexpr (std::is_same_v<R, bool>)
84 : {
85 : if(a->await_suspend(h, env))
86 : return std::noop_coroutine();
87 : return h;
88 : }
89 : else
90 : {
91 HIT 7 : return a->await_suspend(h, env);
92 : }
93 : }
94 :
95 : } // namespace detail
96 : } // namespace capy
97 : } // namespace boost
98 :
99 : #endif
|