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