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