1  
//
1  
//
2  
// Copyright (c) 2026 Michael Vandeberg
2  
// Copyright (c) 2026 Michael Vandeberg
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// 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)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_DETAIL_CONTINUATION_OP_HPP
10  
#ifndef BOOST_COROSIO_DETAIL_CONTINUATION_OP_HPP
11  
#define BOOST_COROSIO_DETAIL_CONTINUATION_OP_HPP
11  
#define BOOST_COROSIO_DETAIL_CONTINUATION_OP_HPP
12  

12  

13  
#include <boost/corosio/detail/scheduler_op.hpp>
13  
#include <boost/corosio/detail/scheduler_op.hpp>
14  
#include <boost/capy/continuation.hpp>
14  
#include <boost/capy/continuation.hpp>
15  

15  

16  
#include <atomic>
16  
#include <atomic>
17  
#include <cstdint>
17  
#include <cstdint>
18  
#include <cstring>
18  
#include <cstring>
19  

19  

20  
namespace boost::corosio::detail {
20  
namespace boost::corosio::detail {
21  

21  

22  
/* Scheduler operation that resumes a capy::continuation.
22  
/* Scheduler operation that resumes a capy::continuation.
23  

23  

24  
   Embeds a continuation alongside a scheduler_op so the
24  
   Embeds a continuation alongside a scheduler_op so the
25  
   scheduler can queue it in the same FIFO as I/O completions
25  
   scheduler can queue it in the same FIFO as I/O completions
26  
   without a heap allocation. The continuation lives in the
26  
   without a heap allocation. The continuation lives in the
27  
   caller's coroutine frame (awaitable or op struct); this
27  
   caller's coroutine frame (awaitable or op struct); this
28  
   wrapper gives it a scheduler_op identity.
28  
   wrapper gives it a scheduler_op identity.
29  

29  

30  
   io_context::executor_type::post(continuation&) uses
30  
   io_context::executor_type::post(continuation&) uses
31  
   try_from_continuation() to recover the enclosing
31  
   try_from_continuation() to recover the enclosing
32  
   continuation_op via a magic tag. The tag is read through
32  
   continuation_op via a magic tag. The tag is read through
33  
   memcpy (not through a continuation_op*) so that UBSan
33  
   memcpy (not through a continuation_op*) so that UBSan
34  
   does not flag the speculative pointer arithmetic when the
34  
   does not flag the speculative pointer arithmetic when the
35  
   continuation is not actually inside a continuation_op.
35  
   continuation is not actually inside a continuation_op.
36  
*/
36  
*/
37  
struct continuation_op final : scheduler_op
37  
struct continuation_op final : scheduler_op
38  
{
38  
{
39  
    static constexpr std::uint32_t magic_ = 0xC0710Au;
39  
    static constexpr std::uint32_t magic_ = 0xC0710Au;
40  

40  

41  
    std::uint32_t tag_ = magic_;
41  
    std::uint32_t tag_ = magic_;
42  
    capy::continuation cont;
42  
    capy::continuation cont;
43  

43  

44  
    continuation_op() noexcept : scheduler_op(&do_complete) {}
44  
    continuation_op() noexcept : scheduler_op(&do_complete) {}
45  

45  

46  
    // Reactor backends (epoll, select, kqueue) dispatch through
46  
    // Reactor backends (epoll, select, kqueue) dispatch through
47  
    // virtual operator()(). IOCP dispatches through func_ which
47  
    // virtual operator()(). IOCP dispatches through func_ which
48  
    // routes to do_complete below.
48  
    // routes to do_complete below.
49  
    void operator()() override
49  
    void operator()() override
50  
    {
50  
    {
51  
        std::atomic_thread_fence(std::memory_order_acquire);
51  
        std::atomic_thread_fence(std::memory_order_acquire);
52  
        cont.h.resume();
52  
        cont.h.resume();
53  
    }
53  
    }
54  

54  

55  
    void destroy() override
55  
    void destroy() override
56  
    {
56  
    {
57  
        if (cont.h)
57  
        if (cont.h)
58  
            cont.h.destroy();
58  
            cont.h.destroy();
59  
    }
59  
    }
60  

60  

61  
private:
61  
private:
62  
    // IOCP completion entry point. owner == nullptr means destroy.
62  
    // IOCP completion entry point. owner == nullptr means destroy.
63  
    static void do_complete(
63  
    static void do_complete(
64  
        void* owner,
64  
        void* owner,
65  
        scheduler_op* base,
65  
        scheduler_op* base,
66  
        std::uint32_t,
66  
        std::uint32_t,
67  
        std::uint32_t)
67  
        std::uint32_t)
68  
    {
68  
    {
69  
        auto* self = static_cast<continuation_op*>(base);
69  
        auto* self = static_cast<continuation_op*>(base);
70  
        if (!owner)
70  
        if (!owner)
71  
        {
71  
        {
72  
            if (self->cont.h)
72  
            if (self->cont.h)
73  
                self->cont.h.destroy();
73  
                self->cont.h.destroy();
74  
            return;
74  
            return;
75  
        }
75  
        }
76  
        std::atomic_thread_fence(std::memory_order_acquire);
76  
        std::atomic_thread_fence(std::memory_order_acquire);
77  
        self->cont.h.resume();
77  
        self->cont.h.resume();
78  
    }
78  
    }
79  

79  

80  
public:
80  
public:
81  

81  

82  
    // Recover the enclosing continuation_op from its cont member.
82  
    // Recover the enclosing continuation_op from its cont member.
83  
    // Returns nullptr if the continuation is not tagged (bare
83  
    // Returns nullptr if the continuation is not tagged (bare
84  
    // capy::continuation from capy internals like run_async).
84  
    // capy::continuation from capy internals like run_async).
85  
    static continuation_op* try_from_continuation(
85  
    static continuation_op* try_from_continuation(
86  
        capy::continuation& c) noexcept
86  
        capy::continuation& c) noexcept
87  
    {
87  
    {
88  
        // offsetof on non-standard-layout is conditionally-supported;
88  
        // offsetof on non-standard-layout is conditionally-supported;
89  
        // suppress the warning — all targeted compilers handle this
89  
        // suppress the warning — all targeted compilers handle this
90  
        // correctly and the self-relative arithmetic is move-safe.
90  
        // correctly and the self-relative arithmetic is move-safe.
91  
#if defined(__GNUC__) || defined(__clang__)
91  
#if defined(__GNUC__) || defined(__clang__)
92  
#pragma GCC diagnostic push
92  
#pragma GCC diagnostic push
93  
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
93  
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
94  
#endif
94  
#endif
95  
        constexpr auto cont_off = offsetof(continuation_op, cont);
95  
        constexpr auto cont_off = offsetof(continuation_op, cont);
96  
        constexpr auto tag_off  = offsetof(continuation_op, tag_);
96  
        constexpr auto tag_off  = offsetof(continuation_op, tag_);
97  
#if defined(__GNUC__) || defined(__clang__)
97  
#if defined(__GNUC__) || defined(__clang__)
98  
#pragma GCC diagnostic pop
98  
#pragma GCC diagnostic pop
99  
#endif
99  
#endif
100  
        // Read the tag through memcpy from a char*, not through a
100  
        // Read the tag through memcpy from a char*, not through a
101  
        // continuation_op*. This avoids UBSan's vptr check when
101  
        // continuation_op*. This avoids UBSan's vptr check when
102  
        // the continuation is not actually inside a continuation_op.
102  
        // the continuation is not actually inside a continuation_op.
103  
        auto* base = reinterpret_cast<char*>(&c) - cont_off;
103  
        auto* base = reinterpret_cast<char*>(&c) - cont_off;
104  
        std::uint32_t tag;
104  
        std::uint32_t tag;
105  
        std::memcpy(&tag, base + tag_off, sizeof(tag));
105  
        std::memcpy(&tag, base + tag_off, sizeof(tag));
106  
        if (tag != magic_)
106  
        if (tag != magic_)
107  
            return nullptr;
107  
            return nullptr;
108  
        return reinterpret_cast<continuation_op*>(base);
108  
        return reinterpret_cast<continuation_op*>(base);
109  
    }
109  
    }
110  
};
110  
};
111  

111  

112  
} // namespace boost::corosio::detail
112  
} // namespace boost::corosio::detail
113  

113  

114  
#endif
114  
#endif