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