75.47% Lines (40/53) 80.00% Functions (16/20)
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_ANY_EXECUTOR_HPP 10   #ifndef BOOST_CAPY_ANY_EXECUTOR_HPP
11   #define BOOST_CAPY_ANY_EXECUTOR_HPP 11   #define BOOST_CAPY_ANY_EXECUTOR_HPP
12   12  
13   #include <boost/capy/detail/config.hpp> 13   #include <boost/capy/detail/config.hpp>
  14 + #include <boost/capy/continuation.hpp>
14   #include <concepts> 15   #include <concepts>
15   #include <coroutine> 16   #include <coroutine>
16   #include <memory> 17   #include <memory>
17   #include <type_traits> 18   #include <type_traits>
18   #include <typeinfo> 19   #include <typeinfo>
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   template<typename> class strand; 25   template<typename> class strand;
25   26  
26   namespace detail { 27   namespace detail {
27   28  
28   template<typename T> 29   template<typename T>
29   struct is_strand_type : std::false_type {}; 30   struct is_strand_type : std::false_type {};
30   31  
31   template<typename E> 32   template<typename E>
32   struct is_strand_type<strand<E>> : std::true_type {}; 33   struct is_strand_type<strand<E>> : std::true_type {};
33   34  
34   } // detail 35   } // detail
35   36  
36   /** A type-erased wrapper for executor objects. 37   /** A type-erased wrapper for executor objects.
37   38  
38   This class provides type erasure for any executor type, enabling 39   This class provides type erasure for any executor type, enabling
39   runtime polymorphism with automatic memory management via shared 40   runtime polymorphism with automatic memory management via shared
40   ownership. It stores a shared pointer to a polymorphic wrapper, 41   ownership. It stores a shared pointer to a polymorphic wrapper,
41   allowing executors of different types to be stored uniformly 42   allowing executors of different types to be stored uniformly
42   while satisfying the full `Executor` concept. 43   while satisfying the full `Executor` concept.
43   44  
44   @par Value Semantics 45   @par Value Semantics
45   46  
46   This class has value semantics with shared ownership. Copy and 47   This class has value semantics with shared ownership. Copy and
47   move operations are cheap, simply copying the internal shared 48   move operations are cheap, simply copying the internal shared
48   pointer. Multiple `any_executor` instances may share the same 49   pointer. Multiple `any_executor` instances may share the same
49   underlying executor. Move operations do not invalidate the 50   underlying executor. Move operations do not invalidate the
50   source; there is no moved-from state. 51   source; there is no moved-from state.
51   52  
52   @par Default State 53   @par Default State
53   54  
54   A default-constructed `any_executor` holds no executor. Calling 55   A default-constructed `any_executor` holds no executor. Calling
55   executor operations on a default-constructed instance results 56   executor operations on a default-constructed instance results
56   in undefined behavior. Use `operator bool()` to check validity. 57   in undefined behavior. Use `operator bool()` to check validity.
57   58  
58   @par Thread Safety 59   @par Thread Safety
59   60  
60   The `any_executor` itself is thread-safe for concurrent reads. 61   The `any_executor` itself is thread-safe for concurrent reads.
61   Concurrent modification requires external synchronization. 62   Concurrent modification requires external synchronization.
62   Executor operations are safe to call concurrently if the 63   Executor operations are safe to call concurrently if the
63   underlying executor supports it. 64   underlying executor supports it.
64   65  
65   @par Executor Concept 66   @par Executor Concept
66   67  
67   This class satisfies the `Executor` concept, making it usable 68   This class satisfies the `Executor` concept, making it usable
68   anywhere a concrete executor is expected. 69   anywhere a concrete executor is expected.
69   70  
70   @par Example 71   @par Example
71   @code 72   @code
72   any_executor exec = ctx.get_executor(); 73   any_executor exec = ctx.get_executor();
73   if(exec) 74   if(exec)
74   { 75   {
75   auto& context = exec.context(); 76   auto& context = exec.context();
76   exec.post(my_coroutine); 77   exec.post(my_coroutine);
77   } 78   }
78   @endcode 79   @endcode
79   80  
80   @see executor_ref, Executor 81   @see executor_ref, Executor
81   */ 82   */
82   class any_executor 83   class any_executor
83   { 84   {
84   struct impl_base; 85   struct impl_base;
85   86  
86   std::shared_ptr<impl_base> p_; 87   std::shared_ptr<impl_base> p_;
87   88  
88   struct impl_base 89   struct impl_base
89   { 90   {
HITCBC 90   16 virtual ~impl_base() = default; 91   16 virtual ~impl_base() = default;
91   virtual execution_context& context() const noexcept = 0; 92   virtual execution_context& context() const noexcept = 0;
92   virtual void on_work_started() const noexcept = 0; 93   virtual void on_work_started() const noexcept = 0;
93   virtual void on_work_finished() const noexcept = 0; 94   virtual void on_work_finished() const noexcept = 0;
94 - virtual std::coroutine_handle<> dispatch(std::coroutine_handle<>) const = 0; 95 + virtual std::coroutine_handle<> dispatch(continuation&) const = 0;
95 - virtual void post(std::coroutine_handle<>) const = 0; 96 + virtual void post(continuation&) const = 0;
96   virtual bool equals(impl_base const*) const noexcept = 0; 97   virtual bool equals(impl_base const*) const noexcept = 0;
97   virtual std::type_info const& target_type() const noexcept = 0; 98   virtual std::type_info const& target_type() const noexcept = 0;
98   }; 99   };
99   100  
100   template<class Ex> 101   template<class Ex>
101   struct impl final : impl_base 102   struct impl final : impl_base
102   { 103   {
103   Ex ex_; 104   Ex ex_;
104   105  
105   template<class Ex1> 106   template<class Ex1>
HITCBC 106   16 explicit impl(Ex1&& ex) 107   16 explicit impl(Ex1&& ex)
HITCBC 107   16 : ex_(std::forward<Ex1>(ex)) 108   16 : ex_(std::forward<Ex1>(ex))
108   { 109   {
HITCBC 109   16 } 110   16 }
110   111  
HITCBC 111   5 execution_context& context() const noexcept override 112   5 execution_context& context() const noexcept override
112   { 113   {
HITCBC 113   5 return const_cast<Ex&>(ex_).context(); 114   5 return const_cast<Ex&>(ex_).context();
114   } 115   }
115   116  
MISUBC 116   void on_work_started() const noexcept override 117   void on_work_started() const noexcept override
117   { 118   {
MISUBC 118   ex_.on_work_started(); 119   ex_.on_work_started();
MISUBC 119   } 120   }
120   121  
MISUBC 121   void on_work_finished() const noexcept override 122   void on_work_finished() const noexcept override
122   { 123   {
MISUBC 123   ex_.on_work_finished(); 124   ex_.on_work_finished();
MISUBC 124   } 125   }
125   126  
HITCBC 126 - 1 std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const override 127 + 1 std::coroutine_handle<> dispatch(continuation& c) const override
127   { 128   {
HITCBC 128 - 1 return ex_.dispatch(h); 129 + 1 return ex_.dispatch(c);
129   } 130   }
130   131  
HITCBC 131 - 15 void post(std::coroutine_handle<> h) const override 132 + 15 void post(continuation& c) const override
132   { 133   {
HITCBC 133 - 15 ex_.post(h); 134 + 15 ex_.post(c);
HITCBC 134   15 } 135   15 }
135   136  
HITCBC 136   8 bool equals(impl_base const* other) const noexcept override 137   8 bool equals(impl_base const* other) const noexcept override
137   { 138   {
HITCBC 138   8 if(target_type() != other->target_type()) 139   8 if(target_type() != other->target_type())
MISUBC 139   return false; 140   return false;
HITCBC 140   8 return ex_ == static_cast<impl const*>(other)->ex_; 141   8 return ex_ == static_cast<impl const*>(other)->ex_;
141   } 142   }
142   143  
HITCBC 143   17 std::type_info const& target_type() const noexcept override 144   17 std::type_info const& target_type() const noexcept override
144   { 145   {
HITCBC 145   17 return typeid(Ex); 146   17 return typeid(Ex);
146   } 147   }
147   }; 148   };
148   149  
149   public: 150   public:
150 - /** Default constructor. 151 + /** Construct a default instance.
151   152  
152   Constructs an empty `any_executor`. Calling any executor 153   Constructs an empty `any_executor`. Calling any executor
153   operations on a default-constructed instance results in 154   operations on a default-constructed instance results in
154   undefined behavior. 155   undefined behavior.
155   156  
156   @par Postconditions 157   @par Postconditions
157   @li `!*this` 158   @li `!*this`
158   */ 159   */
159   any_executor() = default; 160   any_executor() = default;
160   161  
161 - /** Copy constructor. 162 + /** Construct a copy.
162   163  
163   Creates a new `any_executor` sharing ownership of the 164   Creates a new `any_executor` sharing ownership of the
164   underlying executor with `other`. 165   underlying executor with `other`.
165   166  
166   @par Postconditions 167   @par Postconditions
167   @li `*this == other` 168   @li `*this == other`
168   */ 169   */
HITCBC 169   9 any_executor(any_executor const&) = default; 170   9 any_executor(any_executor const&) = default;
170   171  
171   /** Copy assignment operator. 172   /** Copy assignment operator.
172   173  
173   Shares ownership of the underlying executor with `other`. 174   Shares ownership of the underlying executor with `other`.
174   175  
175   @par Postconditions 176   @par Postconditions
176   @li `*this == other` 177   @li `*this == other`
177   */ 178   */
HITCBC 178   2 any_executor& operator=(any_executor const&) = default; 179   2 any_executor& operator=(any_executor const&) = default;
179   180  
180   /** Constructs from any executor type. 181   /** Constructs from any executor type.
181   182  
182   Allocates storage for a copy of the given executor and 183   Allocates storage for a copy of the given executor and
183   stores it internally. The executor must satisfy the 184   stores it internally. The executor must satisfy the
184   `Executor` concept. 185   `Executor` concept.
185   186  
186   @param ex The executor to wrap. A copy is stored internally. 187   @param ex The executor to wrap. A copy is stored internally.
187   188  
188   @par Postconditions 189   @par Postconditions
189   @li `*this` is valid 190   @li `*this` is valid
190   */ 191   */
191   template<class Ex> 192   template<class Ex>
192   requires ( 193   requires (
193   !std::same_as<std::decay_t<Ex>, any_executor> && 194   !std::same_as<std::decay_t<Ex>, any_executor> &&
194   !detail::is_strand_type<std::decay_t<Ex>>::value && 195   !detail::is_strand_type<std::decay_t<Ex>>::value &&
195   std::copy_constructible<std::decay_t<Ex>>) 196   std::copy_constructible<std::decay_t<Ex>>)
HITCBC 196   16 any_executor(Ex&& ex) 197   16 any_executor(Ex&& ex)
HITCBC 197   16 : p_(std::make_shared<impl<std::decay_t<Ex>>>(std::forward<Ex>(ex))) 198   16 : p_(std::make_shared<impl<std::decay_t<Ex>>>(std::forward<Ex>(ex)))
198   { 199   {
HITCBC 199   16 } 200   16 }
200   201  
201   /** Returns true if this instance holds a valid executor. 202   /** Returns true if this instance holds a valid executor.
202   203  
203   @return `true` if constructed with an executor, `false` if 204   @return `true` if constructed with an executor, `false` if
204   default-constructed. 205   default-constructed.
205   */ 206   */
HITCBC 206   6 explicit operator bool() const noexcept 207   6 explicit operator bool() const noexcept
207   { 208   {
HITCBC 208   6 return p_ != nullptr; 209   6 return p_ != nullptr;
209   } 210   }
210   211  
211   /** Returns a reference to the associated execution context. 212   /** Returns a reference to the associated execution context.
212   213  
213   @return A reference to the execution context. 214   @return A reference to the execution context.
214   215  
215   @pre This instance holds a valid executor. 216   @pre This instance holds a valid executor.
216   */ 217   */
HITCBC 217   5 execution_context& context() const noexcept 218   5 execution_context& context() const noexcept
218   { 219   {
HITCBC 219   5 return p_->context(); 220   5 return p_->context();
220   } 221   }
221   222  
222   /** Informs the executor that work is beginning. 223   /** Informs the executor that work is beginning.
223   224  
224   Must be paired with a subsequent call to `on_work_finished()`. 225   Must be paired with a subsequent call to `on_work_finished()`.
225   226  
226   @pre This instance holds a valid executor. 227   @pre This instance holds a valid executor.
227   */ 228   */
MISUBC 228   void on_work_started() const noexcept 229   void on_work_started() const noexcept
229   { 230   {
MISUBC 230   p_->on_work_started(); 231   p_->on_work_started();
MISUBC 231   } 232   }
232   233  
233   /** Informs the executor that work has completed. 234   /** Informs the executor that work has completed.
234   235  
235   @pre A preceding call to `on_work_started()` was made. 236   @pre A preceding call to `on_work_started()` was made.
236   @pre This instance holds a valid executor. 237   @pre This instance holds a valid executor.
237   */ 238   */
MISUBC 238   void on_work_finished() const noexcept 239   void on_work_finished() const noexcept
239   { 240   {
MISUBC 240   p_->on_work_finished(); 241   p_->on_work_finished();
MISUBC 241   } 242   }
242   243  
243 - /** Dispatches a coroutine handle through the wrapped executor. 244 + /** Dispatches a continuation through the wrapped executor.
244   245  
245   Returns a handle for symmetric transfer. If running in the 246   Returns a handle for symmetric transfer. If running in the
246 - executor's thread, returns `h`. Otherwise, posts the coroutine 247 + executor's thread, returns `c.h`. Otherwise, posts the
247 - for later execution and returns `std::noop_coroutine()`. 248 + continuation for later execution and returns
  249 + `std::noop_coroutine()`.
248   250  
249 - @param h The coroutine handle to dispatch for resumption. 251 + @param c The continuation to dispatch for resumption.
  252 + Must remain at a stable address until dequeued.
250   253  
251   @return A handle for symmetric transfer or `std::noop_coroutine()`. 254   @return A handle for symmetric transfer or `std::noop_coroutine()`.
252   255  
253   @pre This instance holds a valid executor. 256   @pre This instance holds a valid executor.
254   */ 257   */
HITCBC 255 - 1 std::coroutine_handle<> dispatch(std::coroutine_handle<> h) const 258 + 1 std::coroutine_handle<> dispatch(continuation& c) const
256   { 259   {
HITCBC 257 - 1 return p_->dispatch(h); 260 + 1 return p_->dispatch(c);
258   } 261   }
259   262  
260 - /** Posts a coroutine handle to the wrapped executor. 263 + /** Posts a continuation to the wrapped executor.
261   264  
262 - Posts the coroutine handle to the executor for later execution 265 + Posts the continuation to the executor for later execution
263   and returns. The caller should transfer to `std::noop_coroutine()` 266   and returns. The caller should transfer to `std::noop_coroutine()`
264   after calling this. 267   after calling this.
265   268  
266 - @param h The coroutine handle to post for resumption. 269 + @param c The continuation to post for resumption.
  270 + Must remain at a stable address until dequeued.
267   271  
268   @pre This instance holds a valid executor. 272   @pre This instance holds a valid executor.
269   */ 273   */
HITCBC 270 - 15 void post(std::coroutine_handle<> h) const 274 + 15 void post(continuation& c) const
271   { 275   {
HITCBC 272 - 15 p_->post(h); 276 + 15 p_->post(c);
HITCBC 273   15 } 277   15 }
274   278  
275   /** Compares two executor wrappers for equality. 279   /** Compares two executor wrappers for equality.
276   280  
277   Two `any_executor` instances are equal if they both hold 281   Two `any_executor` instances are equal if they both hold
278   executors of the same type that compare equal, or if both 282   executors of the same type that compare equal, or if both
279   are empty. 283   are empty.
280   284  
281   @param other The executor to compare against. 285   @param other The executor to compare against.
282   286  
283   @return `true` if both wrap equal executors of the same type, 287   @return `true` if both wrap equal executors of the same type,
284   or both are empty. 288   or both are empty.
285   */ 289   */
HITCBC 286   10 bool operator==(any_executor const& other) const noexcept 290   10 bool operator==(any_executor const& other) const noexcept
287   { 291   {
HITCBC 288   10 if(!p_ && !other.p_) 292   10 if(!p_ && !other.p_)
HITCBC 289   1 return true; 293   1 return true;
HITCBC 290   9 if(!p_ || !other.p_) 294   9 if(!p_ || !other.p_)
HITCBC 291   1 return false; 295   1 return false;
HITCBC 292   8 return p_->equals(other.p_.get()); 296   8 return p_->equals(other.p_.get());
293   } 297   }
294   298  
295   /** Returns the type_info of the wrapped executor. 299   /** Returns the type_info of the wrapped executor.
296   300  
297   @return The `std::type_info` of the stored executor type, 301   @return The `std::type_info` of the stored executor type,
298   or `typeid(void)` if empty. 302   or `typeid(void)` if empty.
299   */ 303   */
HITCBC 300   2 std::type_info const& target_type() const noexcept 304   2 std::type_info const& target_type() const noexcept
301   { 305   {
HITCBC 302   2 if(!p_) 306   2 if(!p_)
HITCBC 303   1 return typeid(void); 307   1 return typeid(void);
HITCBC 304   1 return p_->target_type(); 308   1 return p_->target_type();
305   } 309   }
306   }; 310   };
307   311  
308   } // capy 312   } // capy
309   } // boost 313   } // boost
310   314  
311   #endif 315   #endif