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