99.30% Lines (283/285) 100.00% Functions (30/30)
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_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP 11   #ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
12   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP 12   #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
13   13  
14   #include <boost/corosio/detail/platform.hpp> 14   #include <boost/corosio/detail/platform.hpp>
15   15  
16   #if BOOST_COROSIO_POSIX 16   #if BOOST_COROSIO_POSIX
17   17  
18   #include <boost/corosio/native/detail/posix/posix_resolver.hpp> 18   #include <boost/corosio/native/detail/posix/posix_resolver.hpp>
19   #include <boost/corosio/native/detail/reactor/reactor_scheduler.hpp> 19   #include <boost/corosio/native/detail/reactor/reactor_scheduler.hpp>
20   #include <boost/corosio/detail/object_pool.hpp> 20   #include <boost/corosio/detail/object_pool.hpp>
21   #include <boost/corosio/detail/object_ref.hpp> 21   #include <boost/corosio/detail/object_ref.hpp>
22   #include <boost/corosio/detail/thread_pool.hpp> 22   #include <boost/corosio/detail/thread_pool.hpp>
23   23  
24   #include <vector> 24   #include <vector>
25   25  
26   namespace boost::corosio::detail { 26   namespace boost::corosio::detail {
27   27  
28   /** Resolver service for POSIX backends. 28   /** Resolver service for POSIX backends.
29   29  
30   Owns all posix_resolver instances. Thread lifecycle is managed 30   Owns all posix_resolver instances. Thread lifecycle is managed
31   by the thread_pool service. 31   by the thread_pool service.
32   */ 32   */
33   class BOOST_COROSIO_DECL posix_resolver_service final 33   class BOOST_COROSIO_DECL posix_resolver_service final
34   : public capy::execution_context::service 34   : public capy::execution_context::service
35   , public io_object::io_service 35   , public io_object::io_service
36   { 36   {
37   friend class posix_resolver; 37   friend class posix_resolver;
38   38  
39   public: 39   public:
40   using key_type = posix_resolver_service; 40   using key_type = posix_resolver_service;
41   41  
HITCBC 42   77 explicit posix_resolver_service(capy::execution_context& ctx) 42   77 explicit posix_resolver_service(capy::execution_context& ctx)
HITCBC 43   231 : sched_(&get_scheduler(ctx)) 43   231 : sched_(&get_scheduler(ctx))
HITCBC 44   77 , pool_(ctx) 44   77 , pool_(ctx)
45   { 45   {
HITCBC 46   77 } 46   77 }
47   47  
HITCBC 48   154 ~posix_resolver_service() override = default; 48   154 ~posix_resolver_service() override = default;
49   49  
50   posix_resolver_service(posix_resolver_service const&) = delete; 50   posix_resolver_service(posix_resolver_service const&) = delete;
51   posix_resolver_service& operator=(posix_resolver_service const&) = delete; 51   posix_resolver_service& operator=(posix_resolver_service const&) = delete;
52   52  
53   io_object::implementation* construct() override; 53   io_object::implementation* construct() override;
54   54  
HITCBC 55   142 void destroy(io_object::implementation* p) override 55   142 void destroy(io_object::implementation* p) override
56   { 56   {
HITCBC 57   142 auto& impl = static_cast<posix_resolver&>(*p); 57   142 auto& impl = static_cast<posix_resolver&>(*p);
HITCBC 58   142 impl.cancel(); 58   142 impl.cancel();
HITCBC 59   142 release(&impl); 59   142 release(&impl);
HITCBC 60   142 } 60   142 }
61   61  
62   void shutdown() override; 62   void shutdown() override;
63   63  
64   void post(scheduler_op* op); 64   void post(scheduler_op* op);
65   65  
66   /** Return the resolver thread pool. 66   /** Return the resolver thread pool.
67   67  
68   The pool's service is created on first use, so this can fail 68   The pool's service is created on first use, so this can fail
69   where a plain accessor could not. Its workers start later, on 69   where a plain accessor could not. Its workers start later, on
70   the first post, and a thread the system refuses there is 70   the first post, and a thread the system refuses there is
71   reported by that post rather than thrown here. 71   reported by that post rather than thrown here.
72   72  
73   @throws std::bad_alloc If the service cannot be allocated. 73   @throws std::bad_alloc If the service cannot be allocated.
74   74  
75   @return The context's shared blocking-I/O pool. 75   @return The context's shared blocking-I/O pool.
76   76  
77   @see thread_pool_ref::get 77   @see thread_pool_ref::get
78   */ 78   */
HITCBC 79   62 thread_pool& pool() 79   62 thread_pool& pool()
80   { 80   {
HITCBC 81   62 return pool_.get(); 81   62 return pool_.get();
82   } 82   }
83   83  
84   /// True when the resolver thread pool is unavailable: the `unsafe` tier, 84   /// True when the resolver thread pool is unavailable: the `unsafe` tier,
85   /// whose lockless scheduler cannot accept the pool's cross-thread 85   /// whose lockless scheduler cannot accept the pool's cross-thread
86   /// completions. 86   /// completions.
HITCBC 87   64 bool resolver_unavailable() const noexcept 87   64 bool resolver_unavailable() const noexcept
88   { 88   {
HITCBC 89   64 return sched_->scheduler_locking_disabled(); 89   64 return sched_->scheduler_locking_disabled();
90   } 90   }
91   91  
92   /// Count an operation as work on this service's context. 92   /// Count an operation as work on this service's context.
HITCBC 93   62 void work_started() noexcept 93   62 void work_started() noexcept
94   { 94   {
HITCBC 95   62 sched_->work_started(); 95   62 sched_->work_started();
HITCBC 96   62 } 96   62 }
97   97  
98   /// Release a count taken by @ref work_started. 98   /// Release a count taken by @ref work_started.
HITCBC 99   62 void work_finished() noexcept 99   62 void work_finished() noexcept
100   { 100   {
HITCBC 101   62 sched_->work_finished(); 101   62 sched_->work_finished();
HITCBC 102   62 } 102   62 }
103   103  
104   private: 104   private:
105   scheduler* sched_; 105   scheduler* sched_;
106   thread_pool_ref pool_; 106   thread_pool_ref pool_;
107   object_pool<posix_resolver> object_pool_; 107   object_pool<posix_resolver> object_pool_;
108   }; 108   };
109   109  
110   // --------------------------------------------------------------------------- 110   // ---------------------------------------------------------------------------
111   // Inline implementation 111   // Inline implementation
112   // --------------------------------------------------------------------------- 112   // ---------------------------------------------------------------------------
113   113  
114   // posix_resolver_detail helpers 114   // posix_resolver_detail helpers
115   115  
116   inline int 116   inline int
HITCBC 117   38 posix_resolver_detail::flags_to_hints(resolve_flags flags) 117   38 posix_resolver_detail::flags_to_hints(resolve_flags flags)
118   { 118   {
HITCBC 119   38 int hints = 0; 119   38 int hints = 0;
120   120  
HITCBC 121   38 if ((flags & resolve_flags::passive) != resolve_flags::none) 121   38 if ((flags & resolve_flags::passive) != resolve_flags::none)
HITCBC 122   1 hints |= AI_PASSIVE; 122   1 hints |= AI_PASSIVE;
HITCBC 123   38 if ((flags & resolve_flags::numeric_host) != resolve_flags::none) 123   38 if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
HITCBC 124   21 hints |= AI_NUMERICHOST; 124   21 hints |= AI_NUMERICHOST;
HITCBC 125   38 if ((flags & resolve_flags::numeric_service) != resolve_flags::none) 125   38 if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
HITCBC 126   15 hints |= AI_NUMERICSERV; 126   15 hints |= AI_NUMERICSERV;
HITCBC 127   38 if ((flags & resolve_flags::address_configured) != resolve_flags::none) 127   38 if ((flags & resolve_flags::address_configured) != resolve_flags::none)
HITCBC 128   1 hints |= AI_ADDRCONFIG; 128   1 hints |= AI_ADDRCONFIG;
HITCBC 129   38 if ((flags & resolve_flags::v4_mapped) != resolve_flags::none) 129   38 if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
HITCBC 130   1 hints |= AI_V4MAPPED; 130   1 hints |= AI_V4MAPPED;
HITCBC 131   38 if ((flags & resolve_flags::all_matching) != resolve_flags::none) 131   38 if ((flags & resolve_flags::all_matching) != resolve_flags::none)
HITCBC 132   1 hints |= AI_ALL; 132   1 hints |= AI_ALL;
133   133  
HITCBC 134   38 return hints; 134   38 return hints;
135   } 135   }
136   136  
137   inline int 137   inline int
HITCBC 138   16 posix_resolver_detail::flags_to_ni_flags(reverse_flags flags) 138   16 posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
139   { 139   {
HITCBC 140   16 int ni_flags = 0; 140   16 int ni_flags = 0;
141   141  
HITCBC 142   16 if ((flags & reverse_flags::numeric_host) != reverse_flags::none) 142   16 if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
HITCBC 143   6 ni_flags |= NI_NUMERICHOST; 143   6 ni_flags |= NI_NUMERICHOST;
HITCBC 144   16 if ((flags & reverse_flags::numeric_service) != reverse_flags::none) 144   16 if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
HITCBC 145   6 ni_flags |= NI_NUMERICSERV; 145   6 ni_flags |= NI_NUMERICSERV;
HITCBC 146   16 if ((flags & reverse_flags::name_required) != reverse_flags::none) 146   16 if ((flags & reverse_flags::name_required) != reverse_flags::none)
HITCBC 147   1 ni_flags |= NI_NAMEREQD; 147   1 ni_flags |= NI_NAMEREQD;
HITCBC 148   16 if ((flags & reverse_flags::datagram_service) != reverse_flags::none) 148   16 if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
HITCBC 149   1 ni_flags |= NI_DGRAM; 149   1 ni_flags |= NI_DGRAM;
150   150  
HITCBC 151   16 return ni_flags; 151   16 return ni_flags;
152   } 152   }
153   153  
154   inline std::vector<endpoint> 154   inline std::vector<endpoint>
HITCBC 155   26 posix_resolver_detail::convert_results(struct addrinfo* ai) 155   25 posix_resolver_detail::convert_results(struct addrinfo* ai)
156   { 156   {
HITCBC 157   26 std::vector<endpoint> endpoints; 157   25 std::vector<endpoint> endpoints;
HITCBC 158   26 endpoints.reserve(4); // Most lookups return 1-4 addresses 158   25 endpoints.reserve(4); // Most lookups return 1-4 addresses
159   159  
HITCBC 160   52 for (auto* p = ai; p != nullptr; p = p->ai_next) 160   50 for (auto* p = ai; p != nullptr; p = p->ai_next)
161   { 161   {
HITCBC 162   26 if (p->ai_family == AF_INET) 162   25 if (p->ai_family == AF_INET)
163   { 163   {
HITCBC 164   23 auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr); 164   22 auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
HITCBC 165   23 endpoints.push_back(from_sockaddr_in(*addr)); 165   22 endpoints.push_back(from_sockaddr_in(*addr));
166   } 166   }
HITCBC 167   3 else if (p->ai_family == AF_INET6) 167   3 else if (p->ai_family == AF_INET6)
168   { 168   {
HITCBC 169   3 auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr); 169   3 auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
HITCBC 170   3 endpoints.push_back(from_sockaddr_in6(*addr)); 170   3 endpoints.push_back(from_sockaddr_in6(*addr));
171   } 171   }
172   } 172   }
173   173  
HITCBC 174   26 return endpoints; 174   25 return endpoints;
MISUBC 175   ✗ } 175   ✗ }
176   176  
177   inline std::error_code 177   inline std::error_code
HITCBC 178   26 posix_resolver_detail::make_gai_error(int gai_err) 178   26 posix_resolver_detail::make_gai_error(int gai_err)
179   { 179   {
180   // Map GAI errors to appropriate generic error codes 180   // Map GAI errors to appropriate generic error codes
HITCBC 181   26 switch (gai_err) 181   26 switch (gai_err)
182   { 182   {
HITCBC 183   1 case EAI_AGAIN: 183   1 case EAI_AGAIN:
184   // Temporary failure - try again later 184   // Temporary failure - try again later
HITCBC 185   1 return std::error_code( 185   1 return std::error_code(
186   static_cast<int>(std::errc::resource_unavailable_try_again), 186   static_cast<int>(std::errc::resource_unavailable_try_again),
HITCBC 187   1 std::generic_category()); 187   1 std::generic_category());
188   188  
HITCBC 189   1 case EAI_BADFLAGS: 189   1 case EAI_BADFLAGS:
190   // Invalid flags 190   // Invalid flags
HITCBC 191   1 return std::error_code( 191   1 return std::error_code(
192   static_cast<int>(std::errc::invalid_argument), 192   static_cast<int>(std::errc::invalid_argument),
HITCBC 193   1 std::generic_category()); 193   1 std::generic_category());
194   194  
HITCBC 195   11 case EAI_FAIL: 195   11 case EAI_FAIL:
196   // Non-recoverable failure 196   // Non-recoverable failure
HITCBC 197   11 return std::error_code( 197   11 return std::error_code(
HITCBC 198   11 static_cast<int>(std::errc::io_error), std::generic_category()); 198   11 static_cast<int>(std::errc::io_error), std::generic_category());
199   199  
HITCBC 200   1 case EAI_FAMILY: 200   1 case EAI_FAMILY:
201   // Address family not supported 201   // Address family not supported
HITCBC 202   1 return std::error_code( 202   1 return std::error_code(
203   static_cast<int>(std::errc::address_family_not_supported), 203   static_cast<int>(std::errc::address_family_not_supported),
HITCBC 204   1 std::generic_category()); 204   1 std::generic_category());
205   205  
HITCBC 206   1 case EAI_MEMORY: 206   1 case EAI_MEMORY:
207   // Memory allocation failure 207   // Memory allocation failure
HITCBC 208   1 return std::error_code( 208   1 return std::error_code(
209   static_cast<int>(std::errc::not_enough_memory), 209   static_cast<int>(std::errc::not_enough_memory),
HITCBC 210   1 std::generic_category()); 210   1 std::generic_category());
211   211  
HITCBC 212   7 case EAI_NONAME: 212   7 case EAI_NONAME:
213   // Host or service not found 213   // Host or service not found
HITCBC 214   7 return std::error_code( 214   7 return std::error_code(
215   static_cast<int>(std::errc::no_such_device_or_address), 215   static_cast<int>(std::errc::no_such_device_or_address),
HITCBC 216   7 std::generic_category()); 216   7 std::generic_category());
217   217  
HITCBC 218   1 case EAI_SERVICE: 218   1 case EAI_SERVICE:
219   // Service not supported for socket type 219   // Service not supported for socket type
HITCBC 220   1 return std::error_code( 220   1 return std::error_code(
221   static_cast<int>(std::errc::invalid_argument), 221   static_cast<int>(std::errc::invalid_argument),
HITCBC 222   1 std::generic_category()); 222   1 std::generic_category());
223   223  
HITCBC 224   1 case EAI_SOCKTYPE: 224   1 case EAI_SOCKTYPE:
225   // Socket type not supported 225   // Socket type not supported
HITCBC 226   1 return std::error_code( 226   1 return std::error_code(
227   static_cast<int>(std::errc::not_supported), 227   static_cast<int>(std::errc::not_supported),
HITCBC 228   1 std::generic_category()); 228   1 std::generic_category());
229   229  
HITCBC 230   1 case EAI_SYSTEM: 230   1 case EAI_SYSTEM:
231   // System error - use errno 231   // System error - use errno
HITCBC 232   1 return std::error_code(errno, std::generic_category()); 232   1 return std::error_code(errno, std::generic_category());
233   233  
HITCBC 234   1 default: 234   1 default:
235   // Unknown error 235   // Unknown error
HITCBC 236   1 return std::error_code( 236   1 return std::error_code(
HITCBC 237   1 static_cast<int>(std::errc::io_error), std::generic_category()); 237   1 static_cast<int>(std::errc::io_error), std::generic_category());
238   } 238   }
239   } 239   }
240   240  
241   // posix_resolver 241   // posix_resolver
242   242  
HITCBC 243   78 inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept 243   78 inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
HITCBC 244   78 : svc_(svc) 244   78 : svc_(svc)
245   { 245   {
HITCBC 246   78 } 246   78 }
247   247  
248   // posix_resolver::resolve_op implementation 248   // posix_resolver::resolve_op implementation
249   249  
250   inline void 250   inline void
HITCBC 251   42 posix_resolver::resolve_op::reset() noexcept 251   42 posix_resolver::resolve_op::reset() noexcept
252   { 252   {
HITCBC 253   42 host.clear(); 253   42 host.clear();
HITCBC 254   42 service.clear(); 254   42 service.clear();
HITCBC 255   42 flags = resolve_flags::none; 255   42 flags = resolve_flags::none;
HITCBC 256   42 stored_results = std::vector<endpoint>{}; 256   42 stored_results = std::vector<endpoint>{};
HITCBC 257   42 gai_error = 0; 257   42 gai_error = 0;
HITCBC 258   42 cancelled.store(false, std::memory_order_relaxed); 258   42 cancelled.store(false, std::memory_order_relaxed);
HITCBC 259   42 stop_cb.reset(); 259   42 stop_cb.reset();
HITCBC 260   42 ec_out = nullptr; 260   42 ec_out = nullptr;
HITCBC 261   42 out = nullptr; 261   42 out = nullptr;
HITCBC 262   42 } 262   42 }
263   263  
264   inline void 264   inline void
HITCBC 265   38 posix_resolver::resolve_op::operator()() 265   38 posix_resolver::resolve_op::operator()()
266   { 266   {
HITCBC 267   38 stop_cb.reset(); // Disconnect stop callback 267   38 stop_cb.reset(); // Disconnect stop callback
268   268  
HITCBC 269   38 bool const was_cancelled = cancelled.load(std::memory_order_acquire); 269   38 bool const was_cancelled = cancelled.load(std::memory_order_acquire);
270   270  
HITCBC 271   38 if (ec_out) 271   38 if (ec_out)
272   { 272   {
HITCBC 273   38 if (was_cancelled) 273   38 if (was_cancelled)
HITCBC 274   2 *ec_out = capy::error::canceled; 274   2 *ec_out = capy::error::canceled;
HITCBC 275   36 else if (gai_error != 0) 275   36 else if (gai_error != 0)
HITCBC 276   11 *ec_out = posix_resolver_detail::make_gai_error(gai_error); 276   11 *ec_out = posix_resolver_detail::make_gai_error(gai_error);
277   else 277   else
HITCBC 278   25 *ec_out = {}; // Clear on success 278   25 *ec_out = {}; // Clear on success
279   } 279   }
280   280  
HITCBC 281   38 if (out && !was_cancelled && gai_error == 0) 281   38 if (out && !was_cancelled && gai_error == 0)
HITCBC 282   25 *out = std::move(stored_results); 282   25 *out = std::move(stored_results);
283   283  
284   // Hold the keepalive across the dispatch: it may be the last 284   // Hold the keepalive across the dispatch: it may be the last
285   // reference to the implementation this op is embedded in. 285   // reference to the implementation this op is embedded in.
HITCBC 286   38 auto prevent_destroy = std::move(object_ref_); 286   38 auto prevent_destroy = std::move(object_ref_);
HITCBC 287   38 svc->work_finished(); 287   38 svc->work_finished();
HITCBC 288   38 dispatch_coro(ex, *cont).resume(); 288   38 dispatch_coro(ex, *cont).resume();
HITCBC 289   38 } 289   38 }
290   290  
291   inline void 291   inline void
HITCBC 292   3 posix_resolver::resolve_op::destroy() 292   3 posix_resolver::resolve_op::destroy()
293   { 293   {
294   // Copied first: the abandon may unwind the frame at once, destroying 294   // Copied first: the abandon may unwind the frame at once, destroying
295   // the resolver, and with it this op. 295   // the resolver, and with it this op.
HITCBC 296   3 auto* s = svc; 296   3 auto* s = svc;
HITCBC 297   3 destroy_awaiting(); 297   3 destroy_awaiting();
HITCBC 298   3 s->work_finished(); 298   3 s->work_finished();
HITCBC 299   3 } 299   3 }
300   300  
301   // posix_resolver::reverse_resolve_op implementation 301   // posix_resolver::reverse_resolve_op implementation
302   302  
303   inline void 303   inline void
HITCBC 304   20 posix_resolver::reverse_resolve_op::reset() noexcept 304   20 posix_resolver::reverse_resolve_op::reset() noexcept
305   { 305   {
HITCBC 306   20 ep = endpoint{}; 306   20 ep = endpoint{};
HITCBC 307   20 flags = reverse_flags::none; 307   20 flags = reverse_flags::none;
HITCBC 308   20 stored_host.clear(); 308   20 stored_host.clear();
HITCBC 309   20 stored_service.clear(); 309   20 stored_service.clear();
HITCBC 310   20 gai_error = 0; 310   20 gai_error = 0;
HITCBC 311   20 cancelled.store(false, std::memory_order_relaxed); 311   20 cancelled.store(false, std::memory_order_relaxed);
HITCBC 312   20 stop_cb.reset(); 312   20 stop_cb.reset();
HITCBC 313   20 ec_out = nullptr; 313   20 ec_out = nullptr;
HITCBC 314   20 result_out = nullptr; 314   20 result_out = nullptr;
HITCBC 315   20 } 315   20 }
316   316  
317   inline void 317   inline void
HITCBC 318   16 posix_resolver::reverse_resolve_op::operator()() 318   16 posix_resolver::reverse_resolve_op::operator()()
319   { 319   {
HITCBC 320   16 stop_cb.reset(); // Disconnect stop callback 320   16 stop_cb.reset(); // Disconnect stop callback
321   321  
HITCBC 322   16 bool const was_cancelled = cancelled.load(std::memory_order_acquire); 322   16 bool const was_cancelled = cancelled.load(std::memory_order_acquire);
323   323  
HITCBC 324   16 if (ec_out) 324   16 if (ec_out)
325   { 325   {
HITCBC 326   16 if (was_cancelled) 326   16 if (was_cancelled)
MISUBC 327   ✗ *ec_out = capy::error::canceled; 327   ✗ *ec_out = capy::error::canceled;
HITCBC 328   16 else if (gai_error != 0) 328   16 else if (gai_error != 0)
HITCBC 329   6 *ec_out = posix_resolver_detail::make_gai_error(gai_error); 329   6 *ec_out = posix_resolver_detail::make_gai_error(gai_error);
330   else 330   else
HITCBC 331   10 *ec_out = {}; // Clear on success 331   10 *ec_out = {}; // Clear on success
332   } 332   }
333   333  
HITCBC 334   16 if (result_out && !was_cancelled && gai_error == 0) 334   16 if (result_out && !was_cancelled && gai_error == 0)
335   { 335   {
HITCBC 336   10 *result_out = 336   10 *result_out =
HITCBC 337   10 endpoint_name{std::move(stored_host), std::move(stored_service)}; 337   10 endpoint_name{std::move(stored_host), std::move(stored_service)};
338   } 338   }
339   339  
340   // Hold the keepalive across the dispatch: it may be the last 340   // Hold the keepalive across the dispatch: it may be the last
341   // reference to the implementation this op is embedded in. 341   // reference to the implementation this op is embedded in.
HITCBC 342   16 auto prevent_destroy = std::move(object_ref_); 342   16 auto prevent_destroy = std::move(object_ref_);
HITCBC 343   16 svc->work_finished(); 343   16 svc->work_finished();
HITCBC 344   16 dispatch_coro(ex, *cont).resume(); 344   16 dispatch_coro(ex, *cont).resume();
HITCBC 345   16 } 345   16 }
346   346  
347   inline void 347   inline void
HITCBC 348   3 posix_resolver::reverse_resolve_op::destroy() 348   3 posix_resolver::reverse_resolve_op::destroy()
349   { 349   {
350   // Copied first: the abandon may unwind the frame at once, destroying 350   // Copied first: the abandon may unwind the frame at once, destroying
351   // the resolver, and with it this op. 351   // the resolver, and with it this op.
HITCBC 352   3 auto* s = svc; 352   3 auto* s = svc;
HITCBC 353   3 destroy_awaiting(); 353   3 destroy_awaiting();
HITCBC 354   3 s->work_finished(); 354   3 s->work_finished();
HITCBC 355   3 } 355   3 }
356   356  
357   // posix_resolver implementation 357   // posix_resolver implementation
358   358  
359   inline std::coroutine_handle<> 359   inline std::coroutine_handle<>
HITCBC 360   43 posix_resolver::resolve( 360   43 posix_resolver::resolve(
361   capy::continuation& cont, 361   capy::continuation& cont,
362   capy::executor_ref ex, 362   capy::executor_ref ex,
363   std::string_view host, 363   std::string_view host,
364   std::string_view service, 364   std::string_view service,
365   resolve_flags flags, 365   resolve_flags flags,
366   std::stop_token token, 366   std::stop_token token,
367   std::error_code* ec, 367   std::error_code* ec,
368   std::vector<endpoint>* out) 368   std::vector<endpoint>* out)
369   { 369   {
HITCBC 370   43 if (svc_.resolver_unavailable()) 370   43 if (svc_.resolver_unavailable())
371   { 371   {
HITCBC 372   1 *ec = std::make_error_code(std::errc::operation_not_supported); 372   1 *ec = std::make_error_code(std::errc::operation_not_supported);
HITCBC 373   1 return dispatch_coro(ex, cont); 373   1 return dispatch_coro(ex, cont);
374   } 374   }
375   375  
HITCBC 376   42 auto& op = op_; 376   42 auto& op = op_;
HITCBC 377   42 op.reset(); 377   42 op.reset();
HITCBC 378   42 op.cont = &cont; 378   42 op.cont = &cont;
HITCBC 379   42 op.ex = ex; 379   42 op.ex = ex;
HITCBC 380   42 op.ec_out = ec; 380   42 op.ec_out = ec;
HITCBC 381   42 op.out = out; 381   42 op.out = out;
HITCBC 382   42 op.host = host; 382   42 op.host = host;
HITCBC 383   42 op.service = service; 383   42 op.service = service;
HITCBC 384   42 op.flags = flags; 384   42 op.flags = flags;
HITCBC 385   42 op.start(token); 385   42 op.start(token);
386   386  
387   // Keep this context alive while resolution is pending 387   // Keep this context alive while resolution is pending
HITCBC 388   42 op.svc = &svc_; 388   42 op.svc = &svc_;
HITCBC 389   42 svc_.work_started(); 389   42 svc_.work_started();
390   390  
391   // Prevent impl destruction while work is in flight 391   // Prevent impl destruction while work is in flight
HITCBC 392   42 resolve_pool_op_.resolver_ = this; 392   42 resolve_pool_op_.resolver_ = this;
HITCBC 393   42 resolve_pool_op_.ref_ = detail::object_ref(this); 393   42 resolve_pool_op_.ref_ = detail::object_ref(this);
HITCBC 394   42 resolve_pool_op_.func_ = &posix_resolver::do_resolve_work; 394   42 resolve_pool_op_.func_ = &posix_resolver::do_resolve_work;
HITCBC 395   42 resolve_pool_op_.destroy_ = &posix_resolver::destroy_resolve_work; 395   42 resolve_pool_op_.destroy_ = &posix_resolver::destroy_resolve_work;
HITCBC 396   42 if (auto pec = svc_.pool().post(&resolve_pool_op_)) 396   42 if (auto pec = svc_.pool().post(&resolve_pool_op_))
397   { 397   {
398   // The pool is shutting down, or the system refused it a thread. 398   // The pool is shutting down, or the system refused it a thread.
399   // Nothing of this resolve went cross-thread, so it answers here 399   // Nothing of this resolve went cross-thread, so it answers here
400   // like the no-resolver exit above rather than through a 400   // like the no-resolver exit above rather than through a
401   // completion the scheduler has to carry back. 401   // completion the scheduler has to carry back.
HITCBC 402   1 resolve_pool_op_.ref_.reset(); 402   1 resolve_pool_op_.ref_.reset();
HITCBC 403   1 op.stop_cb.reset(); 403   1 op.stop_cb.reset();
HITCBC 404   1 svc_.work_finished(); 404   1 svc_.work_finished();
HITCBC 405   1 *ec = pec; 405   1 *ec = pec;
HITCBC 406   1 return dispatch_coro(ex, cont); 406   1 return dispatch_coro(ex, cont);
407   } 407   }
HITCBC 408   41 return std::noop_coroutine(); 408   41 return std::noop_coroutine();
409   } 409   }
410   410  
411   inline std::coroutine_handle<> 411   inline std::coroutine_handle<>
HITCBC 412   21 posix_resolver::reverse_resolve( 412   21 posix_resolver::reverse_resolve(
413   capy::continuation& cont, 413   capy::continuation& cont,
414   capy::executor_ref ex, 414   capy::executor_ref ex,
415   endpoint const& ep, 415   endpoint const& ep,
416   reverse_flags flags, 416   reverse_flags flags,
417   std::stop_token token, 417   std::stop_token token,
418   std::error_code* ec, 418   std::error_code* ec,
419   endpoint_name* result_out) 419   endpoint_name* result_out)
420   { 420   {
HITCBC 421   21 if (svc_.resolver_unavailable()) 421   21 if (svc_.resolver_unavailable())
422   { 422   {
HITCBC 423   1 *ec = std::make_error_code(std::errc::operation_not_supported); 423   1 *ec = std::make_error_code(std::errc::operation_not_supported);
HITCBC 424   1 return dispatch_coro(ex, cont); 424   1 return dispatch_coro(ex, cont);
425   } 425   }
426   426  
HITCBC 427   20 auto& op = reverse_op_; 427   20 auto& op = reverse_op_;
HITCBC 428   20 op.reset(); 428   20 op.reset();
HITCBC 429   20 op.cont = &cont; 429   20 op.cont = &cont;
HITCBC 430   20 op.ex = ex; 430   20 op.ex = ex;
HITCBC 431   20 op.ec_out = ec; 431   20 op.ec_out = ec;
HITCBC 432   20 op.result_out = result_out; 432   20 op.result_out = result_out;
HITCBC 433   20 op.ep = ep; 433   20 op.ep = ep;
HITCBC 434   20 op.flags = flags; 434   20 op.flags = flags;
HITCBC 435   20 op.start(token); 435   20 op.start(token);
436   436  
437   // Keep this context alive while resolution is pending 437   // Keep this context alive while resolution is pending
HITCBC 438   20 op.svc = &svc_; 438   20 op.svc = &svc_;
HITCBC 439   20 svc_.work_started(); 439   20 svc_.work_started();
440   440  
441   // Prevent impl destruction while work is in flight 441   // Prevent impl destruction while work is in flight
HITCBC 442   20 reverse_pool_op_.resolver_ = this; 442   20 reverse_pool_op_.resolver_ = this;
HITCBC 443   20 reverse_pool_op_.ref_ = detail::object_ref(this); 443   20 reverse_pool_op_.ref_ = detail::object_ref(this);
HITCBC 444   20 reverse_pool_op_.func_ = &posix_resolver::do_reverse_resolve_work; 444   20 reverse_pool_op_.func_ = &posix_resolver::do_reverse_resolve_work;
HITCBC 445   20 reverse_pool_op_.destroy_ = &posix_resolver::destroy_reverse_resolve_work; 445   20 reverse_pool_op_.destroy_ = &posix_resolver::destroy_reverse_resolve_work;
HITCBC 446   20 if (auto pec = svc_.pool().post(&reverse_pool_op_)) 446   20 if (auto pec = svc_.pool().post(&reverse_pool_op_))
447   { 447   {
448   // The pool is shutting down, or the system refused it a thread. 448   // The pool is shutting down, or the system refused it a thread.
449   // Nothing of this resolve went cross-thread, so it answers here 449   // Nothing of this resolve went cross-thread, so it answers here
450   // like the no-resolver exit above rather than through a 450   // like the no-resolver exit above rather than through a
451   // completion the scheduler has to carry back. 451   // completion the scheduler has to carry back.
HITCBC 452   1 reverse_pool_op_.ref_.reset(); 452   1 reverse_pool_op_.ref_.reset();
HITCBC 453   1 op.stop_cb.reset(); 453   1 op.stop_cb.reset();
HITCBC 454   1 svc_.work_finished(); 454   1 svc_.work_finished();
HITCBC 455   1 *ec = pec; 455   1 *ec = pec;
HITCBC 456   1 return dispatch_coro(ex, cont); 456   1 return dispatch_coro(ex, cont);
457   } 457   }
HITCBC 458   19 return std::noop_coroutine(); 458   19 return std::noop_coroutine();
459   } 459   }
460   460  
461   inline void 461   inline void
HITCBC 462   152 posix_resolver::cancel() noexcept 462   152 posix_resolver::cancel() noexcept
463   { 463   {
HITCBC 464   152 op_.request_cancel(); 464   152 op_.request_cancel();
HITCBC 465   152 reverse_op_.request_cancel(); 465   152 reverse_op_.request_cancel();
HITCBC 466   152 } 466   152 }
467   467  
468   inline void 468   inline void
HITCBC 469   38 posix_resolver::do_resolve_work(pool_work_item* w) noexcept 469   38 posix_resolver::do_resolve_work(pool_work_item* w) noexcept
470   { 470   {
HITCBC 471   38 auto* pw = static_cast<pool_op*>(w); 471   38 auto* pw = static_cast<pool_op*>(w);
HITCBC 472   38 auto* self = pw->resolver_; 472   38 auto* self = pw->resolver_;
473   473  
HITCBC 474   38 struct addrinfo hints{}; 474   38 struct addrinfo hints{};
HITCBC 475   38 hints.ai_family = AF_UNSPEC; 475   38 hints.ai_family = AF_UNSPEC;
HITCBC 476   38 hints.ai_socktype = SOCK_STREAM; 476   38 hints.ai_socktype = SOCK_STREAM;
HITCBC 477   38 hints.ai_flags = posix_resolver_detail::flags_to_hints(self->op_.flags); 477   38 hints.ai_flags = posix_resolver_detail::flags_to_hints(self->op_.flags);
478   478  
HITCBC 479   38 struct addrinfo* ai = nullptr; 479   38 struct addrinfo* ai = nullptr;
HITCBC 480   114 int result = ::getaddrinfo( 480   114 int result = ::getaddrinfo(
HITCBC 481   76 self->op_.host.empty() ? nullptr : self->op_.host.c_str(), 481   76 self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
HITCBC 482   71 self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints, 482   71 self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
483   &ai); 483   &ai);
484   484  
HITCBC 485   38 if (!self->op_.cancelled.load(std::memory_order_acquire)) 485   38 if (!self->op_.cancelled.load(std::memory_order_acquire))
486   { 486   {
HITCBC 487   37 if (result == 0 && ai) 487   36 if (result == 0 && ai)
488   { 488   {
489   self->op_.stored_results = 489   self->op_.stored_results =
HITCBC 490   26 posix_resolver_detail::convert_results(ai); 490   25 posix_resolver_detail::convert_results(ai);
HITCBC 491   26 self->op_.gai_error = 0; 491   25 self->op_.gai_error = 0;
492   } 492   }
493   else 493   else
494   { 494   {
HITCBC 495   11 self->op_.gai_error = result; 495   11 self->op_.gai_error = result;
496   } 496   }
497   } 497   }
498   498  
HITCBC 499   38 if (ai) 499   38 if (ai)
HITCBC 500   27 ::freeaddrinfo(ai); 500   27 ::freeaddrinfo(ai);
501   501  
502   // Hand the keepalive to the op: the completion waits in the 502   // Hand the keepalive to the op: the completion waits in the
503   // scheduler's queue, and the implementation embedding it must 503   // scheduler's queue, and the implementation embedding it must
504   // outlive that wait. Nothing may touch *self after the post. 504   // outlive that wait. Nothing may touch *self after the post.
HITCBC 505   38 self->op_.object_ref_ = std::move(pw->ref_); 505   38 self->op_.object_ref_ = std::move(pw->ref_);
HITCBC 506   38 self->svc_.post(&self->op_); 506   38 self->svc_.post(&self->op_);
HITCBC 507   38 } 507   38 }
508   508  
509   inline void 509   inline void
HITCBC 510   16 posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept 510   16 posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
511   { 511   {
HITCBC 512   16 auto* pw = static_cast<pool_op*>(w); 512   16 auto* pw = static_cast<pool_op*>(w);
HITCBC 513   16 auto* self = pw->resolver_; 513   16 auto* self = pw->resolver_;
514   514  
HITCBC 515   16 sockaddr_storage ss{}; 515   16 sockaddr_storage ss{};
516   socklen_t ss_len; 516   socklen_t ss_len;
517   517  
HITCBC 518   16 if (self->reverse_op_.ep.is_v4()) 518   16 if (self->reverse_op_.ep.is_v4())
519   { 519   {
HITCBC 520   14 auto sa = to_sockaddr_in(self->reverse_op_.ep); 520   14 auto sa = to_sockaddr_in(self->reverse_op_.ep);
HITCBC 521   14 std::memcpy(&ss, &sa, sizeof(sa)); 521   14 std::memcpy(&ss, &sa, sizeof(sa));
HITCBC 522   14 ss_len = sizeof(sockaddr_in); 522   14 ss_len = sizeof(sockaddr_in);
523   } 523   }
524   else 524   else
525   { 525   {
HITCBC 526   2 auto sa = to_sockaddr_in6(self->reverse_op_.ep); 526   2 auto sa = to_sockaddr_in6(self->reverse_op_.ep);
HITCBC 527   2 std::memcpy(&ss, &sa, sizeof(sa)); 527   2 std::memcpy(&ss, &sa, sizeof(sa));
HITCBC 528   2 ss_len = sizeof(sockaddr_in6); 528   2 ss_len = sizeof(sockaddr_in6);
529   } 529   }
530   530  
531   char host[NI_MAXHOST]; 531   char host[NI_MAXHOST];
532   char service[NI_MAXSERV]; 532   char service[NI_MAXSERV];
533   533  
HITCBC 534   16 int result = ::getnameinfo( 534   16 int result = ::getnameinfo(
535   reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service, 535   reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
536   sizeof(service), 536   sizeof(service),
537   posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags)); 537   posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
538   538  
HITCBC 539   16 if (!self->reverse_op_.cancelled.load(std::memory_order_acquire)) 539   16 if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
540   { 540   {
HITCBC 541   16 if (result == 0) 541   16 if (result == 0)
542   { 542   {
HITCBC 543   10 self->reverse_op_.stored_host = host; 543   10 self->reverse_op_.stored_host = host;
HITCBC 544   10 self->reverse_op_.stored_service = service; 544   10 self->reverse_op_.stored_service = service;
HITCBC 545   10 self->reverse_op_.gai_error = 0; 545   10 self->reverse_op_.gai_error = 0;
546   } 546   }
547   else 547   else
548   { 548   {
HITCBC 549   6 self->reverse_op_.gai_error = result; 549   6 self->reverse_op_.gai_error = result;
550   } 550   }
551   } 551   }
552   552  
553   // Hand the keepalive to the op: the completion waits in the 553   // Hand the keepalive to the op: the completion waits in the
554   // scheduler's queue, and the implementation embedding it must 554   // scheduler's queue, and the implementation embedding it must
555   // outlive that wait. Nothing may touch *self after the post. 555   // outlive that wait. Nothing may touch *self after the post.
HITCBC 556   16 self->reverse_op_.object_ref_ = std::move(pw->ref_); 556   16 self->reverse_op_.object_ref_ = std::move(pw->ref_);
HITCBC 557   16 self->svc_.post(&self->reverse_op_); 557   16 self->svc_.post(&self->reverse_op_);
HITCBC 558   16 } 558   16 }
559   559  
560   inline void 560   inline void
HITCBC 561   3 posix_resolver::destroy_resolve_work(pool_work_item* w) noexcept 561   3 posix_resolver::destroy_resolve_work(pool_work_item* w) noexcept
562   { 562   {
HITCBC 563   3 auto* pw = static_cast<pool_op*>(w); 563   3 auto* pw = static_cast<pool_op*>(w);
HITCBC 564   3 auto* self = pw->resolver_; 564   3 auto* self = pw->resolver_;
HITCBC 565   3 self->op_.object_ref_ = std::move(pw->ref_); 565   3 self->op_.object_ref_ = std::move(pw->ref_);
HITCBC 566   3 self->op_.destroy(); 566   3 self->op_.destroy();
HITCBC 567   3 } 567   3 }
568   568  
569   inline void 569   inline void
HITCBC 570   3 posix_resolver::destroy_reverse_resolve_work(pool_work_item* w) noexcept 570   3 posix_resolver::destroy_reverse_resolve_work(pool_work_item* w) noexcept
571   { 571   {
HITCBC 572   3 auto* pw = static_cast<pool_op*>(w); 572   3 auto* pw = static_cast<pool_op*>(w);
HITCBC 573   3 auto* self = pw->resolver_; 573   3 auto* self = pw->resolver_;
HITCBC 574   3 self->reverse_op_.object_ref_ = std::move(pw->ref_); 574   3 self->reverse_op_.object_ref_ = std::move(pw->ref_);
HITCBC 575   3 self->reverse_op_.destroy(); 575   3 self->reverse_op_.destroy();
HITCBC 576   3 } 576   3 }
577   577  
578   // posix_resolver_service implementation 578   // posix_resolver_service implementation
579   579  
580   inline void 580   inline void
HITCBC 581   77 posix_resolver_service::shutdown() 581   77 posix_resolver_service::shutdown()
582   { 582   {
583   // See uring_socket_service_base::shutdown(): snapshot under an 583   // See uring_socket_service_base::shutdown(): snapshot under an
584   // acquired reference, cancel without the pool lock held; shutdown() 584   // acquired reference, cancel without the pool lock held; shutdown()
585   // sets shutting-down and takes the snapshot in one critical 585   // sets shutting-down and takes the snapshot in one critical
586   // section, so a cancel that drops the last ref deletes rather than 586   // section, so a cancel that drops the last ref deletes rather than
587   // recycles. The thread pool service shuts down separately via 587   // recycles. The thread pool service shuts down separately via
588   // execution_context ordering. 588   // execution_context ordering.
HITCBC 589   77 std::vector<posix_resolver*> live; 589   77 std::vector<posix_resolver*> live;
HITCBC 590   77 object_pool_.shutdown( 590   77 object_pool_.shutdown(
HITCBC 591   77 [&](posix_resolver* r) 591   77 [&](posix_resolver* r)
592   { 592   {
HITCBC 593   3 acquire(r); 593   3 acquire(r);
HITCBC 594   3 live.push_back(r); 594   3 live.push_back(r);
HITCBC 595   3 }); 595   3 });
HITCBC 596   80 for (auto* r : live) 596   80 for (auto* r : live)
597   { 597   {
HITCBC 598   3 r->cancel(); 598   3 r->cancel();
HITCBC 599   3 release(r); 599   3 release(r);
600   } 600   }
HITCBC 601   77 } 601   77 }
602   602  
603   inline io_object::implementation* 603   inline io_object::implementation*
HITCBC 604   142 posix_resolver_service::construct() 604   142 posix_resolver_service::construct()
605   { 605   {
HITCBC 606   142 return object_pool_.acquire(*this); 606   142 return object_pool_.acquire(*this);
607   } 607   }
608   608  
609   inline void 609   inline void
HITCBC 610   54 posix_resolver_service::post(scheduler_op* op) 610   54 posix_resolver_service::post(scheduler_op* op)
611   { 611   {
HITCBC 612   54 sched_->post(op); 612   54 sched_->post(op);
HITCBC 613   54 } 613   54 }
614   614  
615   inline void 615   inline void
HITCBC 616   142 posix_resolver::retire() noexcept 616   142 posix_resolver::retire() noexcept
617   { 617   {
HITCBC 618   142 svc_.object_pool_.recycle(this); 618   142 svc_.object_pool_.recycle(this);
HITCBC 619   142 } 619   142 }
620   620  
621   } // namespace boost::corosio::detail 621   } // namespace boost::corosio::detail
622   622  
623   #endif // BOOST_COROSIO_POSIX 623   #endif // BOOST_COROSIO_POSIX
624   624  
625   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP 625   #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP