include/boost/corosio/detail/continuation_op.hpp
52.2% Lines (12/23)
60.0% List of functions (3/5)
Functions (5)
Function
Calls
Lines
Blocks
boost::corosio::detail::continuation_op::continuation_op()
:44
80883x
100.0%
100.0%
boost::corosio::detail::continuation_op::operator()()
:49
8632x
100.0%
100.0%
boost::corosio::detail::continuation_op::destroy()
:55
0
0.0%
0.0%
boost::corosio::detail::continuation_op::do_complete(void*, boost::corosio::detail::scheduler_op*, unsigned int, unsigned int)
:63
0
0.0%
0.0%
boost::corosio::detail::continuation_op::try_from_continuation(boost::capy::continuation&)
:85
9323x
100.0%
100.0%
| 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 |