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   2106 posix_resolver_service(capy::execution_context& ctx, scheduler& sched) 38   2106 posix_resolver_service(capy::execution_context& ctx, scheduler& sched)
HITCBC 39   4212 : sched_(&sched) 39   4212 : sched_(&sched)
HITCBC 40   2106 , pool_(ctx) 40   2106 , pool_(ctx)
41   { 41   {
HITCBC 42   2106 } 42   2106 }
43   43  
HITCBC 44   4212 ~posix_resolver_service() override = default; 44   4212 ~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   59 void destroy(io_object::implementation* p) override 51   59 void destroy(io_object::implementation* p) override
52   { 52   {
HITCBC 53   59 auto& impl = static_cast<posix_resolver&>(*p); 53   59 auto& impl = static_cast<posix_resolver&>(*p);
HITCBC 54   59 impl.cancel(); 54   59 impl.cancel();
HITCBC 55   59 destroy_impl(impl); 55   59 destroy_impl(impl);
HITCBC 56   59 } 56   59 }
57   57  
58   void shutdown() override; 58   void shutdown() override;
59   void destroy_impl(posix_resolver& impl); 59   void destroy_impl(posix_resolver& impl);
60   60  
61   void post(scheduler_op* op); 61   void post(scheduler_op* op);
62   62  
63   /** Return the resolver thread pool. 63   /** Return the resolver thread pool.
64   64  
65   The pool's service is created on first use, so this can fail 65   The pool's service is created on first use, so this can fail
66   where a plain accessor could not. Its workers start later, on 66   where a plain accessor could not. Its workers start later, on
67   the first post, and a thread the system refuses there is 67   the first post, and a thread the system refuses there is
68   reported by that post rather than thrown here. 68   reported by that post rather than thrown here.
69   69  
70   @throws std::bad_alloc If the service cannot be allocated. 70   @throws std::bad_alloc If the service cannot be allocated.
71   71  
72   @return The context's shared blocking-I/O pool. 72   @return The context's shared blocking-I/O pool.
73   73  
74   @see thread_pool_ref::get 74   @see thread_pool_ref::get
75   */ 75   */
HITCBC 76   52 thread_pool& pool() 76   52 thread_pool& pool()
77   { 77   {
HITCBC 78   52 return pool_.get(); 78   52 return pool_.get();
79   } 79   }
80   80  
81   /// True when the resolver thread pool is unavailable: the `unsafe` tier, 81   /// True when the resolver thread pool is unavailable: the `unsafe` tier,
82   /// whose lockless scheduler cannot accept the pool's cross-thread 82   /// whose lockless scheduler cannot accept the pool's cross-thread
83   /// completions. 83   /// completions.
HITCBC 84   54 bool resolver_unavailable() const noexcept 84   54 bool resolver_unavailable() const noexcept
85   { 85   {
HITCBC 86   54 return sched_->scheduler_locking_disabled(); 86   54 return sched_->scheduler_locking_disabled();
87   } 87   }
88   88  
89   private: 89   private:
90   scheduler* sched_; 90   scheduler* sched_;
91   thread_pool_ref pool_; 91   thread_pool_ref pool_;
92   std::mutex mutex_; 92   std::mutex mutex_;
93   intrusive_list<posix_resolver> resolver_list_; 93   intrusive_list<posix_resolver> resolver_list_;
94   std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>> 94   std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
95   resolver_ptrs_; 95   resolver_ptrs_;
96   }; 96   };
97   97  
98   /** Get or create the resolver service for the given context. 98   /** Get or create the resolver service for the given context.
99   99  
100   This function is called by the concrete scheduler during initialization 100   This function is called by the concrete scheduler during initialization
101   to create the resolver service with a reference to itself. 101   to create the resolver service with a reference to itself.
102   102  
103   @param ctx Reference to the owning execution_context. 103   @param ctx Reference to the owning execution_context.
104   @param sched Reference to the scheduler for posting completions. 104   @param sched Reference to the scheduler for posting completions.
105   @return Reference to the resolver service. 105   @return Reference to the resolver service.
106   */ 106   */
107   posix_resolver_service& 107   posix_resolver_service&
108   get_resolver_service(capy::execution_context& ctx, scheduler& sched); 108   get_resolver_service(capy::execution_context& ctx, scheduler& sched);
109   109  
110   // --------------------------------------------------------------------------- 110   // ---------------------------------------------------------------------------
111   // Inline implementation 111   // Inline implementation
112   // --------------------------------------------------------------------------- 112   // ---------------------------------------------------------------------------
113   113  
114   // posix_resolver_detail helpers 114   // posix_resolver_detail helpers
115   115  
116   inline int 116   inline int
HITCBC 117   32 posix_resolver_detail::flags_to_hints(resolve_flags flags) 117   32 posix_resolver_detail::flags_to_hints(resolve_flags flags)
118   { 118   {
HITCBC 119   32 int hints = 0; 119   32 int hints = 0;
120   120  
HITCBC 121   32 if ((flags & resolve_flags::passive) != resolve_flags::none) 121   32 if ((flags & resolve_flags::passive) != resolve_flags::none)
HITCBC 122   1 hints |= AI_PASSIVE; 122   1 hints |= AI_PASSIVE;
HITCBC 123   32 if ((flags & resolve_flags::numeric_host) != resolve_flags::none) 123   32 if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
HITCBC 124   15 hints |= AI_NUMERICHOST; 124   15 hints |= AI_NUMERICHOST;
HITCBC 125   32 if ((flags & resolve_flags::numeric_service) != resolve_flags::none) 125   32 if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
HITCBC 126   12 hints |= AI_NUMERICSERV; 126   12 hints |= AI_NUMERICSERV;
HITCBC 127   32 if ((flags & resolve_flags::address_configured) != resolve_flags::none) 127   32 if ((flags & resolve_flags::address_configured) != resolve_flags::none)
HITCBC 128   1 hints |= AI_ADDRCONFIG; 128   1 hints |= AI_ADDRCONFIG;
HITCBC 129   32 if ((flags & resolve_flags::v4_mapped) != resolve_flags::none) 129   32 if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
HITCBC 130   1 hints |= AI_V4MAPPED; 130   1 hints |= AI_V4MAPPED;
HITCBC 131   32 if ((flags & resolve_flags::all_matching) != resolve_flags::none) 131   32 if ((flags & resolve_flags::all_matching) != resolve_flags::none)
HITCBC 132   1 hints |= AI_ALL; 132   1 hints |= AI_ALL;
133   133  
HITCBC 134   32 return hints; 134   32 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   60 inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept 246   60 inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
HITCBC 247   60 : svc_(svc) 247   60 : svc_(svc)
248   { 248   {
HITCBC 249   60 } 249   60 }
250   250  
251   // posix_resolver::resolve_op implementation 251   // posix_resolver::resolve_op implementation
252   252  
253   inline void 253   inline void
HITCBC 254   33 posix_resolver::resolve_op::reset() noexcept 254   33 posix_resolver::resolve_op::reset() noexcept
255   { 255   {
HITCBC 256   33 host.clear(); 256   33 host.clear();
HITCBC 257   33 service.clear(); 257   33 service.clear();
HITCBC 258   33 flags = resolve_flags::none; 258   33 flags = resolve_flags::none;
HITCBC 259   33 stored_results = resolver_results{}; 259   33 stored_results = resolver_results{};
HITCBC 260   33 gai_error = 0; 260   33 gai_error = 0;
HITCBC 261   33 cancelled.store(false, std::memory_order_relaxed); 261   33 cancelled.store(false, std::memory_order_relaxed);
HITCBC 262   33 stop_cb.reset(); 262   33 stop_cb.reset();
HITCBC 263   33 ec_out = nullptr; 263   33 ec_out = nullptr;
HITCBC 264   33 out = nullptr; 264   33 out = nullptr;
HITCBC 265   33 } 265   33 }
266   266  
267   inline void 267   inline void
HITCBC 268   31 posix_resolver::resolve_op::operator()() 268   31 posix_resolver::resolve_op::operator()()
269   { 269   {
HITCBC 270   31 stop_cb.reset(); // Disconnect stop callback 270   31 stop_cb.reset(); // Disconnect stop callback
271   271  
HITCBC 272   31 bool const was_cancelled = cancelled.load(std::memory_order_acquire); 272   31 bool const was_cancelled = cancelled.load(std::memory_order_acquire);
273   273  
HITCBC 274   31 if (ec_out) 274   31 if (ec_out)
275   { 275   {
HITCBC 276   31 if (was_cancelled) 276   31 if (was_cancelled)
HITCBC 277   3 *ec_out = capy::error::canceled; 277   3 *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   31 if (out && !was_cancelled && gai_error == 0) 284   31 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   31 auto prevent_destroy = std::move(impl_ptr); 289   31 auto prevent_destroy = std::move(impl_ptr);
HITCBC 290   31 ex.on_work_finished(); 290   31 ex.on_work_finished();
HITCBC 291   31 cont.h = h; 291   31 cont.h = h;
HITCBC 292   31 dispatch_coro(ex, cont).resume(); 292   31 dispatch_coro(ex, cont).resume();
HITCBC 293   31 } 293   31 }
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   19 posix_resolver::reverse_resolve_op::reset() noexcept 308   19 posix_resolver::reverse_resolve_op::reset() noexcept
309   { 309   {
HITCBC 310   19 ep = endpoint{}; 310   19 ep = endpoint{};
HITCBC 311   19 flags = reverse_flags::none; 311   19 flags = reverse_flags::none;
HITCBC 312   19 stored_host.clear(); 312   19 stored_host.clear();
HITCBC 313   19 stored_service.clear(); 313   19 stored_service.clear();
HITCBC 314   19 gai_error = 0; 314   19 gai_error = 0;
HITCBC 315   19 cancelled.store(false, std::memory_order_relaxed); 315   19 cancelled.store(false, std::memory_order_relaxed);
HITCBC 316   19 stop_cb.reset(); 316   19 stop_cb.reset();
HITCBC 317   19 ec_out = nullptr; 317   19 ec_out = nullptr;
HITCBC 318   19 result_out = nullptr; 318   19 result_out = nullptr;
HITCBC 319   19 } 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   34 posix_resolver::resolve( 365   34 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   34 if (svc_.resolver_unavailable()) 375   34 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   33 auto& op = op_; 382   33 auto& op = op_;
HITCBC 383   33 op.reset(); 383   33 op.reset();
HITCBC 384   33 op.h = h; 384   33 op.h = h;
HITCBC 385   33 op.ex = ex; 385   33 op.ex = ex;
HITCBC 386   33 op.ec_out = ec; 386   33 op.ec_out = ec;
HITCBC 387   33 op.out = out; 387   33 op.out = out;
HITCBC 388   33 op.host = host; 388   33 op.host = host;
HITCBC 389   33 op.service = service; 389   33 op.service = service;
HITCBC 390   33 op.flags = flags; 390   33 op.flags = flags;
HITCBC 391   33 op.start(token); 391   33 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   33 op.ex.on_work_started(); 394   33 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   33 resolve_pool_op_.resolver_ = this; 397   33 resolve_pool_op_.resolver_ = this;
HITCBC 398   33 resolve_pool_op_.ref_ = this->shared_from_this(); 398   33 resolve_pool_op_.ref_ = this->shared_from_this();
HITCBC 399   33 resolve_pool_op_.func_ = &posix_resolver::do_resolve_work; 399   33 resolve_pool_op_.func_ = &posix_resolver::do_resolve_work;
HITCBC 400   33 if (auto pec = svc_.pool().post(&resolve_pool_op_)) 400   33 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.
HITCBC 406   1 resolve_pool_op_.ref_.reset(); 406   1 resolve_pool_op_.ref_.reset();
HITCBC 407   1 op.stop_cb.reset(); 407   1 op.stop_cb.reset();
HITCBC 408   1 op.ex.on_work_finished(); 408   1 op.ex.on_work_finished();
HITCBC 409   1 *ec = pec; 409   1 *ec = pec;
HITCBC 410   1 op.cont.h = h; 410   1 op.cont.h = h;
HITCBC 411   1 return dispatch_coro(ex, op.cont); 411   1 return dispatch_coro(ex, op.cont);
412   } 412   }
HITCBC 413   32 return std::noop_coroutine(); 413   32 return std::noop_coroutine();
414   } 414   }
415   415  
416   inline std::coroutine_handle<> 416   inline std::coroutine_handle<>
HITCBC 417   20 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   20 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   19 auto& op = reverse_op_; 433   19 auto& op = reverse_op_;
HITCBC 434   19 op.reset(); 434   19 op.reset();
HITCBC 435   19 op.h = h; 435   19 op.h = h;
HITCBC 436   19 op.ex = ex; 436   19 op.ex = ex;
HITCBC 437   19 op.ec_out = ec; 437   19 op.ec_out = ec;
HITCBC 438   19 op.result_out = result_out; 438   19 op.result_out = result_out;
HITCBC 439   19 op.ep = ep; 439   19 op.ep = ep;
HITCBC 440   19 op.flags = flags; 440   19 op.flags = flags;
HITCBC 441   19 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   19 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   19 reverse_pool_op_.resolver_ = this; 447   19 reverse_pool_op_.resolver_ = this;
HITCBC 448   19 reverse_pool_op_.ref_ = this->shared_from_this(); 448   19 reverse_pool_op_.ref_ = this->shared_from_this();
HITCBC 449   19 reverse_pool_op_.func_ = &posix_resolver::do_reverse_resolve_work; 449   19 reverse_pool_op_.func_ = &posix_resolver::do_reverse_resolve_work;
HITCBC 450   19 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.
HITCBC 456   1 reverse_pool_op_.ref_.reset(); 456   1 reverse_pool_op_.ref_.reset();
HITCBC 457   1 op.stop_cb.reset(); 457   1 op.stop_cb.reset();
HITCBC 458   1 op.ex.on_work_finished(); 458   1 op.ex.on_work_finished();
HITCBC 459   1 *ec = pec; 459   1 *ec = pec;
HITCBC 460   1 op.cont.h = h; 460   1 op.cont.h = h;
HITCBC 461   1 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   67 posix_resolver::cancel() noexcept 467   67 posix_resolver::cancel() noexcept
468   { 468   {
HITCBC 469   67 op_.request_cancel(); 469   67 op_.request_cancel();
HITCBC 470   67 reverse_op_.request_cancel(); 470   67 reverse_op_.request_cancel();
HITCBC 471   67 } 471   67 }
472   472  
473   inline void 473   inline void
HITCBC 474   32 posix_resolver::do_resolve_work(pool_work_item* w) noexcept 474   32 posix_resolver::do_resolve_work(pool_work_item* w) noexcept
475   { 475   {
HITCBC 476   32 auto* pw = static_cast<pool_op*>(w); 476   32 auto* pw = static_cast<pool_op*>(w);
HITCBC 477   32 auto* self = pw->resolver_; 477   32 auto* self = pw->resolver_;
478   478  
HITCBC 479   32 struct addrinfo hints{}; 479   32 struct addrinfo hints{};
HITCBC 480   32 hints.ai_family = AF_UNSPEC; 480   32 hints.ai_family = AF_UNSPEC;
HITCBC 481   32 hints.ai_socktype = SOCK_STREAM; 481   32 hints.ai_socktype = SOCK_STREAM;
HITCBC 482   32 hints.ai_flags = posix_resolver_detail::flags_to_hints(self->op_.flags); 482   32 hints.ai_flags = posix_resolver_detail::flags_to_hints(self->op_.flags);
483   483  
HITCBC 484   32 struct addrinfo* ai = nullptr; 484   32 struct addrinfo* ai = nullptr;
HITCBC 485   96 int result = ::getaddrinfo( 485   96 int result = ::getaddrinfo(
HITCBC 486   64 self->op_.host.empty() ? nullptr : self->op_.host.c_str(), 486   64 self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
HITCBC 487   64 self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints, 487   64 self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
488   &ai); 488   &ai);
489   489  
HITCBC 490   32 if (!self->op_.cancelled.load(std::memory_order_acquire)) 490   32 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   32 if (ai) 504   32 if (ai)
HITCBC 505   23 ::freeaddrinfo(ai); 505   23 ::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   32 self->op_.impl_ptr = std::move(pw->ref_); 510   32 self->op_.impl_ptr = std::move(pw->ref_);
HITCBC 511   32 self->svc_.post(&self->op_); 511   32 self->svc_.post(&self->op_);
HITCBC 512   32 } 512   32 }
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   2106 posix_resolver_service::shutdown() 568   2106 posix_resolver_service::shutdown()
569   { 569   {
HITCBC 570   2106 std::lock_guard<std::mutex> lock(mutex_); 570   2106 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   2107 for (auto* impl = resolver_list_.pop_front(); impl != nullptr; 573   2107 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   2106 resolver_ptrs_.clear(); 582   2106 resolver_ptrs_.clear();
HITCBC 583   2106 } 583   2106 }
584   584  
585   inline io_object::implementation* 585   inline io_object::implementation*
HITCBC 586   60 posix_resolver_service::construct() 586   60 posix_resolver_service::construct()
587   { 587   {
HITCBC 588   60 auto ptr = std::make_shared<posix_resolver>(*this); 588   60 auto ptr = std::make_shared<posix_resolver>(*this);
HITCBC 589   60 auto* impl = ptr.get(); 589   60 auto* impl = ptr.get();
590   590  
591   { 591   {
HITCBC 592   60 std::lock_guard<std::mutex> lock(mutex_); 592   60 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 593   60 resolver_list_.push_back(impl); 593   60 resolver_list_.push_back(impl);
HITCBC 594   60 resolver_ptrs_[impl] = std::move(ptr); 594   60 resolver_ptrs_[impl] = std::move(ptr);
HITCBC 595   60 } 595   60 }
596   596  
HITCBC 597   60 return impl; 597   60 return impl;
HITCBC 598   60 } 598   60 }
599   599  
600   inline void 600   inline void
HITCBC 601   59 posix_resolver_service::destroy_impl(posix_resolver& impl) 601   59 posix_resolver_service::destroy_impl(posix_resolver& impl)
602   { 602   {
HITCBC 603   59 std::lock_guard<std::mutex> lock(mutex_); 603   59 std::lock_guard<std::mutex> lock(mutex_);
HITCBC 604   59 resolver_list_.remove(&impl); 604   59 resolver_list_.remove(&impl);
HITCBC 605   59 resolver_ptrs_.erase(&impl); 605   59 resolver_ptrs_.erase(&impl);
HITCBC 606   59 } 606   59 }
607   607  
608   inline void 608   inline void
HITCBC 609   50 posix_resolver_service::post(scheduler_op* op) 609   50 posix_resolver_service::post(scheduler_op* op)
610   { 610   {
HITCBC 611   50 sched_->post(op); 611   50 sched_->post(op);
HITCBC 612   50 } 612   50 }
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   2106 get_resolver_service(capy::execution_context& ctx, scheduler& sched) 617   2106 get_resolver_service(capy::execution_context& ctx, scheduler& sched)
618   { 618   {
HITCBC 619   2106 return ctx.make_service<posix_resolver_service>(sched); 619   2106 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