95.19% Lines (277/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   1018 posix_resolver_service(capy::execution_context& ctx, scheduler& sched) 37   1018 posix_resolver_service(capy::execution_context& ctx, scheduler& sched)
HITCBC 38   2036 : sched_(&sched) 38   2036 : sched_(&sched)
HITCBC 39   1018 , pool_(ctx.use_service<thread_pool>()) 39   1018 , pool_(ctx.use_service<thread_pool>())
40   { 40   {
HITCBC 41   1018 } 41   1018 }
42   42  
HITCBC 43   2036 ~posix_resolver_service() override = default; 43   2036 ~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 static_cast<reactor_scheduler const*>(sched_) 73   35 return static_cast<reactor_scheduler const*>(sched_)
HITCBC 74   35 ->is_single_threaded(); 74   35 ->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   21 posix_resolver_detail::flags_to_hints(resolve_flags flags) 105   21 posix_resolver_detail::flags_to_hints(resolve_flags flags)
106   { 106   {
HITCBC 107   21 int hints = 0; 107   21 int hints = 0;
108   108  
HITCBC 109   21 if ((flags & resolve_flags::passive) != resolve_flags::none) 109   21 if ((flags & resolve_flags::passive) != resolve_flags::none)
HITCBC 110   1 hints |= AI_PASSIVE; 110   1 hints |= AI_PASSIVE;
HITCBC 111   21 if ((flags & resolve_flags::numeric_host) != resolve_flags::none) 111   21 if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
HITCBC 112   12 hints |= AI_NUMERICHOST; 112   12 hints |= AI_NUMERICHOST;
HITCBC 113   21 if ((flags & resolve_flags::numeric_service) != resolve_flags::none) 113   21 if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
HITCBC 114   9 hints |= AI_NUMERICSERV; 114   9 hints |= AI_NUMERICSERV;
HITCBC 115   21 if ((flags & resolve_flags::address_configured) != resolve_flags::none) 115   21 if ((flags & resolve_flags::address_configured) != resolve_flags::none)
HITCBC 116   1 hints |= AI_ADDRCONFIG; 116   1 hints |= AI_ADDRCONFIG;
HITCBC 117   21 if ((flags & resolve_flags::v4_mapped) != resolve_flags::none) 117   21 if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
HITCBC 118   1 hints |= AI_V4MAPPED; 118   1 hints |= AI_V4MAPPED;
HITCBC 119   21 if ((flags & resolve_flags::all_matching) != resolve_flags::none) 119   21 if ((flags & resolve_flags::all_matching) != resolve_flags::none)
HITCBC 120   1 hints |= AI_ALL; 120   1 hints |= AI_ALL;
121   121  
HITCBC 122   21 return hints; 122   21 return hints;
123   } 123   }
124   124  
125   inline int 125   inline int
HITCBC 126   12 posix_resolver_detail::flags_to_ni_flags(reverse_flags flags) 126   12 posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
127   { 127   {
HITCBC 128   12 int ni_flags = 0; 128   12 int ni_flags = 0;
129   129  
HITCBC 130   12 if ((flags & reverse_flags::numeric_host) != reverse_flags::none) 130   12 if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
HITCBC 131   6 ni_flags |= NI_NUMERICHOST; 131   6 ni_flags |= NI_NUMERICHOST;
HITCBC 132   12 if ((flags & reverse_flags::numeric_service) != reverse_flags::none) 132   12 if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
HITCBC 133   6 ni_flags |= NI_NUMERICSERV; 133   6 ni_flags |= NI_NUMERICSERV;
HITCBC 134   12 if ((flags & reverse_flags::name_required) != reverse_flags::none) 134   12 if ((flags & reverse_flags::name_required) != reverse_flags::none)
HITCBC 135   1 ni_flags |= NI_NAMEREQD; 135   1 ni_flags |= NI_NAMEREQD;
HITCBC 136   12 if ((flags & reverse_flags::datagram_service) != reverse_flags::none) 136   12 if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
HITCBC 137   1 ni_flags |= NI_DGRAM; 137   1 ni_flags |= NI_DGRAM;
138   138  
HITCBC 139   12 return ni_flags; 139   12 return ni_flags;
140   } 140   }
141   141  
142   inline resolver_results 142   inline resolver_results
HITCBC 143   16 posix_resolver_detail::convert_results( 143   16 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   16 std::vector<resolver_entry> entries; 146   16 std::vector<resolver_entry> entries;
HITCBC 147   16 entries.reserve(4); // Most lookups return 1-4 addresses 147   16 entries.reserve(4); // Most lookups return 1-4 addresses
148   148  
HITCBC 149   32 for (auto* p = ai; p != nullptr; p = p->ai_next) 149   32 for (auto* p = ai; p != nullptr; p = p->ai_next)
150   { 150   {
HITCBC 151   16 if (p->ai_family == AF_INET) 151   16 if (p->ai_family == AF_INET)
152   { 152   {
HITCBC 153   14 auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr); 153   14 auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
HITCBC 154   14 auto ep = from_sockaddr_in(*addr); 154   14 auto ep = from_sockaddr_in(*addr);
HITCBC 155   14 entries.emplace_back(ep, host, service); 155   14 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   32 return resolver_results(std::move(entries)); 165   32 return resolver_results(std::move(entries));
HITCBC 166   16 } 166   16 }
167   167  
168   inline std::error_code 168   inline std::error_code
HITCBC 169   14 posix_resolver_detail::make_gai_error(int gai_err) 169   14 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   14 switch (gai_err) 172   14 switch (gai_err)
173   { 173   {
HITCBC 174   1 case EAI_AGAIN: 174   1 case EAI_AGAIN:
175   // Temporary failure - try again later 175   // Temporary failure - try again later
HITCBC 176   1 return std::error_code( 176   1 return std::error_code(
177   static_cast<int>(std::errc::resource_unavailable_try_again), 177   static_cast<int>(std::errc::resource_unavailable_try_again),
HITCBC 178   1 std::generic_category()); 178   1 std::generic_category());
179   179  
HITCBC 180   1 case EAI_BADFLAGS: 180   1 case EAI_BADFLAGS:
181   // Invalid flags 181   // Invalid flags
HITCBC 182   1 return std::error_code( 182   1 return std::error_code(
183   static_cast<int>(std::errc::invalid_argument), 183   static_cast<int>(std::errc::invalid_argument),
HITCBC 184   1 std::generic_category()); 184   1 std::generic_category());
185   185  
HITCBC 186   1 case EAI_FAIL: 186   1 case EAI_FAIL:
187   // Non-recoverable failure 187   // Non-recoverable failure
HITCBC 188   1 return std::error_code( 188   1 return std::error_code(
HITCBC 189   1 static_cast<int>(std::errc::io_error), std::generic_category()); 189   1 static_cast<int>(std::errc::io_error), std::generic_category());
190   190  
HITCBC 191   1 case EAI_FAMILY: 191   1 case EAI_FAMILY:
192   // Address family not supported 192   // Address family not supported
HITCBC 193   1 return std::error_code( 193   1 return std::error_code(
194   static_cast<int>(std::errc::address_family_not_supported), 194   static_cast<int>(std::errc::address_family_not_supported),
HITCBC 195   1 std::generic_category()); 195   1 std::generic_category());
196   196  
HITCBC 197   1 case EAI_MEMORY: 197   1 case EAI_MEMORY:
198   // Memory allocation failure 198   // Memory allocation failure
HITCBC 199   1 return std::error_code( 199   1 return std::error_code(
200   static_cast<int>(std::errc::not_enough_memory), 200   static_cast<int>(std::errc::not_enough_memory),
HITCBC 201   1 std::generic_category()); 201   1 std::generic_category());
202   202  
HITCBC 203   5 case EAI_NONAME: 203   5 case EAI_NONAME:
204   // Host or service not found 204   // Host or service not found
HITCBC 205   5 return std::error_code( 205   5 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   5 std::generic_category()); 207   5 std::generic_category());
208   208  
HITCBC 209   1 case EAI_SERVICE: 209   1 case EAI_SERVICE:
210   // Service not supported for socket type 210   // Service not supported for socket type
HITCBC 211   1 return std::error_code( 211   1 return std::error_code(
212   static_cast<int>(std::errc::invalid_argument), 212   static_cast<int>(std::errc::invalid_argument),
HITCBC 213   1 std::generic_category()); 213   1 std::generic_category());
214   214  
HITCBC 215   1 case EAI_SOCKTYPE: 215   1 case EAI_SOCKTYPE:
216   // Socket type not supported 216   // Socket type not supported
HITCBC 217   1 return std::error_code( 217   1 return std::error_code(
218   static_cast<int>(std::errc::not_supported), 218   static_cast<int>(std::errc::not_supported),
HITCBC 219   1 std::generic_category()); 219   1 std::generic_category());
220   220  
HITCBC 221   1 case EAI_SYSTEM: 221   1 case EAI_SYSTEM:
222   // System error - use errno 222   // System error - use errno
HITCBC 223   1 return std::error_code(errno, std::generic_category()); 223   1 return std::error_code(errno, std::generic_category());
224   224  
HITCBC 225   1 default: 225   1 default:
226   // Unknown error 226   // Unknown error
HITCBC 227   1 return std::error_code( 227   1 return std::error_code(
HITCBC 228   1 static_cast<int>(std::errc::io_error), std::generic_category()); 228   1 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   42 inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept 234   42 inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
HITCBC 235   42 : svc_(svc) 235   42 : svc_(svc)
236   { 236   {
HITCBC 237   42 } 237   42 }
238   238  
239   // posix_resolver::resolve_op implementation 239   // posix_resolver::resolve_op implementation
240   240  
241   inline void 241   inline void
HITCBC 242   21 posix_resolver::resolve_op::reset() noexcept 242   21 posix_resolver::resolve_op::reset() noexcept
243   { 243   {
HITCBC 244   21 host.clear(); 244   21 host.clear();
HITCBC 245   21 service.clear(); 245   21 service.clear();
HITCBC 246   21 flags = resolve_flags::none; 246   21 flags = resolve_flags::none;
HITCBC 247   21 stored_results = resolver_results{}; 247   21 stored_results = resolver_results{};
HITCBC 248   21 gai_error = 0; 248   21 gai_error = 0;
HITCBC 249   21 cancelled.store(false, std::memory_order_relaxed); 249   21 cancelled.store(false, std::memory_order_relaxed);
HITCBC 250   21 stop_cb.reset(); 250   21 stop_cb.reset();
HITCBC 251   21 ec_out = nullptr; 251   21 ec_out = nullptr;
HITCBC 252   21 out = nullptr; 252   21 out = nullptr;
HITCBC 253   21 } 253   21 }
254   254  
255   inline void 255   inline void
HITCBC 256   21 posix_resolver::resolve_op::operator()() 256   21 posix_resolver::resolve_op::operator()()
257   { 257   {
HITCBC 258   21 stop_cb.reset(); // Disconnect stop callback 258   21 stop_cb.reset(); // Disconnect stop callback
259   259  
HITCBC 260   21 bool const was_cancelled = cancelled.load(std::memory_order_acquire); 260   21 bool const was_cancelled = cancelled.load(std::memory_order_acquire);
261   261  
HITCBC 262   21 if (ec_out) 262   21 if (ec_out)
263   { 263   {
HITCBC 264   21 if (was_cancelled) 264   21 if (was_cancelled)
HITCBC 265   1 *ec_out = capy::error::canceled; 265   1 *ec_out = capy::error::canceled;
HITCBC 266   20 else if (gai_error != 0) 266   20 else if (gai_error != 0)
HITCBC 267   4 *ec_out = posix_resolver_detail::make_gai_error(gai_error); 267   4 *ec_out = posix_resolver_detail::make_gai_error(gai_error);
268   else 268   else
HITCBC 269   16 *ec_out = {}; // Clear on success 269   16 *ec_out = {}; // Clear on success
270   } 270   }
271   271  
HITCBC 272   21 if (out && !was_cancelled && gai_error == 0) 272   21 if (out && !was_cancelled && gai_error == 0)
HITCBC 273   16 *out = std::move(stored_results); 273   16 *out = std::move(stored_results);
274   274  
HITCBC 275   21 impl->svc_.work_finished(); 275   21 impl->svc_.work_finished();
HITCBC 276   21 cont_op.cont.h = h; 276   21 cont_op.cont.h = h;
HITCBC 277   21 dispatch_coro(ex, cont_op.cont).resume(); 277   21 dispatch_coro(ex, cont_op.cont).resume();
HITCBC 278   21 } 278   21 }
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   47 posix_resolver::resolve_op::request_cancel() noexcept 287   47 posix_resolver::resolve_op::request_cancel() noexcept
288   { 288   {
HITCBC 289   47 cancelled.store(true, std::memory_order_release); 289   47 cancelled.store(true, std::memory_order_release);
HITCBC 290   47 } 290   47 }
291   291  
292   inline void 292   inline void
HITCBC 293   21 posix_resolver::resolve_op::start(std::stop_token const& token) 293   21 posix_resolver::resolve_op::start(std::stop_token const& token)
294   { 294   {
HITCBC 295   21 cancelled.store(false, std::memory_order_release); 295   21 cancelled.store(false, std::memory_order_release);
HITCBC 296   21 stop_cb.reset(); 296   21 stop_cb.reset();
297   297  
HITCBC 298   21 if (token.stop_possible()) 298   21 if (token.stop_possible())
HITCBC 299   1 stop_cb.emplace(token, canceller{this}); 299   1 stop_cb.emplace(token, canceller{this});
HITCBC 300   21 } 300   21 }
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   12 posix_resolver::reverse_resolve_op::reset() noexcept 305   12 posix_resolver::reverse_resolve_op::reset() noexcept
306   { 306   {
HITCBC 307   12 ep = endpoint{}; 307   12 ep = endpoint{};
HITCBC 308   12 flags = reverse_flags::none; 308   12 flags = reverse_flags::none;
HITCBC 309   12 stored_host.clear(); 309   12 stored_host.clear();
HITCBC 310   12 stored_service.clear(); 310   12 stored_service.clear();
HITCBC 311   12 gai_error = 0; 311   12 gai_error = 0;
HITCBC 312   12 cancelled.store(false, std::memory_order_relaxed); 312   12 cancelled.store(false, std::memory_order_relaxed);
HITCBC 313   12 stop_cb.reset(); 313   12 stop_cb.reset();
HITCBC 314   12 ec_out = nullptr; 314   12 ec_out = nullptr;
HITCBC 315   12 result_out = nullptr; 315   12 result_out = nullptr;
HITCBC 316   12 } 316   12 }
317   317  
318   inline void 318   inline void
HITCBC 319   12 posix_resolver::reverse_resolve_op::operator()() 319   12 posix_resolver::reverse_resolve_op::operator()()
320   { 320   {
HITCBC 321   12 stop_cb.reset(); // Disconnect stop callback 321   12 stop_cb.reset(); // Disconnect stop callback
322   322  
HITCBC 323   12 bool const was_cancelled = cancelled.load(std::memory_order_acquire); 323   12 bool const was_cancelled = cancelled.load(std::memory_order_acquire);
324   324  
HITCBC 325   12 if (ec_out) 325   12 if (ec_out)
326   { 326   {
HITCBC 327   12 if (was_cancelled) 327   12 if (was_cancelled)
HITCBC 328   1 *ec_out = capy::error::canceled; 328   1 *ec_out = capy::error::canceled;
HITCBC 329   11 else if (gai_error != 0) 329   11 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   10 *ec_out = {}; // Clear on success 332   10 *ec_out = {}; // Clear on success
333   } 333   }
334   334  
HITCBC 335   12 if (result_out && !was_cancelled && gai_error == 0) 335   12 if (result_out && !was_cancelled && gai_error == 0)
336   { 336   {
HITCBC 337   30 *result_out = reverse_resolver_result( 337   30 *result_out = reverse_resolver_result(
HITCBC 338   30 ep, std::move(stored_host), std::move(stored_service)); 338   30 ep, std::move(stored_host), std::move(stored_service));
339   } 339   }
340   340  
HITCBC 341   12 impl->svc_.work_finished(); 341   12 impl->svc_.work_finished();
HITCBC 342   12 cont_op.cont.h = h; 342   12 cont_op.cont.h = h;
HITCBC 343   12 dispatch_coro(ex, cont_op.cont).resume(); 343   12 dispatch_coro(ex, cont_op.cont).resume();
HITCBC 344   12 } 344   12 }
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   47 posix_resolver::reverse_resolve_op::request_cancel() noexcept 353   47 posix_resolver::reverse_resolve_op::request_cancel() noexcept
354   { 354   {
HITCBC 355   47 cancelled.store(true, std::memory_order_release); 355   47 cancelled.store(true, std::memory_order_release);
HITCBC 356   47 } 356   47 }
357   357  
358   inline void 358   inline void
HITCBC 359   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)
360   { 360   {
HITCBC 361   12 cancelled.store(false, std::memory_order_release); 361   12 cancelled.store(false, std::memory_order_release);
HITCBC 362   12 stop_cb.reset(); 362   12 stop_cb.reset();
363   363  
HITCBC 364   12 if (token.stop_possible()) 364   12 if (token.stop_possible())
HITCBC 365   1 stop_cb.emplace(token, canceller{this}); 365   1 stop_cb.emplace(token, canceller{this});
HITCBC 366   12 } 366   12 }
367   367  
368   // posix_resolver implementation 368   // posix_resolver implementation
369   369  
370   inline std::coroutine_handle<> 370   inline std::coroutine_handle<>
HITCBC 371   22 posix_resolver::resolve( 371   22 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   22 if (svc_.single_threaded()) 381   22 if (svc_.single_threaded())
382   { 382   {
HITCBC 383   1 *ec = std::make_error_code(std::errc::operation_not_supported); 383   1 *ec = std::make_error_code(std::errc::operation_not_supported);
HITCBC 384   1 op_.cont_op.cont.h = h; 384   1 op_.cont_op.cont.h = h;
HITCBC 385   1 return dispatch_coro(ex, op_.cont_op.cont); 385   1 return dispatch_coro(ex, op_.cont_op.cont);
386   } 386   }
387   387  
HITCBC 388   21 auto& op = op_; 388   21 auto& op = op_;
HITCBC 389   21 op.reset(); 389   21 op.reset();
HITCBC 390   21 op.h = h; 390   21 op.h = h;
HITCBC 391   21 op.ex = ex; 391   21 op.ex = ex;
HITCBC 392   21 op.impl = this; 392   21 op.impl = this;
HITCBC 393   21 op.ec_out = ec; 393   21 op.ec_out = ec;
HITCBC 394   21 op.out = out; 394   21 op.out = out;
HITCBC 395   21 op.host = host; 395   21 op.host = host;
HITCBC 396   21 op.service = service; 396   21 op.service = service;
HITCBC 397   21 op.flags = flags; 397   21 op.flags = flags;
HITCBC 398   21 op.start(token); 398   21 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   21 op.ex.on_work_started(); 401   21 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   21 resolve_pool_op_.resolver_ = this; 404   21 resolve_pool_op_.resolver_ = this;
HITCBC 405   21 resolve_pool_op_.ref_ = this->shared_from_this(); 405   21 resolve_pool_op_.ref_ = this->shared_from_this();
HITCBC 406   21 resolve_pool_op_.func_ = &posix_resolver::do_resolve_work; 406   21 resolve_pool_op_.func_ = &posix_resolver::do_resolve_work;
HITCBC 407   21 if (!svc_.pool().post(&resolve_pool_op_)) 407   21 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   21 return std::noop_coroutine(); 414   21 return std::noop_coroutine();
415   } 415   }
416   416  
417   inline std::coroutine_handle<> 417   inline std::coroutine_handle<>
HITCBC 418   13 posix_resolver::reverse_resolve( 418   13 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   13 if (svc_.single_threaded()) 427   13 if (svc_.single_threaded())
428   { 428   {
HITCBC 429   1 *ec = std::make_error_code(std::errc::operation_not_supported); 429   1 *ec = std::make_error_code(std::errc::operation_not_supported);
HITCBC 430   1 reverse_op_.cont_op.cont.h = h; 430   1 reverse_op_.cont_op.cont.h = h;
HITCBC 431   1 return dispatch_coro(ex, reverse_op_.cont_op.cont); 431   1 return dispatch_coro(ex, reverse_op_.cont_op.cont);
432   } 432   }
433   433  
HITCBC 434   12 auto& op = reverse_op_; 434   12 auto& op = reverse_op_;
HITCBC 435   12 op.reset(); 435   12 op.reset();
HITCBC 436   12 op.h = h; 436   12 op.h = h;
HITCBC 437   12 op.ex = ex; 437   12 op.ex = ex;
HITCBC 438   12 op.impl = this; 438   12 op.impl = this;
HITCBC 439   12 op.ec_out = ec; 439   12 op.ec_out = ec;
HITCBC 440   12 op.result_out = result_out; 440   12 op.result_out = result_out;
HITCBC 441   12 op.ep = ep; 441   12 op.ep = ep;
HITCBC 442   12 op.flags = flags; 442   12 op.flags = flags;
HITCBC 443   12 op.start(token); 443   12 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   12 op.ex.on_work_started(); 446   12 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   12 reverse_pool_op_.resolver_ = this; 449   12 reverse_pool_op_.resolver_ = this;
HITCBC 450   12 reverse_pool_op_.ref_ = this->shared_from_this(); 450   12 reverse_pool_op_.ref_ = this->shared_from_this();
HITCBC 451   12 reverse_pool_op_.func_ = &posix_resolver::do_reverse_resolve_work; 451   12 reverse_pool_op_.func_ = &posix_resolver::do_reverse_resolve_work;
HITCBC 452   12 if (!svc_.pool().post(&reverse_pool_op_)) 452   12 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   12 return std::noop_coroutine(); 459   12 return std::noop_coroutine();
460   } 460   }
461   461  
462   inline void 462   inline void
HITCBC 463   46 posix_resolver::cancel() noexcept 463   46 posix_resolver::cancel() noexcept
464   { 464   {
HITCBC 465   46 op_.request_cancel(); 465   46 op_.request_cancel();
HITCBC 466   46 reverse_op_.request_cancel(); 466   46 reverse_op_.request_cancel();
HITCBC 467   46 } 467   46 }
468   468  
469   inline void 469   inline void
HITCBC 470   21 posix_resolver::do_resolve_work(pool_work_item* w) noexcept 470   21 posix_resolver::do_resolve_work(pool_work_item* w) noexcept
471   { 471   {
HITCBC 472   21 auto* pw = static_cast<pool_op*>(w); 472   21 auto* pw = static_cast<pool_op*>(w);
HITCBC 473   21 auto* self = pw->resolver_; 473   21 auto* self = pw->resolver_;
474   474  
HITCBC 475   21 struct addrinfo hints{}; 475   21 struct addrinfo hints{};
HITCBC 476   21 hints.ai_family = AF_UNSPEC; 476   21 hints.ai_family = AF_UNSPEC;
HITCBC 477   21 hints.ai_socktype = SOCK_STREAM; 477   21 hints.ai_socktype = SOCK_STREAM;
HITCBC 478   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);
479   479  
HITCBC 480   21 struct addrinfo* ai = nullptr; 480   21 struct addrinfo* ai = nullptr;
HITCBC 481   63 int result = ::getaddrinfo( 481   63 int result = ::getaddrinfo(
HITCBC 482   42 self->op_.host.empty() ? nullptr : self->op_.host.c_str(), 482   42 self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
HITCBC 483   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,
484   &ai); 484   &ai);
485   485  
HITCBC 486   21 if (!self->op_.cancelled.load(std::memory_order_acquire)) 486   21 if (!self->op_.cancelled.load(std::memory_order_acquire))
487   { 487   {
HITCBC 488   20 if (result == 0 && ai) 488   20 if (result == 0 && ai)
489   { 489   {
HITCBC 490   32 self->op_.stored_results = posix_resolver_detail::convert_results( 490   32 self->op_.stored_results = posix_resolver_detail::convert_results(
HITCBC 491   16 ai, self->op_.host, self->op_.service); 491   16 ai, self->op_.host, self->op_.service);
HITCBC 492   16 self->op_.gai_error = 0; 492   16 self->op_.gai_error = 0;
493   } 493   }
494   else 494   else
495   { 495   {
HITCBC 496   4 self->op_.gai_error = result; 496   4 self->op_.gai_error = result;
497   } 497   }
498   } 498   }
499   499  
HITCBC 500   21 if (ai) 500   21 if (ai)
HITCBC 501   17 ::freeaddrinfo(ai); 501   17 ::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   21 auto ref = std::move(pw->ref_); 505   21 auto ref = std::move(pw->ref_);
HITCBC 506   21 self->svc_.post(&self->op_); 506   21 self->svc_.post(&self->op_);
HITCBC 507   21 } 507   21 }
508   508  
509   inline void 509   inline void
HITCBC 510   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
511   { 511   {
HITCBC 512   12 auto* pw = static_cast<pool_op*>(w); 512   12 auto* pw = static_cast<pool_op*>(w);
HITCBC 513   12 auto* self = pw->resolver_; 513   12 auto* self = pw->resolver_;
514   514  
HITCBC 515   12 sockaddr_storage ss{}; 515   12 sockaddr_storage ss{};
516   socklen_t ss_len; 516   socklen_t ss_len;
517   517  
HITCBC 518   12 if (self->reverse_op_.ep.is_v4()) 518   12 if (self->reverse_op_.ep.is_v4())
519   { 519   {
HITCBC 520   10 auto sa = to_sockaddr_in(self->reverse_op_.ep); 520   10 auto sa = to_sockaddr_in(self->reverse_op_.ep);
HITCBC 521   10 std::memcpy(&ss, &sa, sizeof(sa)); 521   10 std::memcpy(&ss, &sa, sizeof(sa));
HITCBC 522   10 ss_len = sizeof(sockaddr_in); 522   10 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   12 int result = ::getnameinfo( 534   12 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   12 if (!self->reverse_op_.cancelled.load(std::memory_order_acquire)) 539   12 if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
540   { 540   {
HITCBC 541   11 if (result == 0) 541   11 if (result == 0)
542   { 542   {
HITCBC 543   10 self->reverse_op_.stored_host = host; 543   10 self->reverse_op_.stored_host = host;
HITCBC 544   10 self->reverse_op_.stored_service = service; 544   10 self->reverse_op_.stored_service = service;
HITCBC 545   10 self->reverse_op_.gai_error = 0; 545   10 self->reverse_op_.gai_error = 0;
546   } 546   }
547   else 547   else
548   { 548   {
HITCBC 549   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   12 auto ref = std::move(pw->ref_); 555   12 auto ref = std::move(pw->ref_);
HITCBC 556   12 self->svc_.post(&self->reverse_op_); 556   12 self->svc_.post(&self->reverse_op_);
HITCBC 557   12 } 557   12 }
558   558  
559   // posix_resolver_service implementation 559   // posix_resolver_service implementation
560   560  
561   inline void 561   inline void
HITCBC 562   1018 posix_resolver_service::shutdown() 562   1018 posix_resolver_service::shutdown()
563   { 563   {
HITCBC 564   1018 std::lock_guard<std::mutex> lock(mutex_); 564   1018 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   1018 for (auto* impl = resolver_list_.pop_front(); impl != nullptr; 567   1018 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   1018 resolver_ptrs_.clear(); 576   1018 resolver_ptrs_.clear();
HITCBC 577   1018 } 577   1018 }
578   578  
579   inline io_object::implementation* 579   inline io_object::implementation*
HITCBC 580   42 posix_resolver_service::construct() 580   42 posix_resolver_service::construct()
581   { 581   {
HITCBC 582   42 auto ptr = std::make_shared<posix_resolver>(*this); 582   42 auto ptr = std::make_shared<posix_resolver>(*this);
HITCBC 583   42 auto* impl = ptr.get(); 583   42 auto* impl = ptr.get();
584   584  
585   { 585   {
HITCBC 586   42 std::lock_guard<std::mutex> lock(mutex_); 586   42 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 587   42 resolver_list_.push_back(impl); 587   42 resolver_list_.push_back(impl);
HITCBC 588   42 resolver_ptrs_[impl] = std::move(ptr); 588   42 resolver_ptrs_[impl] = std::move(ptr);
HITCBC 589   42 } 589   42 }
590   590  
HITCBC 591   42 return impl; 591   42 return impl;
HITCBC 592   42 } 592   42 }
593   593  
594   inline void 594   inline void
HITCBC 595   42 posix_resolver_service::destroy_impl(posix_resolver& impl) 595   42 posix_resolver_service::destroy_impl(posix_resolver& impl)
596   { 596   {
HITCBC 597   42 std::lock_guard<std::mutex> lock(mutex_); 597   42 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 598   42 resolver_list_.remove(&impl); 598   42 resolver_list_.remove(&impl);
HITCBC 599   42 resolver_ptrs_.erase(&impl); 599   42 resolver_ptrs_.erase(&impl);
HITCBC 600   42 } 600   42 }
601   601  
602   inline void 602   inline void
HITCBC 603   33 posix_resolver_service::post(scheduler_op* op) 603   33 posix_resolver_service::post(scheduler_op* op)
604   { 604   {
HITCBC 605   33 sched_->post(op); 605   33 sched_->post(op);
HITCBC 606   33 } 606   33 }
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   33 posix_resolver_service::work_finished() noexcept 615   33 posix_resolver_service::work_finished() noexcept
616   { 616   {
HITCBC 617   33 sched_->work_finished(); 617   33 sched_->work_finished();
HITCBC 618   33 } 618   33 }
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   1018 get_resolver_service(capy::execution_context& ctx, scheduler& sched) 623   1018 get_resolver_service(capy::execution_context& ctx, scheduler& sched)
624   { 624   {
HITCBC 625   1018 return ctx.make_service<posix_resolver_service>(sched); 625   1018 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