100.00% Lines (15/15) 100.00% Functions (5/5)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
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/corosio 8   // Official repository: https://github.com/cppalliance/corosio
9   // 9   //
10   10  
11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_RESOLVER_HPP 11   #ifndef BOOST_COROSIO_NATIVE_NATIVE_RESOLVER_HPP
12   #define BOOST_COROSIO_NATIVE_NATIVE_RESOLVER_HPP 12   #define BOOST_COROSIO_NATIVE_NATIVE_RESOLVER_HPP
13   13  
14   #include <boost/corosio/resolver.hpp> 14   #include <boost/corosio/resolver.hpp>
15   #include <boost/corosio/backend.hpp> 15   #include <boost/corosio/backend.hpp>
  16 + #include <boost/corosio/detail/op_base.hpp>
16   17  
17   #ifndef BOOST_COROSIO_MRDOCS 18   #ifndef BOOST_COROSIO_MRDOCS
18   #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \ 19   #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \
19   BOOST_COROSIO_HAS_KQUEUE 20   BOOST_COROSIO_HAS_KQUEUE
20   #include <boost/corosio/native/detail/posix/posix_resolver_service.hpp> 21   #include <boost/corosio/native/detail/posix/posix_resolver_service.hpp>
21   #endif 22   #endif
22   23  
23   #if BOOST_COROSIO_HAS_IOCP 24   #if BOOST_COROSIO_HAS_IOCP
24   #include <boost/corosio/native/detail/iocp/win_resolver_service.hpp> 25   #include <boost/corosio/native/detail/iocp/win_resolver_service.hpp>
25   #endif 26   #endif
26   #endif // !BOOST_COROSIO_MRDOCS 27   #endif // !BOOST_COROSIO_MRDOCS
27   28  
28   namespace boost::corosio { 29   namespace boost::corosio {
29   30  
30   /** An asynchronous DNS resolver with devirtualized operations. 31   /** An asynchronous DNS resolver with devirtualized operations.
31   32  
32   This class template inherits from @ref resolver and shadows 33   This class template inherits from @ref resolver and shadows
33   the `resolve` operations with versions that call the backend 34   the `resolve` operations with versions that call the backend
34   implementation directly, allowing the compiler to inline 35   implementation directly, allowing the compiler to inline
35   through the entire call chain. 36   through the entire call chain.
36   37  
37   Non-async operations (`cancel`) remain unchanged and dispatch 38   Non-async operations (`cancel`) remain unchanged and dispatch
38   through the compiled library. 39   through the compiled library.
39   40  
40   A `native_resolver` IS-A `resolver` and can be passed to any 41   A `native_resolver` IS-A `resolver` and can be passed to any
41   function expecting `resolver&`. 42   function expecting `resolver&`.
42   43  
43   @tparam Backend A backend tag value (e.g., `epoll`). 44   @tparam Backend A backend tag value (e.g., `epoll`).
44   45  
45   @par Thread Safety 46   @par Thread Safety
46   Same as @ref resolver. 47   Same as @ref resolver.
47   48  
48   @see resolver, epoll_t, iocp_t 49   @see resolver, epoll_t, iocp_t
49   */ 50   */
50   template<auto Backend> 51   template<auto Backend>
51   class native_resolver : public resolver 52   class native_resolver : public resolver
52   { 53   {
53   using backend_type = decltype(Backend); 54   using backend_type = decltype(Backend);
54   using impl_type = typename backend_type::resolver_type; 55   using impl_type = typename backend_type::resolver_type;
55   56  
HITCBC 56   4 impl_type& get_impl() noexcept 57   2 impl_type& get_impl() noexcept
57   { 58   {
HITCBC 58   4 return *static_cast<impl_type*>(h_.get()); 59   2 return *static_cast<impl_type*>(h_.get());
59   } 60   }
60   61  
61   struct native_resolve_awaitable 62   struct native_resolve_awaitable
  63 + : detail::value_op_base<native_resolve_awaitable, resolver_results>
62   { 64   {
63   native_resolver& self_; 65   native_resolver& self_;
64   std::string host_; 66   std::string host_;
65   std::string service_; 67   std::string service_;
66 - std::stop_token token_;  
67 - mutable std::error_code ec_;  
68 - mutable resolver_results results_;  
69   resolve_flags flags_; 68   resolve_flags flags_;
70   69  
HITCBC 71   4 native_resolve_awaitable( 70   4 native_resolve_awaitable(
72   native_resolver& self, 71   native_resolver& self,
73   std::string_view host, 72   std::string_view host,
74   std::string_view service, 73   std::string_view service,
75   resolve_flags flags) noexcept 74   resolve_flags flags) noexcept
HITCBC 76   4 : self_(self) 75   4 : self_(self)
HITCBC 77   8 , host_(host) 76   8 , host_(host)
HITCBC 78   8 , service_(service) 77   8 , service_(service)
HITCBC 79   4 , flags_(flags) 78   4 , flags_(flags)
80   { 79   {
HITCBC 81   4 } 80   4 }
82   81  
ECB 83 - 4 bool await_ready() const noexcept 82 + std::coroutine_handle<>
HITGIC 84 - { 83 + 2 dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
DCB 85 - 4 return static_cast<bool>(ec_) || token_.stop_requested();  
86 - }  
87 -  
88 - [[nodiscard]] capy::io_result<resolver_results>  
DCB 89 - 4 await_resume() const noexcept  
90 - {  
DCB 91 - 4 if (token_.stop_requested())  
DCB 92 - 2 return {make_error_code(std::errc::operation_canceled), {}};  
DCB 93 - 2 return {ec_, std::move(results_)};  
94 - }  
95 -  
DCB 96 - 4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)  
97 - -> std::coroutine_handle<>  
98 - token_ = env->stop_token;  
ECB 99   4 { 84   {
HITCBC 100   12 return self_.get_impl().resolve( 85   6 return self_.get_impl().resolve(
HITCBC 101 - 4 h, env->executor, host_, service_, flags_, token_, &ec_, 86 + 2 h, ex, host_, service_, flags_, this->token_, &this->ec_,
HITCBC 102 - 8 &results_); 87 + 4 &this->value_);
103   } 88   }
104   }; 89   };
105   90  
106   struct native_reverse_awaitable 91   struct native_reverse_awaitable
  92 + : detail::
  93 + value_op_base<native_reverse_awaitable, reverse_resolver_result>
107   { 94   {
108   native_resolver& self_; 95   native_resolver& self_;
109   endpoint ep_; 96   endpoint ep_;
110 - std::stop_token token_;  
111 - mutable std::error_code ec_;  
112 - mutable reverse_resolver_result result_;  
113   reverse_flags flags_; 97   reverse_flags flags_;
114   98  
115   native_reverse_awaitable( 99   native_reverse_awaitable(
116   native_resolver& self, 100   native_resolver& self,
117   endpoint const& ep, 101   endpoint const& ep,
118   reverse_flags flags) noexcept 102   reverse_flags flags) noexcept
119   : self_(self) 103   : self_(self)
120   , ep_(ep) 104   , ep_(ep)
121   , flags_(flags) 105   , flags_(flags)
122   { 106   {
123   } 107   }
124   108  
125 - bool await_ready() const noexcept 109 + std::coroutine_handle<>
126 - { 110 + dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
127 - return static_cast<bool>(ec_) || token_.stop_requested();  
128 - }  
129 -  
130 - [[nodiscard]] capy::io_result<reverse_resolver_result>  
131 - await_resume() const noexcept  
132 - {  
133 - if (token_.stop_requested())  
134 - return {make_error_code(std::errc::operation_canceled), {}};  
135 - return {ec_, std::move(result_)};  
136 - }  
137 -  
138 - auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)  
139 - -> std::coroutine_handle<>  
140 - token_ = env->stop_token;  
141   { 111   {
142   return self_.get_impl().reverse_resolve( 112   return self_.get_impl().reverse_resolve(
143 - h, env->executor, ep_, flags_, token_, &ec_, &result_); 113 + h, ex, ep_, flags_, this->token_, &this->ec_, &this->value_);
144   } 114   }
145   }; 115   };
146   116  
147   public: 117   public:
148   /** Construct a native resolver from an execution context. 118   /** Construct a native resolver from an execution context.
149   119  
150   @param ctx The execution context that will own this resolver. 120   @param ctx The execution context that will own this resolver.
151   */ 121   */
HITCBC 152   8 explicit native_resolver(capy::execution_context& ctx) : resolver(ctx) {} 122   8 explicit native_resolver(capy::execution_context& ctx) : resolver(ctx) {}
153   123  
154   /** Construct a native resolver from an executor. 124   /** Construct a native resolver from an executor.
155   125  
156   @param ex The executor whose context will own the resolver. 126   @param ex The executor whose context will own the resolver.
157   */ 127   */
158   template<class Ex> 128   template<class Ex>
159   requires(!std::same_as<std::remove_cvref_t<Ex>, native_resolver>) && 129   requires(!std::same_as<std::remove_cvref_t<Ex>, native_resolver>) &&
160   capy::Executor<Ex> 130   capy::Executor<Ex>
161   explicit native_resolver(Ex const& ex) : native_resolver(ex.context()) 131   explicit native_resolver(Ex const& ex) : native_resolver(ex.context())
162   { 132   {
163   } 133   }
164   134  
165   /** Move construct. 135   /** Move construct.
166   136  
167   @pre No awaitables returned by @p other's `resolve` methods 137   @pre No awaitables returned by @p other's `resolve` methods
168   exist. 138   exist.
169   @pre The execution context associated with @p other must 139   @pre The execution context associated with @p other must
170   outlive this resolver. 140   outlive this resolver.
171   */ 141   */
172   native_resolver(native_resolver&&) noexcept = default; 142   native_resolver(native_resolver&&) noexcept = default;
173   143  
174   /** Move assign. 144   /** Move assign.
175   145  
176   @pre No awaitables returned by either `*this` or the source's 146   @pre No awaitables returned by either `*this` or the source's
177   `resolve` methods exist. 147   `resolve` methods exist.
178   @pre The execution context associated with the source must 148   @pre The execution context associated with the source must
179   outlive this resolver. 149   outlive this resolver.
180   */ 150   */
181   native_resolver& operator=(native_resolver&&) noexcept = default; 151   native_resolver& operator=(native_resolver&&) noexcept = default;
182   152  
183   native_resolver(native_resolver const&) = delete; 153   native_resolver(native_resolver const&) = delete;
184   native_resolver& operator=(native_resolver const&) = delete; 154   native_resolver& operator=(native_resolver const&) = delete;
185   155  
186   /** Asynchronously resolve a host and service to endpoints. 156   /** Asynchronously resolve a host and service to endpoints.
187   157  
188   Calls the backend implementation directly, bypassing virtual 158   Calls the backend implementation directly, bypassing virtual
189   dispatch. Otherwise identical to @ref resolver::resolve. 159   dispatch. Otherwise identical to @ref resolver::resolve.
190   160  
191   This resolver must outlive the returned awaitable. 161   This resolver must outlive the returned awaitable.
192   162  
193   @param host The host name or address string. 163   @param host The host name or address string.
194   @param service The service name or port string. 164   @param service The service name or port string.
195   165  
196   @return An awaitable yielding `io_result<resolver_results>`. 166   @return An awaitable yielding `io_result<resolver_results>`.
197   167  
198   @note `resolver_results` is an alias for `std::vector<resolver_entry>`; 168   @note `resolver_results` is an alias for `std::vector<resolver_entry>`;
199   copying it deep-copies every entry. See @ref resolver::resolve. 169   copying it deep-copies every entry. See @ref resolver::resolve.
200   */ 170   */
HITCBC 201   4 [[nodiscard]] auto resolve(std::string_view host, std::string_view service) 171   4 [[nodiscard]] auto resolve(std::string_view host, std::string_view service)
202   { 172   {
203   return native_resolve_awaitable( 173   return native_resolve_awaitable(
HITCBC 204   4 *this, host, service, resolve_flags::none); 174   4 *this, host, service, resolve_flags::none);
205   } 175   }
206   176  
207   /** Asynchronously resolve a host and service with flags. 177   /** Asynchronously resolve a host and service with flags.
208   178  
209   This resolver must outlive the returned awaitable. 179   This resolver must outlive the returned awaitable.
210   180  
211   @param host The host name or address string. 181   @param host The host name or address string.
212   @param service The service name or port string. 182   @param service The service name or port string.
213   @param flags Flags controlling resolution behavior. 183   @param flags Flags controlling resolution behavior.
214   184  
215   @return An awaitable yielding `io_result<resolver_results>`. 185   @return An awaitable yielding `io_result<resolver_results>`.
216   */ 186   */
217   [[nodiscard]] auto resolve( 187   [[nodiscard]] auto resolve(
218   std::string_view host, std::string_view service, resolve_flags flags) 188   std::string_view host, std::string_view service, resolve_flags flags)
219   { 189   {
220   return native_resolve_awaitable(*this, host, service, flags); 190   return native_resolve_awaitable(*this, host, service, flags);
221   } 191   }
222   192  
223   /** Asynchronously reverse-resolve an endpoint. 193   /** Asynchronously reverse-resolve an endpoint.
224   194  
225   Calls the backend implementation directly, bypassing virtual 195   Calls the backend implementation directly, bypassing virtual
226   dispatch. Otherwise identical to the endpoint overload of 196   dispatch. Otherwise identical to the endpoint overload of
227   @ref resolver::resolve. 197   @ref resolver::resolve.
228   198  
229   This resolver must outlive the returned awaitable. 199   This resolver must outlive the returned awaitable.
230   200  
231   @param ep The endpoint to resolve. 201   @param ep The endpoint to resolve.
232   202  
233   @return An awaitable yielding 203   @return An awaitable yielding
234   `io_result<reverse_resolver_result>`. 204   `io_result<reverse_resolver_result>`.
235   */ 205   */
236   [[nodiscard]] auto resolve(endpoint const& ep) 206   [[nodiscard]] auto resolve(endpoint const& ep)
237   { 207   {
238   return native_reverse_awaitable(*this, ep, reverse_flags::none); 208   return native_reverse_awaitable(*this, ep, reverse_flags::none);
239   } 209   }
240   210  
241   /** Asynchronously reverse-resolve an endpoint with flags. 211   /** Asynchronously reverse-resolve an endpoint with flags.
242   212  
243   This resolver must outlive the returned awaitable. 213   This resolver must outlive the returned awaitable.
244   214  
245   @param ep The endpoint to resolve. 215   @param ep The endpoint to resolve.
246   @param flags Flags controlling resolution behavior. 216   @param flags Flags controlling resolution behavior.
247   217  
248   @return An awaitable yielding 218   @return An awaitable yielding
249   `io_result<reverse_resolver_result>`. 219   `io_result<reverse_resolver_result>`.
250   */ 220   */
251   [[nodiscard]] auto resolve(endpoint const& ep, reverse_flags flags) 221   [[nodiscard]] auto resolve(endpoint const& ep, reverse_flags flags)
252   { 222   {
253   return native_reverse_awaitable(*this, ep, flags); 223   return native_reverse_awaitable(*this, ep, flags);
254   } 224   }
255   }; 225   };
256   226  
257   } // namespace boost::corosio 227   } // namespace boost::corosio
258   228  
259   #endif 229   #endif