TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Michael Vandeberg
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/corosio
8 : //
9 :
10 : #ifndef BOOST_COROSIO_DETAIL_CONTINUATION_OP_HPP
11 : #define BOOST_COROSIO_DETAIL_CONTINUATION_OP_HPP
12 :
13 : #include <boost/corosio/detail/scheduler_op.hpp>
14 : #include <boost/capy/continuation.hpp>
15 :
16 : #include <atomic>
17 : #include <cstdint>
18 : #include <cstring>
19 :
20 : namespace boost::corosio::detail {
21 :
22 : /* Scheduler operation that resumes a capy::continuation.
23 :
24 : Embeds a continuation alongside a scheduler_op so the
25 : scheduler can queue it in the same FIFO as I/O completions
26 : without a heap allocation. The continuation lives in the
27 : caller's coroutine frame (awaitable or op struct); this
28 : wrapper gives it a scheduler_op identity.
29 :
30 : io_context::executor_type::post(continuation&) uses
31 : try_from_continuation() to recover the enclosing
32 : continuation_op via a magic tag. The tag is read through
33 : memcpy (not through a continuation_op*) so that UBSan
34 : does not flag the speculative pointer arithmetic when the
35 : continuation is not actually inside a continuation_op.
36 : */
37 : struct continuation_op final : scheduler_op
38 : {
39 : static constexpr std::uint32_t magic_ = 0xC0710Au;
40 :
41 : std::uint32_t tag_ = magic_;
42 : capy::continuation cont;
43 :
44 HIT 80883 : continuation_op() noexcept : scheduler_op(&do_complete) {}
45 :
46 : // Reactor backends (epoll, select, kqueue) dispatch through
47 : // virtual operator()(). IOCP dispatches through func_ which
48 : // routes to do_complete below.
49 8632 : void operator()() override
50 : {
51 : std::atomic_thread_fence(std::memory_order_acquire);
52 8632 : cont.h.resume();
53 8632 : }
54 :
55 MIS 0 : void destroy() override
56 : {
57 0 : if (cont.h)
58 0 : cont.h.destroy();
59 0 : }
60 :
61 : private:
62 : // IOCP completion entry point. owner == nullptr means destroy.
63 0 : static void do_complete(
64 : void* owner,
65 : scheduler_op* base,
66 : std::uint32_t,
67 : std::uint32_t)
68 : {
69 0 : auto* self = static_cast<continuation_op*>(base);
70 0 : if (!owner)
71 : {
72 0 : if (self->cont.h)
73 0 : self->cont.h.destroy();
74 0 : return;
75 : }
76 : std::atomic_thread_fence(std::memory_order_acquire);
77 0 : self->cont.h.resume();
78 : }
79 :
80 : public:
81 :
82 : // Recover the enclosing continuation_op from its cont member.
83 : // Returns nullptr if the continuation is not tagged (bare
84 : // capy::continuation from capy internals like run_async).
85 HIT 9323 : static continuation_op* try_from_continuation(
86 : capy::continuation& c) noexcept
87 : {
88 : // offsetof on non-standard-layout is conditionally-supported;
89 : // suppress the warning — all targeted compilers handle this
90 : // correctly and the self-relative arithmetic is move-safe.
91 : #if defined(__GNUC__) || defined(__clang__)
92 : #pragma GCC diagnostic push
93 : #pragma GCC diagnostic ignored "-Winvalid-offsetof"
94 : #endif
95 9323 : constexpr auto cont_off = offsetof(continuation_op, cont);
96 9323 : constexpr auto tag_off = offsetof(continuation_op, tag_);
97 : #if defined(__GNUC__) || defined(__clang__)
98 : #pragma GCC diagnostic pop
99 : #endif
100 : // Read the tag through memcpy from a char*, not through a
101 : // continuation_op*. This avoids UBSan's vptr check when
102 : // the continuation is not actually inside a continuation_op.
103 9323 : auto* base = reinterpret_cast<char*>(&c) - cont_off;
104 : std::uint32_t tag;
105 9323 : std::memcpy(&tag, base + tag_off, sizeof(tag));
106 9323 : if (tag != magic_)
107 691 : return nullptr;
108 8632 : return reinterpret_cast<continuation_op*>(base);
109 : }
110 : };
111 :
112 : } // namespace boost::corosio::detail
113 :
114 : #endif
|