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