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