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