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