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 `executor::dispatch()`, which may resume the parent.
39  
        The parent can destroy this frame before the runtime reads
39  
        The parent can destroy this frame before the runtime reads
40  
        `__$ReturnUdt$` (e.g. `dispatch_trampoline` final_suspend).
40  
        `__$ReturnUdt$` (e.g. `dispatch_trampoline` final_suspend).
41  

41  

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

45  

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

48  

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

51  

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

66  

67  
// Helper to normalize await_suspend return types to std::coroutine_handle<>
67  
// Helper to normalize await_suspend return types to std::coroutine_handle<>
68  
template<typename Awaitable>
68  
template<typename Awaitable>
69  
std::coroutine_handle<> call_await_suspend(
69  
std::coroutine_handle<> call_await_suspend(
70  
    Awaitable* a,
70  
    Awaitable* a,
71  
    std::coroutine_handle<> h,
71  
    std::coroutine_handle<> h,
72  
    io_env const* env)
72  
    io_env const* env)
73  
{
73  
{
74  
    using R = decltype(a->await_suspend(h, env));
74  
    using R = decltype(a->await_suspend(h, env));
75  
    if constexpr (std::is_void_v<R>)
75  
    if constexpr (std::is_void_v<R>)
76  
    {
76  
    {
77  
        a->await_suspend(h, env);
77  
        a->await_suspend(h, env);
78  
        return std::noop_coroutine();
78  
        return std::noop_coroutine();
79  
    }
79  
    }
80  
    else if constexpr (std::is_same_v<R, bool>)
80  
    else if constexpr (std::is_same_v<R, bool>)
81  
    {
81  
    {
82  
        if(a->await_suspend(h, env))
82  
        if(a->await_suspend(h, env))
83  
            return std::noop_coroutine();
83  
            return std::noop_coroutine();
84  
        return h;
84  
        return h;
85  
    }
85  
    }
86  
    else
86  
    else
87  
    {
87  
    {
88  
        return a->await_suspend(h, env);
88  
        return a->await_suspend(h, env);
89  
    }
89  
    }
90  
}
90  
}
91  

91  

92  
} // namespace detail
92  
} // namespace detail
93  
} // namespace capy
93  
} // namespace capy
94  
} // namespace boost
94  
} // namespace boost
95  

95  

96  
#endif
96  
#endif