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