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