52.17% Lines (12/23) 60.00% Functions (3/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   65120 continuation_op() noexcept : scheduler_op(&do_complete) {} 44   77228 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   7000 void operator()() override 49   8212 void operator()() override
50   { 50   {
51   std::atomic_thread_fence(std::memory_order_acquire); 51   std::atomic_thread_fence(std::memory_order_acquire);
HITCBC 52   7000 cont.h.resume(); 52   8212 cont.h.resume();
HITCBC 53   7000 } 53   8212 }
54   54  
MISUBC 55   void destroy() override 55   void destroy() override
56   { 56   {
MISUBC 57   if (cont.h) 57   if (cont.h)
MISUBC 58   cont.h.destroy(); 58   cont.h.destroy();
MISUBC 59   } 59   }
60   60  
61   private: 61   private:
62   // IOCP completion entry point. owner == nullptr means destroy. 62   // IOCP completion entry point. owner == nullptr means destroy.
MISUBC 63   static void do_complete( 63   static void do_complete(
64   void* owner, 64   void* owner,
65   scheduler_op* base, 65   scheduler_op* base,
66   std::uint32_t, 66   std::uint32_t,
67   std::uint32_t) 67   std::uint32_t)
68   { 68   {
MISUBC 69   auto* self = static_cast<continuation_op*>(base); 69   auto* self = static_cast<continuation_op*>(base);
MISUBC 70   if (!owner) 70   if (!owner)
71   { 71   {
MISUBC 72   if (self->cont.h) 72   if (self->cont.h)
MISUBC 73   self->cont.h.destroy(); 73   self->cont.h.destroy();
MISUBC 74   return; 74   return;
75   } 75   }
76   std::atomic_thread_fence(std::memory_order_acquire); 76   std::atomic_thread_fence(std::memory_order_acquire);
MISUBC 77   self->cont.h.resume(); 77   self->cont.h.resume();
78   } 78   }
79   79  
80   public: 80   public:
81   81  
82   // Recover the enclosing continuation_op from its cont member. 82   // Recover the enclosing continuation_op from its cont member.
83   // Returns nullptr if the continuation is not tagged (bare 83   // Returns nullptr if the continuation is not tagged (bare
84   // capy::continuation from capy internals like run_async). 84   // capy::continuation from capy internals like run_async).
HITCBC 85   7763 static continuation_op* try_from_continuation( 85   8975 static continuation_op* try_from_continuation(
86   capy::continuation& c) noexcept 86   capy::continuation& c) noexcept
87   { 87   {
88   // offsetof on non-standard-layout is conditionally-supported; 88   // offsetof on non-standard-layout is conditionally-supported;
89   // suppress the warning — all targeted compilers handle this 89   // suppress the warning — all targeted compilers handle this
90   // correctly and the self-relative arithmetic is move-safe. 90   // correctly and the self-relative arithmetic is move-safe.
91   #if defined(__GNUC__) || defined(__clang__) 91   #if defined(__GNUC__) || defined(__clang__)
92   #pragma GCC diagnostic push 92   #pragma GCC diagnostic push
93   #pragma GCC diagnostic ignored "-Winvalid-offsetof" 93   #pragma GCC diagnostic ignored "-Winvalid-offsetof"
94   #endif 94   #endif
HITCBC 95   7763 constexpr auto cont_off = offsetof(continuation_op, cont); 95   8975 constexpr auto cont_off = offsetof(continuation_op, cont);
HITCBC 96   7763 constexpr auto tag_off = offsetof(continuation_op, tag_); 96   8975 constexpr auto tag_off = offsetof(continuation_op, tag_);
97   #if defined(__GNUC__) || defined(__clang__) 97   #if defined(__GNUC__) || defined(__clang__)
98   #pragma GCC diagnostic pop 98   #pragma GCC diagnostic pop
99   #endif 99   #endif
100   // Read the tag through memcpy from a char*, not through a 100   // Read the tag through memcpy from a char*, not through a
101   // continuation_op*. This avoids UBSan's vptr check when 101   // continuation_op*. This avoids UBSan's vptr check when
102   // the continuation is not actually inside a continuation_op. 102   // the continuation is not actually inside a continuation_op.
HITCBC 103   7763 auto* base = reinterpret_cast<char*>(&c) - cont_off; 103   8975 auto* base = reinterpret_cast<char*>(&c) - cont_off;
104   std::uint32_t tag; 104   std::uint32_t tag;
HITCBC 105   7763 std::memcpy(&tag, base + tag_off, sizeof(tag)); 105   8975 std::memcpy(&tag, base + tag_off, sizeof(tag));
HITCBC 106   7763 if (tag != magic_) 106   8975 if (tag != magic_)
HITCBC 107   763 return nullptr; 107   763 return nullptr;
HITCBC 108   7000 return reinterpret_cast<continuation_op*>(base); 108   8212 return reinterpret_cast<continuation_op*>(base);
109   } 109   }
110   }; 110   };
111   111  
112   } // namespace boost::corosio::detail 112   } // namespace boost::corosio::detail
113   113  
114   #endif 114   #endif