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   37044 [](void const* p, continuation& c) { 57   35424 [](void const* p, continuation& c) {
HITCBC 58   18522 static_cast<Ex const*>(p)->post(c); 58   17712 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 - @code 98 + @par !example example
99 - void store_executor(executor_ref ex)  
100 - {  
101 - if(ex)  
102 - ex.post(my_continuation);  
103 - }  
104 - thread_pool ctx;  
105 - store_executor(ctx.get_executor());  
106 - @endcode  
107   99  
108   100  
109   @see any_executor, Executor 101   @see any_executor, Executor
110   */ 102   */
111   class executor_ref 103   class executor_ref
112   { 104   {
113   void const* ex_ = nullptr; 105   void const* ex_ = nullptr;
114   detail::executor_vtable const* vt_ = nullptr; 106   detail::executor_vtable const* vt_ = nullptr;
115   107  
116   public: 108   public:
117   /** Construct a default instance. 109   /** Construct a default instance.
118   110  
119   Constructs an empty `executor_ref`. `operator bool()` and 111   Constructs an empty `executor_ref`. `operator bool()` and
120   `operator==()` report the empty state; `context()`, 112   `operator==()` report the empty state; `context()`,
121   `on_work_started()`, `on_work_finished()`, `dispatch()`, 113   `on_work_started()`, `on_work_finished()`, `dispatch()`,
122   `post()`, and `target()` are undefined behavior until an 114   `post()`, and `target()` are undefined behavior until an
123   executor is assigned. 115   executor is assigned.
124   */ 116   */
HITCBC 125   3534 executor_ref() = default; 117   3546 executor_ref() = default;
126   118  
127   /** Construct a copy. 119   /** Construct a copy.
128   120  
129   Copies the internal pointers, preserving identity. 121   Copies the internal pointers, preserving identity.
130   This enables the same-executor optimization when passing 122   This enables the same-executor optimization when passing
131   executor_ref through coroutine chains. 123   executor_ref through coroutine chains.
132   124  
133   @param other The reference to copy. 125   @param other The reference to copy.
134   */ 126   */
135   executor_ref(executor_ref const& other) = default; 127   executor_ref(executor_ref const& other) = default;
136   128  
137   /** Copy assignment operator. 129   /** Copy assignment operator.
138   130  
139   @param other The reference to copy. 131   @param other The reference to copy.
140   132  
141   @return A reference to `*this`. 133   @return A reference to `*this`.
142   */ 134   */
143   executor_ref& operator=(executor_ref const& other) = default; 135   executor_ref& operator=(executor_ref const& other) = default;
144   136  
145   /** Constructs from any executor type. 137   /** Constructs from any executor type.
146   138  
147   Captures a reference to the given executor and stores a pointer 139   Captures a reference to the given executor and stores a pointer
148   to the type-specific vtable. The executor must remain valid for 140   to the type-specific vtable. The executor must remain valid for
149   the lifetime of this `executor_ref` instance. 141   the lifetime of this `executor_ref` instance.
150   142  
151   @param ex The executor to wrap. Must satisfy the `Executor` 143   @param ex The executor to wrap. Must satisfy the `Executor`
152   concept. A pointer to this object is stored 144   concept. A pointer to this object is stored
153   internally; the executor must outlive this wrapper. 145   internally; the executor must outlive this wrapper.
154   */ 146   */
155   #if defined(__GNUC__) && !defined(__clang__) 147   #if defined(__GNUC__) && !defined(__clang__)
156   // GCC constraint satisfaction caching bug workaround 148   // GCC constraint satisfaction caching bug workaround
157   template<class Ex, 149   template<class Ex,
158   std::enable_if_t<!std::is_same_v< 150   std::enable_if_t<!std::is_same_v<
159   std::decay_t<Ex>, executor_ref>, int> = 0> 151   std::decay_t<Ex>, executor_ref>, int> = 0>
160   #else 152   #else
161   template<class Ex> 153   template<class Ex>
162   requires (!std::same_as<std::decay_t<Ex>, executor_ref>) 154   requires (!std::same_as<std::decay_t<Ex>, executor_ref>)
163   #endif 155   #endif
HITCBC 164   32433 executor_ref(Ex const& ex) noexcept 156   32439 executor_ref(Ex const& ex) noexcept
HITCBC 165   32433 : ex_(&ex) 157   32439 : ex_(&ex)
HITCBC 166   32433 , vt_(&detail::vtable_for<Ex>) 158   32439 , vt_(&detail::vtable_for<Ex>)
167   { 159   {
HITCBC 168   32433 } 160   32439 }
169   161  
170   /** Returns true if this instance holds a valid executor. 162   /** Returns true if this instance holds a valid executor.
171   163  
172   @return `true` if constructed with an executor, `false` if 164   @return `true` if constructed with an executor, `false` if
173   default-constructed. 165   default-constructed.
174   */ 166   */
HITCBC 175   6 explicit operator bool() const noexcept 167   6 explicit operator bool() const noexcept
176   { 168   {
HITCBC 177   6 return ex_ != nullptr; 169   6 return ex_ != nullptr;
178   } 170   }
179   171  
180   /** Returns a reference to the associated execution context. 172   /** Returns a reference to the associated execution context.
181   173  
182   @return A reference to the execution context. 174   @return A reference to the execution context.
183   175  
184   @pre This instance was constructed with a valid executor. 176   @pre This instance was constructed with a valid executor.
185   */ 177   */
HITCBC 186   1 execution_context& context() const noexcept 178   1 execution_context& context() const noexcept
187   { 179   {
HITCBC 188   1 return vt_->context(ex_); 180   1 return vt_->context(ex_);
189   } 181   }
190   182  
191   /** Informs the executor that work is beginning. 183   /** Informs the executor that work is beginning.
192   184  
193   Must be paired with a subsequent call to `on_work_finished()`. 185   Must be paired with a subsequent call to `on_work_finished()`.
194   186  
195   @pre This instance was constructed with a valid executor. 187   @pre This instance was constructed with a valid executor.
196   */ 188   */
HITCBC 197   1 void on_work_started() const noexcept 189   1 void on_work_started() const noexcept
198   { 190   {
HITCBC 199   1 vt_->on_work_started(ex_); 191   1 vt_->on_work_started(ex_);
HITCBC 200   1 } 192   1 }
201   193  
202   /** Informs the executor that work has completed. 194   /** Informs the executor that work has completed.
203   195  
204   @pre A preceding call to `on_work_started()` was made. 196   @pre A preceding call to `on_work_started()` was made.
205   @pre This instance was constructed with a valid executor. 197   @pre This instance was constructed with a valid executor.
206   */ 198   */
HITCBC 207   1 void on_work_finished() const noexcept 199   1 void on_work_finished() const noexcept
208   { 200   {
HITCBC 209   1 vt_->on_work_finished(ex_); 201   1 vt_->on_work_finished(ex_);
HITCBC 210   1 } 202   1 }
211   203  
212   /** Dispatches a continuation through the wrapped executor. 204   /** Dispatches a continuation through the wrapped executor.
213   205  
214   Returns a handle for symmetric transfer. If running in the 206   Returns a handle for symmetric transfer. If running in the
215   executor's thread, returns `c.h`. Otherwise, posts the 207   executor's thread, returns `c.h`. Otherwise, posts the
216   continuation for later execution and returns 208   continuation for later execution and returns
217   `std::noop_coroutine()`. 209   `std::noop_coroutine()`.
218   210  
219   @param c The continuation to dispatch for resumption. 211   @param c The continuation to dispatch for resumption.
220   Must remain at a stable address until dequeued. 212   Must remain at a stable address until dequeued.
221   213  
222   @return A handle for symmetric transfer or `std::noop_coroutine()`. 214   @return A handle for symmetric transfer or `std::noop_coroutine()`.
223   215  
224   @pre This instance was constructed with a valid executor. 216   @pre This instance was constructed with a valid executor.
225   */ 217   */
HITCBC 226   122 std::coroutine_handle<> dispatch(continuation& c) const 218   122 std::coroutine_handle<> dispatch(continuation& c) const
227   { 219   {
HITCBC 228   122 return vt_->dispatch(ex_, c); 220   122 return vt_->dispatch(ex_, c);
229   } 221   }
230   222  
231   /** Posts a continuation to the wrapped executor. 223   /** Posts a continuation to the wrapped executor.
232   224  
233   Posts the continuation to the executor for later execution 225   Posts the continuation to the executor for later execution
234   and returns. The caller should transfer to `std::noop_coroutine()` 226   and returns. The caller should transfer to `std::noop_coroutine()`
235   after calling this. 227   after calling this.
236   228  
237   @param c The continuation to post for resumption. 229   @param c The continuation to post for resumption.
238   Must remain at a stable address until dequeued. 230   Must remain at a stable address until dequeued.
239   231  
240   @pre This instance was constructed with a valid executor. 232   @pre This instance was constructed with a valid executor.
241   */ 233   */
HITCBC 242   18522 void post(continuation& c) const 234   17712 void post(continuation& c) const
243   { 235   {
HITCBC 244   18522 vt_->post(ex_, c); 236   17712 vt_->post(ex_, c);
HITCBC 245   18522 } 237   17712 }
246   238  
247   /** Compares two executor references for equality. 239   /** Compares two executor references for equality.
248   240  
249   Two `executor_ref` instances are equal if they wrap 241   Two `executor_ref` instances are equal if they wrap
250   executors of the same type that compare equal. 242   executors of the same type that compare equal.
251   243  
252   @param other The executor reference to compare against. 244   @param other The executor reference to compare against.
253   245  
254   @return `true` if both wrap equal executors of the same type. 246   @return `true` if both wrap equal executors of the same type.
255   */ 247   */
HITCBC 256   7 bool operator==(executor_ref const& other) const noexcept 248   7 bool operator==(executor_ref const& other) const noexcept
257   { 249   {
HITCBC 258   7 if (ex_ == other.ex_) 250   7 if (ex_ == other.ex_)
HITCBC 259   5 return true; 251   5 return true;
HITCBC 260   2 if (vt_ != other.vt_) 252   2 if (vt_ != other.vt_)
HITCBC 261   1 return false; 253   1 return false;
HITCBC 262   1 return vt_->equals(ex_, other.ex_); 254   1 return vt_->equals(ex_, other.ex_);
263   } 255   }
264   256  
265   /** Return a pointer to the wrapped executor if it matches 257   /** Return a pointer to the wrapped executor if it matches
266   the requested type. 258   the requested type.
267   259  
268   Performs a type check against the stored executor and 260   Performs a type check against the stored executor and
269   returns a typed pointer when the types match, or 261   returns a typed pointer when the types match, or
270   `nullptr` otherwise. Analogous to 262   `nullptr` otherwise. Analogous to
271   `std::any_cast< Executor >( &a )`. 263   `std::any_cast< Executor >( &a )`.
272   264  
273   @tparam Executor The executor type to retrieve. 265   @tparam Executor The executor type to retrieve.
274   266  
275   @return A pointer to the underlying executor, or 267   @return A pointer to the underlying executor, or
276   `nullptr` if the type does not match. 268   `nullptr` if the type does not match.
277   */ 269   */
278   template< typename Executor > 270   template< typename Executor >
HITCBC 279   2 const Executor* target() const 271   2 const Executor* target() const
280   { 272   {
HITCBC 281   2 if ( *vt_->type_id == detail::type_id< Executor >() ) 273   2 if ( *vt_->type_id == detail::type_id< Executor >() )
HITCBC 282   1 return static_cast< Executor const* >( ex_ ); 274   1 return static_cast< Executor const* >( ex_ );
HITCBC 283   1 return nullptr; 275   1 return nullptr;
284   } 276   }
285   277  
286   /// @copydoc target() const 278   /// @copydoc target() const
287   template< typename Executor> 279   template< typename Executor>
HITCBC 288   2 Executor* target() 280   2 Executor* target()
289   { 281   {
HITCBC 290   2 if ( *vt_->type_id == detail::type_id< Executor >() ) 282   2 if ( *vt_->type_id == detail::type_id< Executor >() )
291   return const_cast< Executor* >( 283   return const_cast< Executor* >(
HITCBC 292   1 static_cast< Executor const* >( ex_ )); 284   1 static_cast< Executor const* >( ex_ ));
HITCBC 293   1 return nullptr; 285   1 return nullptr;
294   } 286   }
295   }; 287   };
296   288  
297   } // capy 289   } // capy
298   } // boost 290   } // boost
299   291  
300   #endif 292   #endif