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