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