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 163510 : 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 8960 : void operator()() override
50 : {
51 : // ThreadSanitizer cannot instrument standalone fences; this acquire
52 : // fence pairs with the scheduler's release and is intentional.
53 : BOOST_COROSIO_GCC_WARNING_PUSH
54 : BOOST_COROSIO_GCC_WARNING_DISABLE("-Wtsan")
55 : std::atomic_thread_fence(std::memory_order_acquire);
56 : BOOST_COROSIO_GCC_WARNING_POP
57 8960 : cont.h.resume();
58 8960 : }
59 :
60 4 : void destroy() override
61 : {
62 4 : if (cont.h)
63 4 : cont.h.destroy();
64 4 : }
65 :
66 : private:
67 : // IOCP completion entry point. owner == nullptr means destroy.
68 MIS 0 : static void do_complete(
69 : void* owner,
70 : scheduler_op* base,
71 : std::uint32_t,
72 : std::uint32_t)
73 : {
74 0 : auto* self = static_cast<continuation_op*>(base);
75 0 : if (!owner)
76 : {
77 0 : if (self->cont.h)
78 0 : self->cont.h.destroy();
79 0 : return;
80 : }
81 : BOOST_COROSIO_GCC_WARNING_PUSH
82 : BOOST_COROSIO_GCC_WARNING_DISABLE("-Wtsan")
83 : std::atomic_thread_fence(std::memory_order_acquire);
84 : BOOST_COROSIO_GCC_WARNING_POP
85 0 : self->cont.h.resume();
86 : }
87 :
88 : public:
89 :
90 : // Recover the enclosing continuation_op from its cont member.
91 : // Returns nullptr if the continuation is not tagged (bare
92 : // capy::continuation from capy internals like run_async).
93 HIT 10443 : static continuation_op* try_from_continuation(
94 : capy::continuation& c) noexcept
95 : {
96 : // offsetof on non-standard-layout is conditionally-supported;
97 : // suppress the warning — all targeted compilers handle this
98 : // correctly and the self-relative arithmetic is move-safe.
99 : #if defined(__GNUC__) || defined(__clang__)
100 : #pragma GCC diagnostic push
101 : #pragma GCC diagnostic ignored "-Winvalid-offsetof"
102 : #endif
103 10443 : constexpr auto cont_off = offsetof(continuation_op, cont);
104 10443 : constexpr auto tag_off = offsetof(continuation_op, tag_);
105 : #if defined(__GNUC__) || defined(__clang__)
106 : #pragma GCC diagnostic pop
107 : #endif
108 : // Read the tag through memcpy from a char*, not through a
109 : // continuation_op*. This avoids UBSan's vptr check when
110 : // the continuation is not actually inside a continuation_op.
111 10443 : auto* base = reinterpret_cast<char*>(&c) - cont_off;
112 : std::uint32_t tag;
113 10443 : std::memcpy(&tag, base + tag_off, sizeof(tag));
114 10443 : if (tag != magic_)
115 1479 : return nullptr;
116 8964 : return reinterpret_cast<continuation_op*>(base);
117 : }
118 : };
119 :
120 : } // namespace boost::corosio::detail
121 :
122 : #endif
|