Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
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)
6 : //
7 : // Official repository: https://github.com/cppalliance/capy
8 : //
9 :
10 : #ifndef BOOST_CAPY_ANY_DISPATCHER_HPP
11 : #define BOOST_CAPY_ANY_DISPATCHER_HPP
12 :
13 : #include <boost/capy/detail/config.hpp>
14 : #include <boost/capy/ex/any_coro.hpp>
15 : #include <boost/capy/concept/dispatcher.hpp>
16 : #include <boost/capy/concept/executor.hpp>
17 :
18 : #include <concepts>
19 : #include <type_traits>
20 : #include <utility>
21 :
22 : namespace boost {
23 : namespace capy {
24 :
25 : /** A type-erased wrapper for dispatcher objects.
26 :
27 : This class provides type erasure for any type satisfying the `dispatcher`
28 : concept, enabling runtime polymorphism without virtual functions. It stores
29 : a pointer to the original dispatcher and a function pointer to invoke it,
30 : allowing dispatchers of different types to be stored uniformly.
31 :
32 : @par Thread Safety
33 : The `any_dispatcher` itself is not thread-safe for concurrent modification,
34 : but `operator()` is const and safe to call concurrently if the underlying
35 : dispatcher supports concurrent dispatch.
36 :
37 : @par Lifetime
38 : The `any_dispatcher` stores a pointer to the original dispatcher object.
39 : The caller must ensure the referenced dispatcher outlives the `any_dispatcher`
40 : instance. This is typically satisfied when the dispatcher is an executor
41 : stored in a coroutine promise or service provider.
42 :
43 : @see dispatcher
44 : */
45 : class any_dispatcher
46 : {
47 : void const* d_ = nullptr;
48 : any_coro(*f_)(void const*, any_coro) = nullptr;
49 :
50 : public:
51 : /** Default constructor.
52 :
53 : Constructs an empty `any_dispatcher`. Calling `operator()` on a
54 : default-constructed instance results in undefined behavior.
55 : */
56 533 : any_dispatcher() = default;
57 :
58 : /** Copy constructor.
59 :
60 : Copies the internal pointer and function, preserving identity.
61 : This enables the same-dispatcher optimization when passing
62 : any_dispatcher through coroutine chains.
63 : */
64 : any_dispatcher(any_dispatcher const&) = default;
65 :
66 : /** Copy assignment operator. */
67 : any_dispatcher& operator=(any_dispatcher const&) = default;
68 :
69 : /** Constructs from any dispatcher type.
70 :
71 : Captures a reference to the given dispatcher and stores a type-erased
72 : invocation function. The dispatcher must remain valid for the lifetime
73 : of this `any_dispatcher` instance.
74 :
75 : @param d The dispatcher to wrap. Must satisfy the `dispatcher` concept.
76 : A pointer to this object is stored internally; the dispatcher
77 : must outlive this wrapper.
78 : */
79 : template<dispatcher D>
80 : requires (!std::same_as<std::decay_t<D>, any_dispatcher>)
81 454 : any_dispatcher(D const& d)
82 454 : : d_(&d)
83 563 : , f_([](void const* pd, any_coro h) {
84 120 : return static_cast<D const*>(pd)->operator()(h);
85 : })
86 : {
87 454 : }
88 :
89 : /** Returns true if this instance holds a valid dispatcher.
90 :
91 : @return `true` if constructed with a dispatcher, `false` if
92 : default-constructed.
93 : */
94 : explicit operator bool() const noexcept
95 : {
96 : return d_ != nullptr;
97 : }
98 :
99 : /** Compares two dispatchers for identity.
100 :
101 : Two `any_dispatcher` instances are equal if they wrap the same
102 : underlying dispatcher object (pointer equality). This enables
103 : the affinity optimization: when `caller_dispatcher == my_dispatcher`,
104 : symmetric transfer can proceed without a `running_in_this_thread()`
105 : check.
106 :
107 : @param other The dispatcher to compare against.
108 :
109 : @return `true` if both wrap the same dispatcher object.
110 : */
111 : bool operator==(any_dispatcher const& other) const noexcept
112 : {
113 : return d_ == other.d_;
114 : }
115 :
116 : /** Dispatches a coroutine handle through the wrapped dispatcher.
117 :
118 : Invokes the stored dispatcher with the given coroutine handle,
119 : returning a handle suitable for symmetric transfer.
120 :
121 : @param h The coroutine handle to dispatch for resumption.
122 :
123 : @return A coroutine handle that the caller may use for symmetric
124 : transfer, or `std::noop_coroutine()` if the dispatcher
125 : posted the work for later execution.
126 :
127 : @pre This instance was constructed with a valid dispatcher
128 : (not default-constructed).
129 : */
130 120 : any_coro operator()(any_coro h) const
131 : {
132 120 : return f_(d_, h);
133 : }
134 : };
135 :
136 : //------------------------------------------------------------------------------
137 :
138 : /** A dispatcher that calls executor::post().
139 :
140 : Adapts an executor's post() operation to the dispatcher
141 : interface. When invoked, posts the coroutine and returns
142 : noop_coroutine for the caller to transfer to.
143 :
144 : @tparam Executor The executor type.
145 : */
146 : template<executor Executor>
147 : class post_dispatcher
148 : {
149 : Executor ex_;
150 :
151 : public:
152 24 : explicit post_dispatcher(Executor ex) noexcept
153 24 : : ex_(std::move(ex))
154 24 : {}
155 :
156 6 : Executor const& get_inner_executor() const noexcept { return ex_; }
157 :
158 11 : any_coro operator()(any_coro h) const
159 : {
160 11 : ex_.post(h);
161 11 : return std::noop_coroutine();
162 : }
163 : };
164 :
165 : /** A dispatcher that calls executor::defer().
166 :
167 : Adapts an executor's defer() operation to the dispatcher
168 : interface. When invoked, defers the coroutine and returns
169 : noop_coroutine for the caller to transfer to.
170 :
171 : @tparam Executor The executor type.
172 : */
173 : template<executor Executor>
174 : class defer_dispatcher
175 : {
176 : Executor ex_;
177 :
178 : public:
179 : explicit defer_dispatcher(Executor ex) noexcept
180 : : ex_(std::move(ex))
181 : {}
182 :
183 : Executor const& get_inner_executor() const noexcept { return ex_; }
184 :
185 : any_coro operator()(any_coro h) const
186 : {
187 : ex_.defer(h);
188 : return std::noop_coroutine();
189 : }
190 : };
191 :
192 : template<executor E> post_dispatcher(E) -> post_dispatcher<E>;
193 : template<executor E> defer_dispatcher(E) -> defer_dispatcher<E>;
194 :
195 : } // capy
196 : } // boost
197 :
198 : #endif
|