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