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