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