include/boost/corosio/detail/continuation_op.hpp

52.2% Lines (12/23) 60.0% List of functions (3/5)
continuation_op.hpp
f(x) Functions (5)
Line TLA Hits 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 80883x 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 8632x void operator()() override
50 {
51 std::atomic_thread_fence(std::memory_order_acquire);
52 8632x cont.h.resume();
53 8632x }
54
55 void destroy() override
56 {
57 if (cont.h)
58 cont.h.destroy();
59 }
60
61 private:
62 // IOCP completion entry point. owner == nullptr means destroy.
63 static void do_complete(
64 void* owner,
65 scheduler_op* base,
66 std::uint32_t,
67 std::uint32_t)
68 {
69 auto* self = static_cast<continuation_op*>(base);
70 if (!owner)
71 {
72 if (self->cont.h)
73 self->cont.h.destroy();
74 return;
75 }
76 std::atomic_thread_fence(std::memory_order_acquire);
77 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 9323x 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 9323x constexpr auto cont_off = offsetof(continuation_op, cont);
96 9323x 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 9323x auto* base = reinterpret_cast<char*>(&c) - cont_off;
104 std::uint32_t tag;
105 9323x std::memcpy(&tag, base + tag_off, sizeof(tag));
106 9323x if (tag != magic_)
107 691x return nullptr;
108 8632x return reinterpret_cast<continuation_op*>(base);
109 }
110 };
111
112 } // namespace boost::corosio::detail
113
114 #endif
115