1  
//
1  
//
2 -
// Copyright (c) 2026 Steve Gerbino
 
3  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
4  
//
3  
//
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// 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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
//
6  
//
8  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
9  
//
8  
//
10  

9  

11  
#ifndef BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP
10  
#ifndef BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP
12  
#define BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP
11  
#define BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP
13  

12  

14  
#include <coroutine>
13  
#include <coroutine>
15  
#include <boost/capy/ex/io_env.hpp>
14  
#include <boost/capy/ex/io_env.hpp>
16  

15  

17  
#include <type_traits>
16  
#include <type_traits>
18  

17  

19  
namespace boost {
18  
namespace boost {
20  
namespace capy {
19  
namespace capy {
21 -

 
22 -
/** Perform symmetric transfer, working around an MSVC codegen bug.
 
23 -

 
24 -
    MSVC stores the `std::coroutine_handle<>` returned from
 
25 -
    `await_suspend` in a hidden `__$ReturnUdt$` variable located
 
26 -
    on the coroutine frame. When another thread resumes or destroys
 
27 -
    the frame between the store and the read-back for the
 
28 -
    symmetric-transfer tail-call, the read hits freed memory.
 
29 -

 
30 -
    This occurs in two scenarios:
 
31 -

 
32 -
    @li `await_suspend` calls `h.destroy()` then returns a handle
 
33 -
        (e.g. `when_all_runner` and `when_any_runner` final_suspend).
 
34 -
        The return value is written to the now-destroyed frame.
 
35 -

 
36 -
    @li `await_suspend` hands the continuation to another thread
 
37 -
        via `executor::dispatch()`, which may resume the parent.
 
38 -
        The parent can destroy this frame before the runtime reads
 
39 -
        `__$ReturnUdt$` (e.g. `dispatch_trampoline` final_suspend).
 
40 -

 
41 -
    On MSVC this function calls `h.resume()` on the current stack
 
42 -
    and returns `void`, causing unconditional suspension. The
 
43 -
    trade-off is O(n) stack growth instead of O(1) tail-calls.
 
44 -

 
45 -
    On other compilers the handle is returned directly for proper
 
46 -
    symmetric transfer.
 
47 -

 
48 -
    Callers must use `auto` return type on their `await_suspend`
 
49 -
    so the return type adapts per platform.
 
50 -

 
51 -
    @param h The coroutine handle to transfer to.
 
52 -
*/
 
53 -
#ifdef _MSC_VER
 
54 -
inline void symmetric_transfer(std::coroutine_handle<> h) noexcept
 
55 -
{
 
56 -
    h.resume();
 
57 -
}
 
58 -
#else
 
59 -
inline std::coroutine_handle<>
 
60 -
symmetric_transfer(std::coroutine_handle<> h) noexcept
 
61 -
{
 
62 -
    return h;
 
63 -
}
 
64 -
#endif
 
65  
namespace detail {
20  
namespace detail {
66  

21  

67  
// Helper to normalize await_suspend return types to std::coroutine_handle<>
22  
// Helper to normalize await_suspend return types to std::coroutine_handle<>
68  
template<typename Awaitable>
23  
template<typename Awaitable>
69  
std::coroutine_handle<> call_await_suspend(
24  
std::coroutine_handle<> call_await_suspend(
70  
    Awaitable* a,
25  
    Awaitable* a,
71  
    std::coroutine_handle<> h,
26  
    std::coroutine_handle<> h,
72  
    io_env const* env)
27  
    io_env const* env)
73  
{
28  
{
74  
    using R = decltype(a->await_suspend(h, env));
29  
    using R = decltype(a->await_suspend(h, env));
75  
    if constexpr (std::is_void_v<R>)
30  
    if constexpr (std::is_void_v<R>)
76  
    {
31  
    {
77  
        a->await_suspend(h, env);
32  
        a->await_suspend(h, env);
78  
        return std::noop_coroutine();
33  
        return std::noop_coroutine();
79  
    }
34  
    }
80  
    else if constexpr (std::is_same_v<R, bool>)
35  
    else if constexpr (std::is_same_v<R, bool>)
81  
    {
36  
    {
82  
        if(a->await_suspend(h, env))
37  
        if(a->await_suspend(h, env))
83  
            return std::noop_coroutine();
38  
            return std::noop_coroutine();
84  
        return h;
39  
        return h;
85  
    }
40  
    }
86  
    else
41  
    else
87  
    {
42  
    {
88  
        return a->await_suspend(h, env);
43  
        return a->await_suspend(h, env);
89  
    }
44  
    }
90  
}
45  
}
91  

46  

92  
} // namespace detail
47  
} // namespace detail
93  
} // namespace capy
48  
} // namespace capy
94  
} // namespace boost
49  
} // namespace boost
95  

50  

96  
#endif
51  
#endif