73.91% Lines (34/46) 76.47% Functions (13/17)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
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/capy 7   // Official repository: https://github.com/cppalliance/capy
8   // 8   //
9   9  
10   #ifndef BOOST_CAPY_EXECUTOR_REF_HPP 10   #ifndef BOOST_CAPY_EXECUTOR_REF_HPP
11   #define BOOST_CAPY_EXECUTOR_REF_HPP 11   #define BOOST_CAPY_EXECUTOR_REF_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
14   #include <boost/capy/detail/type_id.hpp> 14   #include <boost/capy/detail/type_id.hpp>
  15 + #include <boost/capy/continuation.hpp>
15   #include <concepts> 16   #include <concepts>
16   #include <coroutine> 17   #include <coroutine>
17   #include <type_traits> 18   #include <type_traits>
18   #include <utility> 19   #include <utility>
19   20  
20   namespace boost { 21   namespace boost {
21   namespace capy { 22   namespace capy {
22   23  
23   class execution_context; 24   class execution_context;
24   25  
25   namespace detail { 26   namespace detail {
26   27  
27   /** Virtual function table for type-erased executor operations. */ 28   /** Virtual function table for type-erased executor operations. */
28   struct executor_vtable 29   struct executor_vtable
29   { 30   {
30   execution_context& (*context)(void const*) noexcept; 31   execution_context& (*context)(void const*) noexcept;
31   void (*on_work_started)(void const*) noexcept; 32   void (*on_work_started)(void const*) noexcept;
32   void (*on_work_finished)(void const*) noexcept; 33   void (*on_work_finished)(void const*) noexcept;
33 - void (*post)(void const*, std::coroutine_handle<>); 34 + void (*post)(void const*, continuation&);
34 - std::coroutine_handle<> (*dispatch)(void const*, std::coroutine_handle<>); 35 + std::coroutine_handle<> (*dispatch)(void const*, continuation&);
35   bool (*equals)(void const*, void const*) noexcept; 36   bool (*equals)(void const*, void const*) noexcept;
36   detail::type_info const* type_id; 37   detail::type_info const* type_id;
37   }; 38   };
38   39  
39   /** Vtable instance for a specific executor type. */ 40   /** Vtable instance for a specific executor type. */
40   template<class Ex> 41   template<class Ex>
41   inline constexpr executor_vtable vtable_for = { 42   inline constexpr executor_vtable vtable_for = {
42   // context 43   // context
HITGBC 43   [](void const* p) noexcept -> execution_context& { 44   21 [](void const* p) noexcept -> execution_context& {
HITGBC 44   return const_cast<Ex*>(static_cast<Ex const*>(p))->context(); 45   21 return const_cast<Ex*>(static_cast<Ex const*>(p))->context();
45   }, 46   },
46   // on_work_started 47   // on_work_started
MISUBC 47   [](void const* p) noexcept { 48   [](void const* p) noexcept {
MISUBC 48   const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_started(); 49   const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_started();
49   }, 50   },
50   // on_work_finished 51   // on_work_finished
MISUBC 51   [](void const* p) noexcept { 52   [](void const* p) noexcept {
MISUBC 52   const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_finished(); 53   const_cast<Ex*>(static_cast<Ex const*>(p))->on_work_finished();
53   }, 54   },
54   // post 55   // post
HITCBC 55 - 878 [](void const* p, std::coroutine_handle<> h) { 56 + 40390 [](void const* p, continuation& c) {
HITCBC 56 - 439 static_cast<Ex const*>(p)->post(h); 57 + 20195 static_cast<Ex const*>(p)->post(c);
57   }, 58   },
58   // dispatch 59   // dispatch
HITCBC 59 - 134 [](void const* p, std::coroutine_handle<> h) -> std::coroutine_handle<> { 60 + 104 [](void const* p, continuation& c) -> std::coroutine_handle<> {
HITCBC 60 - 134 return static_cast<Ex const*>(p)->dispatch(h); 61 + 104 return static_cast<Ex const*>(p)->dispatch(c);
61   }, 62   },
62   // equals 63   // equals
HITCBC 63   1 [](void const* a, void const* b) noexcept -> bool { 64   1 [](void const* a, void const* b) noexcept -> bool {
HITCBC 64   1 return *static_cast<Ex const*>(a) == *static_cast<Ex const*>(b); 65   1 return *static_cast<Ex const*>(a) == *static_cast<Ex const*>(b);
65   }, 66   },
66   // type_id 67   // type_id
67   &detail::type_id<Ex>() 68   &detail::type_id<Ex>()
68   }; 69   };
69   70  
70   } // detail 71   } // detail
71   72  
72   /** A type-erased reference wrapper for executor objects. 73   /** A type-erased reference wrapper for executor objects.
73   74  
74   This class provides type erasure for any executor type, enabling 75   This class provides type erasure for any executor type, enabling
75   runtime polymorphism without virtual functions or allocation. 76   runtime polymorphism without virtual functions or allocation.
76   It stores a pointer to the original executor and a pointer to a 77   It stores a pointer to the original executor and a pointer to a
77   static vtable, allowing executors of different types to be stored 78   static vtable, allowing executors of different types to be stored
78   uniformly while satisfying the full `Executor` concept. 79   uniformly while satisfying the full `Executor` concept.
79   80  
80   @par Reference Semantics 81   @par Reference Semantics
81   This class has reference semantics: it does not allocate or own 82   This class has reference semantics: it does not allocate or own
82   the wrapped executor. Copy operations simply copy the internal 83   the wrapped executor. Copy operations simply copy the internal
83   pointers. The caller must ensure the referenced executor outlives 84   pointers. The caller must ensure the referenced executor outlives
84   all `executor_ref` instances that wrap it. 85   all `executor_ref` instances that wrap it.
85   86  
86   @par Thread Safety 87   @par Thread Safety
87   The `executor_ref` itself is not thread-safe for concurrent 88   The `executor_ref` itself is not thread-safe for concurrent
88   modification, but its executor operations are safe to call 89   modification, but its executor operations are safe to call
89   concurrently if the underlying executor supports it. 90   concurrently if the underlying executor supports it.
90   91  
91   @par Executor Concept 92   @par Executor Concept
92   This class satisfies the `Executor` concept, making it usable 93   This class satisfies the `Executor` concept, making it usable
93   anywhere a concrete executor is expected. 94   anywhere a concrete executor is expected.
94   95  
95   @par Example 96   @par Example
96   @code 97   @code
97   void store_executor(executor_ref ex) 98   void store_executor(executor_ref ex)
98   { 99   {
99   if(ex) 100   if(ex)
100 - ex.post(my_coroutine); 101 + ex.post(my_continuation);
101   } 102   }
102   103  
103   io_context ctx; 104   io_context ctx;
104   store_executor(ctx.get_executor()); 105   store_executor(ctx.get_executor());
105   @endcode 106   @endcode
106   107  
107   @see any_executor, Executor 108   @see any_executor, Executor
108   */ 109   */
109   class executor_ref 110   class executor_ref
110   { 111   {
111   void const* ex_ = nullptr; 112   void const* ex_ = nullptr;
112   detail::executor_vtable const* vt_ = nullptr; 113   detail::executor_vtable const* vt_ = nullptr;
113   114  
114   public: 115   public:
115 - /** Default constructor. 116 + /** Construct a default instance.
116   117  
117   Constructs an empty `executor_ref`. Calling any executor 118   Constructs an empty `executor_ref`. Calling any executor
118   operations on a default-constructed instance results in 119   operations on a default-constructed instance results in
119   undefined behavior. 120   undefined behavior.
120   */ 121   */
HITCBC 121   3923 executor_ref() = default; 122   4205 executor_ref() = default;
122   123  
123 - /** Copy constructor. 124 + /** Construct a copy.
124   125  
125   Copies the internal pointers, preserving identity. 126   Copies the internal pointers, preserving identity.
126   This enables the same-executor optimization when passing 127   This enables the same-executor optimization when passing
127   executor_ref through coroutine chains. 128   executor_ref through coroutine chains.
128   */ 129   */
129   executor_ref(executor_ref const&) = default; 130   executor_ref(executor_ref const&) = default;
130   131  
131   /** Copy assignment operator. */ 132   /** Copy assignment operator. */
132   executor_ref& operator=(executor_ref const&) = default; 133   executor_ref& operator=(executor_ref const&) = default;
133   134  
134   /** Constructs from any executor type. 135   /** Constructs from any executor type.
135   136  
136   Captures a reference to the given executor and stores a pointer 137   Captures a reference to the given executor and stores a pointer
137   to the type-specific vtable. The executor must remain valid for 138   to the type-specific vtable. The executor must remain valid for
138   the lifetime of this `executor_ref` instance. 139   the lifetime of this `executor_ref` instance.
139   140  
140   @param ex The executor to wrap. Must satisfy the `Executor` 141   @param ex The executor to wrap. Must satisfy the `Executor`
141   concept. A pointer to this object is stored 142   concept. A pointer to this object is stored
142   internally; the executor must outlive this wrapper. 143   internally; the executor must outlive this wrapper.
143   */ 144   */
144   #if defined(__GNUC__) && !defined(__clang__) 145   #if defined(__GNUC__) && !defined(__clang__)
145   // GCC constraint satisfaction caching bug workaround 146   // GCC constraint satisfaction caching bug workaround
146   template<class Ex, 147   template<class Ex,
147   std::enable_if_t<!std::is_same_v< 148   std::enable_if_t<!std::is_same_v<
148   std::decay_t<Ex>, executor_ref>, int> = 0> 149   std::decay_t<Ex>, executor_ref>, int> = 0>
149   #else 150   #else
150   template<class Ex> 151   template<class Ex>
151   requires (!std::same_as<std::decay_t<Ex>, executor_ref>) 152   requires (!std::same_as<std::decay_t<Ex>, executor_ref>)
152   #endif 153   #endif
HITCBC 153   3302 executor_ref(Ex const& ex) noexcept 154   33720 executor_ref(Ex const& ex) noexcept
HITCBC 154   3302 : ex_(&ex) 155   33720 : ex_(&ex)
HITCBC 155   3302 , vt_(&detail::vtable_for<Ex>) 156   33720 , vt_(&detail::vtable_for<Ex>)
156   { 157   {
HITCBC 157   3302 } 158   33720 }
158   159  
159   /** Returns true if this instance holds a valid executor. 160   /** Returns true if this instance holds a valid executor.
160   161  
161   @return `true` if constructed with an executor, `false` if 162   @return `true` if constructed with an executor, `false` if
162   default-constructed. 163   default-constructed.
163   */ 164   */
HITCBC 164   6 explicit operator bool() const noexcept 165   6 explicit operator bool() const noexcept
165   { 166   {
HITCBC 166   6 return ex_ != nullptr; 167   6 return ex_ != nullptr;
167   } 168   }
168   169  
169   /** Returns a reference to the associated execution context. 170   /** Returns a reference to the associated execution context.
170   171  
171   @return A reference to the execution context. 172   @return A reference to the execution context.
172   173  
173   @pre This instance was constructed with a valid executor. 174   @pre This instance was constructed with a valid executor.
174   */ 175   */
HITGIC 175   execution_context& context() const noexcept 176   21 execution_context& context() const noexcept
176   { 177   {
HITGIC 177   return vt_->context(ex_); 178   21 return vt_->context(ex_);
178   } 179   }
179   180  
180   /** Informs the executor that work is beginning. 181   /** Informs the executor that work is beginning.
181   182  
182   Must be paired with a subsequent call to `on_work_finished()`. 183   Must be paired with a subsequent call to `on_work_finished()`.
183   184  
184   @pre This instance was constructed with a valid executor. 185   @pre This instance was constructed with a valid executor.
185   */ 186   */
MISUIC 186   void on_work_started() const noexcept 187   void on_work_started() const noexcept
187   { 188   {
MISUIC 188   vt_->on_work_started(ex_); 189   vt_->on_work_started(ex_);
MISUIC 189   } 190   }
190   191  
191   /** Informs the executor that work has completed. 192   /** Informs the executor that work has completed.
192   193  
193   @pre A preceding call to `on_work_started()` was made. 194   @pre A preceding call to `on_work_started()` was made.
194   @pre This instance was constructed with a valid executor. 195   @pre This instance was constructed with a valid executor.
195   */ 196   */
MISUIC 196   void on_work_finished() const noexcept 197   void on_work_finished() const noexcept
197   { 198   {
MISUIC 198   vt_->on_work_finished(ex_); 199   vt_->on_work_finished(ex_);
MISUIC 199   } 200   }
200   201  
201 - /** Dispatches a coroutine handle through the wrapped executor. 202 + /** Dispatches a continuation through the wrapped executor.
202   203  
203   Returns a handle for symmetric transfer. If running in the 204   Returns a handle for symmetric transfer. If running in the
204 - executor's thread, returns `h`. Otherwise, posts the coroutine 205 + executor's thread, returns `c.h`. Otherwise, posts the
205 - for later execution and returns `std::noop_coroutine()`. 206 + continuation for later execution and returns
  207 + `std::noop_coroutine()`.
206   208  
207 - @param h The coroutine handle to dispatch for resumption. 209 + @param c The continuation to dispatch for resumption.
  210 + Must remain at a stable address until dequeued.
208   211  
209   @return A handle for symmetric transfer or `std::noop_coroutine()`. 212   @return A handle for symmetric transfer or `std::noop_coroutine()`.
210   213  
211   @pre This instance was constructed with a valid executor. 214   @pre This instance was constructed with a valid executor.
212   */ 215   */
HITCBC 213 - 134 std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const 216 + 104 std::coroutine_handle<> dispatch(continuation& c) const
214   { 217   {
HITCBC 215 - 134 return vt_->dispatch(ex_, h); 218 + 104 return vt_->dispatch(ex_, c);
216   } 219   }
217   220  
218 - /** Posts a coroutine handle to the wrapped executor. 221 + /** Posts a continuation to the wrapped executor.
219   222  
220 - Posts the coroutine handle to the executor for later execution 223 + Posts the continuation to the executor for later execution
221   and returns. The caller should transfer to `std::noop_coroutine()` 224   and returns. The caller should transfer to `std::noop_coroutine()`
222   after calling this. 225   after calling this.
223   226  
224 - @param h The coroutine handle to post for resumption. 227 + @param c The continuation to post for resumption.
  228 + Must remain at a stable address until dequeued.
225   229  
226   @pre This instance was constructed with a valid executor. 230   @pre This instance was constructed with a valid executor.
227   */ 231   */
HITCBC 228 - 439 void post(std::coroutine_handle<> h) const 232 + 20195 void post(continuation& c) const
229   { 233   {
HITCBC 230 - 439 vt_->post(ex_, h); 234 + 20195 vt_->post(ex_, c);
HITCBC 231   439 } 235   20195 }
232   236  
233   /** Compares two executor references for equality. 237   /** Compares two executor references for equality.
234   238  
235   Two `executor_ref` instances are equal if they wrap 239   Two `executor_ref` instances are equal if they wrap
236   executors of the same type that compare equal. 240   executors of the same type that compare equal.
237   241  
238   @param other The executor reference to compare against. 242   @param other The executor reference to compare against.
239   243  
240   @return `true` if both wrap equal executors of the same type. 244   @return `true` if both wrap equal executors of the same type.
241   */ 245   */
HITCBC 242   6 bool operator==(executor_ref const& other) const noexcept 246   6 bool operator==(executor_ref const& other) const noexcept
243   { 247   {
HITCBC 244   6 if (ex_ == other.ex_) 248   6 if (ex_ == other.ex_)
HITCBC 245   5 return true; 249   5 return true;
HITCBC 246   1 if (vt_ != other.vt_) 250   1 if (vt_ != other.vt_)
MISUBC 247   return false; 251   return false;
HITCBC 248   1 return vt_->equals(ex_, other.ex_); 252   1 return vt_->equals(ex_, other.ex_);
249   } 253   }
250   254  
251   /** Return a pointer to the wrapped executor if it matches 255   /** Return a pointer to the wrapped executor if it matches
252   the requested type. 256   the requested type.
253   257  
254   Performs a type check against the stored executor and 258   Performs a type check against the stored executor and
255   returns a typed pointer when the types match, or 259   returns a typed pointer when the types match, or
256   `nullptr` otherwise. Analogous to 260   `nullptr` otherwise. Analogous to
257   `std::any_cast< Executor >( &a )`. 261   `std::any_cast< Executor >( &a )`.
258   262  
259   @tparam Executor The executor type to retrieve. 263   @tparam Executor The executor type to retrieve.
260   264  
261   @return A pointer to the underlying executor, or 265   @return A pointer to the underlying executor, or
262   `nullptr` if the type does not match. 266   `nullptr` if the type does not match.
263   */ 267   */
264   template< typename Executor > 268   template< typename Executor >
HITCBC 265   1 const Executor* target() const 269   1 const Executor* target() const
266   { 270   {
HITCBC 267   1 if ( *vt_->type_id == detail::type_id< Executor >() ) 271   1 if ( *vt_->type_id == detail::type_id< Executor >() )
HITCBC 268   1 return static_cast< Executor const* >( ex_ ); 272   1 return static_cast< Executor const* >( ex_ );
MISUBC 269   return nullptr; 273   return nullptr;
270   } 274   }
271   275  
272   /// @copydoc target() const 276   /// @copydoc target() const
273   template< typename Executor> 277   template< typename Executor>
HITCBC 274   2 Executor* target() 278   2 Executor* target()
275   { 279   {
HITCBC 276   2 if ( *vt_->type_id == detail::type_id< Executor >() ) 280   2 if ( *vt_->type_id == detail::type_id< Executor >() )
277   return const_cast< Executor* >( 281   return const_cast< Executor* >(
HITCBC 278   1 static_cast< Executor const* >( ex_ )); 282   1 static_cast< Executor const* >( ex_ ));
HITCBC 279   1 return nullptr; 283   1 return nullptr;
280   } 284   }
281   }; 285   };
282   286  
283   } // capy 287   } // capy
284   } // boost 288   } // boost
285   289  
286   #endif 290   #endif