1  
//
1  
//
2  
// Copyright (c) 2026 Steve Gerbino
2  
// Copyright (c) 2026 Steve Gerbino
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
12  

12  

13  
#include <boost/corosio/detail/platform.hpp>
13  
#include <boost/corosio/detail/platform.hpp>
14  

14  

15  
#if BOOST_COROSIO_POSIX
15  
#if BOOST_COROSIO_POSIX
16  

16  

17  
#include <boost/corosio/native/detail/posix/posix_resolver.hpp>
17  
#include <boost/corosio/native/detail/posix/posix_resolver.hpp>
18  
#include <boost/corosio/detail/thread_pool.hpp>
18  
#include <boost/corosio/detail/thread_pool.hpp>
19  

19  

20  
#include <unordered_map>
20  
#include <unordered_map>
21  

21  

22  
namespace boost::corosio::detail {
22  
namespace boost::corosio::detail {
23  

23  

24  
/** Resolver service for POSIX backends.
24  
/** Resolver service for POSIX backends.
25  

25  

26  
    Owns all posix_resolver instances. Thread lifecycle is managed
26  
    Owns all posix_resolver instances. Thread lifecycle is managed
27  
    by the thread_pool service.
27  
    by the thread_pool service.
28  
*/
28  
*/
29  
class BOOST_COROSIO_DECL posix_resolver_service final
29  
class BOOST_COROSIO_DECL posix_resolver_service final
30  
    : public capy::execution_context::service
30  
    : public capy::execution_context::service
31  
    , public io_object::io_service
31  
    , public io_object::io_service
32  
{
32  
{
33  
public:
33  
public:
34  
    using key_type = posix_resolver_service;
34  
    using key_type = posix_resolver_service;
35  

35  

36  
    posix_resolver_service(capy::execution_context& ctx, scheduler& sched)
36  
    posix_resolver_service(capy::execution_context& ctx, scheduler& sched)
37  
        : sched_(&sched)
37  
        : sched_(&sched)
38  
        , pool_(ctx.make_service<thread_pool>())
38  
        , pool_(ctx.make_service<thread_pool>())
39  
    {
39  
    {
40  
    }
40  
    }
41  

41  

42  
    ~posix_resolver_service() override = default;
42  
    ~posix_resolver_service() override = default;
43  

43  

44  
    posix_resolver_service(posix_resolver_service const&)            = delete;
44  
    posix_resolver_service(posix_resolver_service const&)            = delete;
45  
    posix_resolver_service& operator=(posix_resolver_service const&) = delete;
45  
    posix_resolver_service& operator=(posix_resolver_service const&) = delete;
46  

46  

47  
    io_object::implementation* construct() override;
47  
    io_object::implementation* construct() override;
48  

48  

49  
    void destroy(io_object::implementation* p) override
49  
    void destroy(io_object::implementation* p) override
50  
    {
50  
    {
51  
        auto& impl = static_cast<posix_resolver&>(*p);
51  
        auto& impl = static_cast<posix_resolver&>(*p);
52  
        impl.cancel();
52  
        impl.cancel();
53  
        destroy_impl(impl);
53  
        destroy_impl(impl);
54  
    }
54  
    }
55  

55  

56  
    void shutdown() override;
56  
    void shutdown() override;
57  
    void destroy_impl(posix_resolver& impl);
57  
    void destroy_impl(posix_resolver& impl);
58  

58  

59  
    void post(scheduler_op* op);
59  
    void post(scheduler_op* op);
60  
    void work_started() noexcept;
60  
    void work_started() noexcept;
61  
    void work_finished() noexcept;
61  
    void work_finished() noexcept;
62  

62  

63  
    /** Return the resolver thread pool. */
63  
    /** Return the resolver thread pool. */
64  
    thread_pool& pool() noexcept { return pool_; }
64  
    thread_pool& pool() noexcept { return pool_; }
65  

65  

66  
private:
66  
private:
67  
    scheduler* sched_;
67  
    scheduler* sched_;
68  
    thread_pool& pool_;
68  
    thread_pool& pool_;
69  
    std::mutex mutex_;
69  
    std::mutex mutex_;
70  
    intrusive_list<posix_resolver> resolver_list_;
70  
    intrusive_list<posix_resolver> resolver_list_;
71  
    std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
71  
    std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
72  
        resolver_ptrs_;
72  
        resolver_ptrs_;
73  
};
73  
};
74  

74  

75  
/** Get or create the resolver service for the given context.
75  
/** Get or create the resolver service for the given context.
76  

76  

77  
    This function is called by the concrete scheduler during initialization
77  
    This function is called by the concrete scheduler during initialization
78  
    to create the resolver service with a reference to itself.
78  
    to create the resolver service with a reference to itself.
79  

79  

80  
    @param ctx Reference to the owning execution_context.
80  
    @param ctx Reference to the owning execution_context.
81  
    @param sched Reference to the scheduler for posting completions.
81  
    @param sched Reference to the scheduler for posting completions.
82  
    @return Reference to the resolver service.
82  
    @return Reference to the resolver service.
83  
*/
83  
*/
84  
posix_resolver_service&
84  
posix_resolver_service&
85  
get_resolver_service(capy::execution_context& ctx, scheduler& sched);
85  
get_resolver_service(capy::execution_context& ctx, scheduler& sched);
86  

86  

87  
// ---------------------------------------------------------------------------
87  
// ---------------------------------------------------------------------------
88  
// Inline implementation
88  
// Inline implementation
89  
// ---------------------------------------------------------------------------
89  
// ---------------------------------------------------------------------------
90  

90  

91  
// posix_resolver_detail helpers
91  
// posix_resolver_detail helpers
92  

92  

93  
inline int
93  
inline int
94  
posix_resolver_detail::flags_to_hints(resolve_flags flags)
94  
posix_resolver_detail::flags_to_hints(resolve_flags flags)
95  
{
95  
{
96  
    int hints = 0;
96  
    int hints = 0;
97  

97  

98  
    if ((flags & resolve_flags::passive) != resolve_flags::none)
98  
    if ((flags & resolve_flags::passive) != resolve_flags::none)
99  
        hints |= AI_PASSIVE;
99  
        hints |= AI_PASSIVE;
100  
    if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
100  
    if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
101  
        hints |= AI_NUMERICHOST;
101  
        hints |= AI_NUMERICHOST;
102  
    if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
102  
    if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
103  
        hints |= AI_NUMERICSERV;
103  
        hints |= AI_NUMERICSERV;
104  
    if ((flags & resolve_flags::address_configured) != resolve_flags::none)
104  
    if ((flags & resolve_flags::address_configured) != resolve_flags::none)
105  
        hints |= AI_ADDRCONFIG;
105  
        hints |= AI_ADDRCONFIG;
106  
    if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
106  
    if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
107  
        hints |= AI_V4MAPPED;
107  
        hints |= AI_V4MAPPED;
108  
    if ((flags & resolve_flags::all_matching) != resolve_flags::none)
108  
    if ((flags & resolve_flags::all_matching) != resolve_flags::none)
109  
        hints |= AI_ALL;
109  
        hints |= AI_ALL;
110  

110  

111  
    return hints;
111  
    return hints;
112  
}
112  
}
113  

113  

114  
inline int
114  
inline int
115  
posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
115  
posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
116  
{
116  
{
117  
    int ni_flags = 0;
117  
    int ni_flags = 0;
118  

118  

119  
    if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
119  
    if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
120  
        ni_flags |= NI_NUMERICHOST;
120  
        ni_flags |= NI_NUMERICHOST;
121  
    if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
121  
    if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
122  
        ni_flags |= NI_NUMERICSERV;
122  
        ni_flags |= NI_NUMERICSERV;
123  
    if ((flags & reverse_flags::name_required) != reverse_flags::none)
123  
    if ((flags & reverse_flags::name_required) != reverse_flags::none)
124  
        ni_flags |= NI_NAMEREQD;
124  
        ni_flags |= NI_NAMEREQD;
125  
    if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
125  
    if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
126  
        ni_flags |= NI_DGRAM;
126  
        ni_flags |= NI_DGRAM;
127  

127  

128  
    return ni_flags;
128  
    return ni_flags;
129  
}
129  
}
130  

130  

131  
inline resolver_results
131  
inline resolver_results
132  
posix_resolver_detail::convert_results(
132  
posix_resolver_detail::convert_results(
133  
    struct addrinfo* ai, std::string_view host, std::string_view service)
133  
    struct addrinfo* ai, std::string_view host, std::string_view service)
134  
{
134  
{
135  
    std::vector<resolver_entry> entries;
135  
    std::vector<resolver_entry> entries;
136  
    entries.reserve(4); // Most lookups return 1-4 addresses
136  
    entries.reserve(4); // Most lookups return 1-4 addresses
137  

137  

138  
    for (auto* p = ai; p != nullptr; p = p->ai_next)
138  
    for (auto* p = ai; p != nullptr; p = p->ai_next)
139  
    {
139  
    {
140  
        if (p->ai_family == AF_INET)
140  
        if (p->ai_family == AF_INET)
141  
        {
141  
        {
142  
            auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
142  
            auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
143  
            auto ep    = from_sockaddr_in(*addr);
143  
            auto ep    = from_sockaddr_in(*addr);
144  
            entries.emplace_back(ep, host, service);
144  
            entries.emplace_back(ep, host, service);
145  
        }
145  
        }
146  
        else if (p->ai_family == AF_INET6)
146  
        else if (p->ai_family == AF_INET6)
147  
        {
147  
        {
148  
            auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
148  
            auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
149  
            auto ep    = from_sockaddr_in6(*addr);
149  
            auto ep    = from_sockaddr_in6(*addr);
150  
            entries.emplace_back(ep, host, service);
150  
            entries.emplace_back(ep, host, service);
151  
        }
151  
        }
152  
    }
152  
    }
153  

153  

154  
    return resolver_results(std::move(entries));
154  
    return resolver_results(std::move(entries));
155  
}
155  
}
156  

156  

157  
inline std::error_code
157  
inline std::error_code
158  
posix_resolver_detail::make_gai_error(int gai_err)
158  
posix_resolver_detail::make_gai_error(int gai_err)
159  
{
159  
{
160  
    // Map GAI errors to appropriate generic error codes
160  
    // Map GAI errors to appropriate generic error codes
161  
    switch (gai_err)
161  
    switch (gai_err)
162  
    {
162  
    {
163  
    case EAI_AGAIN:
163  
    case EAI_AGAIN:
164  
        // Temporary failure - try again later
164  
        // Temporary failure - try again later
165  
        return std::error_code(
165  
        return std::error_code(
166  
            static_cast<int>(std::errc::resource_unavailable_try_again),
166  
            static_cast<int>(std::errc::resource_unavailable_try_again),
167  
            std::generic_category());
167  
            std::generic_category());
168  

168  

169  
    case EAI_BADFLAGS:
169  
    case EAI_BADFLAGS:
170  
        // Invalid flags
170  
        // Invalid flags
171  
        return std::error_code(
171  
        return std::error_code(
172  
            static_cast<int>(std::errc::invalid_argument),
172  
            static_cast<int>(std::errc::invalid_argument),
173  
            std::generic_category());
173  
            std::generic_category());
174  

174  

175  
    case EAI_FAIL:
175  
    case EAI_FAIL:
176  
        // Non-recoverable failure
176  
        // Non-recoverable failure
177  
        return std::error_code(
177  
        return std::error_code(
178  
            static_cast<int>(std::errc::io_error), std::generic_category());
178  
            static_cast<int>(std::errc::io_error), std::generic_category());
179  

179  

180  
    case EAI_FAMILY:
180  
    case EAI_FAMILY:
181  
        // Address family not supported
181  
        // Address family not supported
182  
        return std::error_code(
182  
        return std::error_code(
183  
            static_cast<int>(std::errc::address_family_not_supported),
183  
            static_cast<int>(std::errc::address_family_not_supported),
184  
            std::generic_category());
184  
            std::generic_category());
185  

185  

186  
    case EAI_MEMORY:
186  
    case EAI_MEMORY:
187  
        // Memory allocation failure
187  
        // Memory allocation failure
188  
        return std::error_code(
188  
        return std::error_code(
189  
            static_cast<int>(std::errc::not_enough_memory),
189  
            static_cast<int>(std::errc::not_enough_memory),
190  
            std::generic_category());
190  
            std::generic_category());
191  

191  

192  
    case EAI_NONAME:
192  
    case EAI_NONAME:
193  
        // Host or service not found
193  
        // Host or service not found
194  
        return std::error_code(
194  
        return std::error_code(
195  
            static_cast<int>(std::errc::no_such_device_or_address),
195  
            static_cast<int>(std::errc::no_such_device_or_address),
196  
            std::generic_category());
196  
            std::generic_category());
197  

197  

198  
    case EAI_SERVICE:
198  
    case EAI_SERVICE:
199  
        // Service not supported for socket type
199  
        // Service not supported for socket type
200  
        return std::error_code(
200  
        return std::error_code(
201  
            static_cast<int>(std::errc::invalid_argument),
201  
            static_cast<int>(std::errc::invalid_argument),
202  
            std::generic_category());
202  
            std::generic_category());
203  

203  

204  
    case EAI_SOCKTYPE:
204  
    case EAI_SOCKTYPE:
205  
        // Socket type not supported
205  
        // Socket type not supported
206  
        return std::error_code(
206  
        return std::error_code(
207  
            static_cast<int>(std::errc::not_supported),
207  
            static_cast<int>(std::errc::not_supported),
208  
            std::generic_category());
208  
            std::generic_category());
209  

209  

210  
    case EAI_SYSTEM:
210  
    case EAI_SYSTEM:
211  
        // System error - use errno
211  
        // System error - use errno
212  
        return std::error_code(errno, std::generic_category());
212  
        return std::error_code(errno, std::generic_category());
213  

213  

214  
    default:
214  
    default:
215  
        // Unknown error
215  
        // Unknown error
216  
        return std::error_code(
216  
        return std::error_code(
217  
            static_cast<int>(std::errc::io_error), std::generic_category());
217  
            static_cast<int>(std::errc::io_error), std::generic_category());
218  
    }
218  
    }
219  
}
219  
}
220  

220  

221  
// posix_resolver
221  
// posix_resolver
222  

222  

223  
inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
223  
inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
224  
    : svc_(svc)
224  
    : svc_(svc)
225  
{
225  
{
226  
}
226  
}
227  

227  

228  
// posix_resolver::resolve_op implementation
228  
// posix_resolver::resolve_op implementation
229  

229  

230  
inline void
230  
inline void
231  
posix_resolver::resolve_op::reset() noexcept
231  
posix_resolver::resolve_op::reset() noexcept
232  
{
232  
{
233  
    host.clear();
233  
    host.clear();
234  
    service.clear();
234  
    service.clear();
235  
    flags          = resolve_flags::none;
235  
    flags          = resolve_flags::none;
236  
    stored_results = resolver_results{};
236  
    stored_results = resolver_results{};
237  
    gai_error      = 0;
237  
    gai_error      = 0;
238  
    cancelled.store(false, std::memory_order_relaxed);
238  
    cancelled.store(false, std::memory_order_relaxed);
239  
    stop_cb.reset();
239  
    stop_cb.reset();
240  
    ec_out = nullptr;
240  
    ec_out = nullptr;
241  
    out    = nullptr;
241  
    out    = nullptr;
242  
}
242  
}
243  

243  

244  
inline void
244  
inline void
245  
posix_resolver::resolve_op::operator()()
245  
posix_resolver::resolve_op::operator()()
246  
{
246  
{
247  
    stop_cb.reset(); // Disconnect stop callback
247  
    stop_cb.reset(); // Disconnect stop callback
248  

248  

249  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
249  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
250  

250  

251  
    if (ec_out)
251  
    if (ec_out)
252  
    {
252  
    {
253  
        if (was_cancelled)
253  
        if (was_cancelled)
254  
            *ec_out = capy::error::canceled;
254  
            *ec_out = capy::error::canceled;
255  
        else if (gai_error != 0)
255  
        else if (gai_error != 0)
256  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
256  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
257  
        else
257  
        else
258  
            *ec_out = {}; // Clear on success
258  
            *ec_out = {}; // Clear on success
259  
    }
259  
    }
260  

260  

261  
    if (out && !was_cancelled && gai_error == 0)
261  
    if (out && !was_cancelled && gai_error == 0)
262  
        *out = std::move(stored_results);
262  
        *out = std::move(stored_results);
263  

263  

264  
    impl->svc_.work_finished();
264  
    impl->svc_.work_finished();
265  
    dispatch_coro(ex, h).resume();
265  
    dispatch_coro(ex, h).resume();
266  
}
266  
}
267  

267  

268  
inline void
268  
inline void
269  
posix_resolver::resolve_op::destroy()
269  
posix_resolver::resolve_op::destroy()
270  
{
270  
{
271  
    stop_cb.reset();
271  
    stop_cb.reset();
272  
}
272  
}
273  

273  

274  
inline void
274  
inline void
275  
posix_resolver::resolve_op::request_cancel() noexcept
275  
posix_resolver::resolve_op::request_cancel() noexcept
276  
{
276  
{
277  
    cancelled.store(true, std::memory_order_release);
277  
    cancelled.store(true, std::memory_order_release);
278  
}
278  
}
279  

279  

280  
inline void
280  
inline void
281  
posix_resolver::resolve_op::start(std::stop_token const& token)
281  
posix_resolver::resolve_op::start(std::stop_token const& token)
282  
{
282  
{
283  
    cancelled.store(false, std::memory_order_release);
283  
    cancelled.store(false, std::memory_order_release);
284  
    stop_cb.reset();
284  
    stop_cb.reset();
285  

285  

286  
    if (token.stop_possible())
286  
    if (token.stop_possible())
287  
        stop_cb.emplace(token, canceller{this});
287  
        stop_cb.emplace(token, canceller{this});
288  
}
288  
}
289  

289  

290  
// posix_resolver::reverse_resolve_op implementation
290  
// posix_resolver::reverse_resolve_op implementation
291  

291  

292  
inline void
292  
inline void
293  
posix_resolver::reverse_resolve_op::reset() noexcept
293  
posix_resolver::reverse_resolve_op::reset() noexcept
294  
{
294  
{
295  
    ep    = endpoint{};
295  
    ep    = endpoint{};
296  
    flags = reverse_flags::none;
296  
    flags = reverse_flags::none;
297  
    stored_host.clear();
297  
    stored_host.clear();
298  
    stored_service.clear();
298  
    stored_service.clear();
299  
    gai_error = 0;
299  
    gai_error = 0;
300  
    cancelled.store(false, std::memory_order_relaxed);
300  
    cancelled.store(false, std::memory_order_relaxed);
301  
    stop_cb.reset();
301  
    stop_cb.reset();
302  
    ec_out     = nullptr;
302  
    ec_out     = nullptr;
303  
    result_out = nullptr;
303  
    result_out = nullptr;
304  
}
304  
}
305  

305  

306  
inline void
306  
inline void
307  
posix_resolver::reverse_resolve_op::operator()()
307  
posix_resolver::reverse_resolve_op::operator()()
308  
{
308  
{
309  
    stop_cb.reset(); // Disconnect stop callback
309  
    stop_cb.reset(); // Disconnect stop callback
310  

310  

311  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
311  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
312  

312  

313  
    if (ec_out)
313  
    if (ec_out)
314  
    {
314  
    {
315  
        if (was_cancelled)
315  
        if (was_cancelled)
316  
            *ec_out = capy::error::canceled;
316  
            *ec_out = capy::error::canceled;
317  
        else if (gai_error != 0)
317  
        else if (gai_error != 0)
318  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
318  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
319  
        else
319  
        else
320  
            *ec_out = {}; // Clear on success
320  
            *ec_out = {}; // Clear on success
321  
    }
321  
    }
322  

322  

323  
    if (result_out && !was_cancelled && gai_error == 0)
323  
    if (result_out && !was_cancelled && gai_error == 0)
324  
    {
324  
    {
325  
        *result_out = reverse_resolver_result(
325  
        *result_out = reverse_resolver_result(
326  
            ep, std::move(stored_host), std::move(stored_service));
326  
            ep, std::move(stored_host), std::move(stored_service));
327  
    }
327  
    }
328  

328  

329  
    impl->svc_.work_finished();
329  
    impl->svc_.work_finished();
330  
    dispatch_coro(ex, h).resume();
330  
    dispatch_coro(ex, h).resume();
331  
}
331  
}
332  

332  

333  
inline void
333  
inline void
334  
posix_resolver::reverse_resolve_op::destroy()
334  
posix_resolver::reverse_resolve_op::destroy()
335  
{
335  
{
336  
    stop_cb.reset();
336  
    stop_cb.reset();
337  
}
337  
}
338  

338  

339  
inline void
339  
inline void
340  
posix_resolver::reverse_resolve_op::request_cancel() noexcept
340  
posix_resolver::reverse_resolve_op::request_cancel() noexcept
341  
{
341  
{
342  
    cancelled.store(true, std::memory_order_release);
342  
    cancelled.store(true, std::memory_order_release);
343  
}
343  
}
344  

344  

345  
inline void
345  
inline void
346  
posix_resolver::reverse_resolve_op::start(std::stop_token const& token)
346  
posix_resolver::reverse_resolve_op::start(std::stop_token const& token)
347  
{
347  
{
348  
    cancelled.store(false, std::memory_order_release);
348  
    cancelled.store(false, std::memory_order_release);
349  
    stop_cb.reset();
349  
    stop_cb.reset();
350  

350  

351  
    if (token.stop_possible())
351  
    if (token.stop_possible())
352  
        stop_cb.emplace(token, canceller{this});
352  
        stop_cb.emplace(token, canceller{this});
353  
}
353  
}
354  

354  

355  
// posix_resolver implementation
355  
// posix_resolver implementation
356  

356  

357  
inline std::coroutine_handle<>
357  
inline std::coroutine_handle<>
358  
posix_resolver::resolve(
358  
posix_resolver::resolve(
359  
    std::coroutine_handle<> h,
359  
    std::coroutine_handle<> h,
360  
    capy::executor_ref ex,
360  
    capy::executor_ref ex,
361  
    std::string_view host,
361  
    std::string_view host,
362  
    std::string_view service,
362  
    std::string_view service,
363  
    resolve_flags flags,
363  
    resolve_flags flags,
364  
    std::stop_token token,
364  
    std::stop_token token,
365  
    std::error_code* ec,
365  
    std::error_code* ec,
366  
    resolver_results* out)
366  
    resolver_results* out)
367  
{
367  
{
368  
    auto& op = op_;
368  
    auto& op = op_;
369  
    op.reset();
369  
    op.reset();
370  
    op.h       = h;
370  
    op.h       = h;
371  
    op.ex      = ex;
371  
    op.ex      = ex;
372  
    op.impl    = this;
372  
    op.impl    = this;
373  
    op.ec_out  = ec;
373  
    op.ec_out  = ec;
374  
    op.out     = out;
374  
    op.out     = out;
375  
    op.host    = host;
375  
    op.host    = host;
376  
    op.service = service;
376  
    op.service = service;
377  
    op.flags   = flags;
377  
    op.flags   = flags;
378  
    op.start(token);
378  
    op.start(token);
379  

379  

380  
    // Keep io_context alive while resolution is pending
380  
    // Keep io_context alive while resolution is pending
381  
    op.ex.on_work_started();
381  
    op.ex.on_work_started();
382  

382  

383  
    // Prevent impl destruction while work is in flight
383  
    // Prevent impl destruction while work is in flight
384  
    resolve_pool_op_.resolver_ = this;
384  
    resolve_pool_op_.resolver_ = this;
385  
    resolve_pool_op_.ref_      = this->shared_from_this();
385  
    resolve_pool_op_.ref_      = this->shared_from_this();
386  
    resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
386  
    resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
387  
    if (!svc_.pool().post(&resolve_pool_op_))
387  
    if (!svc_.pool().post(&resolve_pool_op_))
388  
    {
388  
    {
389  
        // Pool shut down — complete with cancellation
389  
        // Pool shut down — complete with cancellation
390  
        resolve_pool_op_.ref_.reset();
390  
        resolve_pool_op_.ref_.reset();
391  
        op.cancelled.store(true, std::memory_order_release);
391  
        op.cancelled.store(true, std::memory_order_release);
392  
        svc_.post(&op_);
392  
        svc_.post(&op_);
393  
    }
393  
    }
394  
    return std::noop_coroutine();
394  
    return std::noop_coroutine();
395  
}
395  
}
396  

396  

397  
inline std::coroutine_handle<>
397  
inline std::coroutine_handle<>
398  
posix_resolver::reverse_resolve(
398  
posix_resolver::reverse_resolve(
399  
    std::coroutine_handle<> h,
399  
    std::coroutine_handle<> h,
400  
    capy::executor_ref ex,
400  
    capy::executor_ref ex,
401  
    endpoint const& ep,
401  
    endpoint const& ep,
402  
    reverse_flags flags,
402  
    reverse_flags flags,
403  
    std::stop_token token,
403  
    std::stop_token token,
404  
    std::error_code* ec,
404  
    std::error_code* ec,
405  
    reverse_resolver_result* result_out)
405  
    reverse_resolver_result* result_out)
406  
{
406  
{
407  
    auto& op = reverse_op_;
407  
    auto& op = reverse_op_;
408  
    op.reset();
408  
    op.reset();
409  
    op.h          = h;
409  
    op.h          = h;
410  
    op.ex         = ex;
410  
    op.ex         = ex;
411  
    op.impl       = this;
411  
    op.impl       = this;
412  
    op.ec_out     = ec;
412  
    op.ec_out     = ec;
413  
    op.result_out = result_out;
413  
    op.result_out = result_out;
414  
    op.ep         = ep;
414  
    op.ep         = ep;
415  
    op.flags      = flags;
415  
    op.flags      = flags;
416  
    op.start(token);
416  
    op.start(token);
417  

417  

418  
    // Keep io_context alive while resolution is pending
418  
    // Keep io_context alive while resolution is pending
419  
    op.ex.on_work_started();
419  
    op.ex.on_work_started();
420  

420  

421  
    // Prevent impl destruction while work is in flight
421  
    // Prevent impl destruction while work is in flight
422  
    reverse_pool_op_.resolver_ = this;
422  
    reverse_pool_op_.resolver_ = this;
423  
    reverse_pool_op_.ref_      = this->shared_from_this();
423  
    reverse_pool_op_.ref_      = this->shared_from_this();
424  
    reverse_pool_op_.func_ = &posix_resolver::do_reverse_resolve_work;
424  
    reverse_pool_op_.func_ = &posix_resolver::do_reverse_resolve_work;
425  
    if (!svc_.pool().post(&reverse_pool_op_))
425  
    if (!svc_.pool().post(&reverse_pool_op_))
426  
    {
426  
    {
427  
        // Pool shut down — complete with cancellation
427  
        // Pool shut down — complete with cancellation
428  
        reverse_pool_op_.ref_.reset();
428  
        reverse_pool_op_.ref_.reset();
429  
        op.cancelled.store(true, std::memory_order_release);
429  
        op.cancelled.store(true, std::memory_order_release);
430  
        svc_.post(&reverse_op_);
430  
        svc_.post(&reverse_op_);
431  
    }
431  
    }
432  
    return std::noop_coroutine();
432  
    return std::noop_coroutine();
433  
}
433  
}
434  

434  

435  
inline void
435  
inline void
436  
posix_resolver::cancel() noexcept
436  
posix_resolver::cancel() noexcept
437  
{
437  
{
438  
    op_.request_cancel();
438  
    op_.request_cancel();
439  
    reverse_op_.request_cancel();
439  
    reverse_op_.request_cancel();
440  
}
440  
}
441  

441  

442  
inline void
442  
inline void
443  
posix_resolver::do_resolve_work(pool_work_item* w) noexcept
443  
posix_resolver::do_resolve_work(pool_work_item* w) noexcept
444  
{
444  
{
445  
    auto* pw   = static_cast<pool_op*>(w);
445  
    auto* pw   = static_cast<pool_op*>(w);
446  
    auto* self = pw->resolver_;
446  
    auto* self = pw->resolver_;
447  

447  

448  
    struct addrinfo hints{};
448  
    struct addrinfo hints{};
449  
    hints.ai_family   = AF_UNSPEC;
449  
    hints.ai_family   = AF_UNSPEC;
450  
    hints.ai_socktype = SOCK_STREAM;
450  
    hints.ai_socktype = SOCK_STREAM;
451  
    hints.ai_flags = posix_resolver_detail::flags_to_hints(self->op_.flags);
451  
    hints.ai_flags = posix_resolver_detail::flags_to_hints(self->op_.flags);
452  

452  

453  
    struct addrinfo* ai = nullptr;
453  
    struct addrinfo* ai = nullptr;
454  
    int result          = ::getaddrinfo(
454  
    int result          = ::getaddrinfo(
455  
        self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
455  
        self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
456  
        self->op_.service.empty() ? nullptr : self->op_.service.c_str(),
456  
        self->op_.service.empty() ? nullptr : self->op_.service.c_str(),
457  
        &hints, &ai);
457  
        &hints, &ai);
458  

458  

459  
    if (!self->op_.cancelled.load(std::memory_order_acquire))
459  
    if (!self->op_.cancelled.load(std::memory_order_acquire))
460  
    {
460  
    {
461  
        if (result == 0 && ai)
461  
        if (result == 0 && ai)
462  
        {
462  
        {
463  
            self->op_.stored_results =
463  
            self->op_.stored_results =
464  
                posix_resolver_detail::convert_results(
464  
                posix_resolver_detail::convert_results(
465  
                    ai, self->op_.host, self->op_.service);
465  
                    ai, self->op_.host, self->op_.service);
466  
            self->op_.gai_error = 0;
466  
            self->op_.gai_error = 0;
467  
        }
467  
        }
468  
        else
468  
        else
469  
        {
469  
        {
470  
            self->op_.gai_error = result;
470  
            self->op_.gai_error = result;
471  
        }
471  
        }
472  
    }
472  
    }
473  

473  

474  
    if (ai)
474  
    if (ai)
475  
        ::freeaddrinfo(ai);
475  
        ::freeaddrinfo(ai);
476  

476  

477  
    // Move ref to stack before post — post may trigger destroy_impl
477  
    // Move ref to stack before post — post may trigger destroy_impl
478  
    // which erases the last shared_ptr, destroying *self (and *pw)
478  
    // which erases the last shared_ptr, destroying *self (and *pw)
479  
    auto ref = std::move(pw->ref_);
479  
    auto ref = std::move(pw->ref_);
480  
    self->svc_.post(&self->op_);
480  
    self->svc_.post(&self->op_);
481  
}
481  
}
482  

482  

483  
inline void
483  
inline void
484  
posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
484  
posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
485  
{
485  
{
486  
    auto* pw   = static_cast<pool_op*>(w);
486  
    auto* pw   = static_cast<pool_op*>(w);
487  
    auto* self = pw->resolver_;
487  
    auto* self = pw->resolver_;
488  

488  

489  
    sockaddr_storage ss{};
489  
    sockaddr_storage ss{};
490  
    socklen_t ss_len;
490  
    socklen_t ss_len;
491  

491  

492  
    if (self->reverse_op_.ep.is_v4())
492  
    if (self->reverse_op_.ep.is_v4())
493  
    {
493  
    {
494  
        auto sa = to_sockaddr_in(self->reverse_op_.ep);
494  
        auto sa = to_sockaddr_in(self->reverse_op_.ep);
495  
        std::memcpy(&ss, &sa, sizeof(sa));
495  
        std::memcpy(&ss, &sa, sizeof(sa));
496  
        ss_len = sizeof(sockaddr_in);
496  
        ss_len = sizeof(sockaddr_in);
497  
    }
497  
    }
498  
    else
498  
    else
499  
    {
499  
    {
500  
        auto sa = to_sockaddr_in6(self->reverse_op_.ep);
500  
        auto sa = to_sockaddr_in6(self->reverse_op_.ep);
501  
        std::memcpy(&ss, &sa, sizeof(sa));
501  
        std::memcpy(&ss, &sa, sizeof(sa));
502  
        ss_len = sizeof(sockaddr_in6);
502  
        ss_len = sizeof(sockaddr_in6);
503  
    }
503  
    }
504  

504  

505  
    char host[NI_MAXHOST];
505  
    char host[NI_MAXHOST];
506  
    char service[NI_MAXSERV];
506  
    char service[NI_MAXSERV];
507  

507  

508  
    int result = ::getnameinfo(
508  
    int result = ::getnameinfo(
509  
        reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host),
509  
        reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host),
510  
        service, sizeof(service),
510  
        service, sizeof(service),
511  
        posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
511  
        posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
512  

512  

513  
    if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
513  
    if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
514  
    {
514  
    {
515  
        if (result == 0)
515  
        if (result == 0)
516  
        {
516  
        {
517  
            self->reverse_op_.stored_host    = host;
517  
            self->reverse_op_.stored_host    = host;
518  
            self->reverse_op_.stored_service = service;
518  
            self->reverse_op_.stored_service = service;
519  
            self->reverse_op_.gai_error      = 0;
519  
            self->reverse_op_.gai_error      = 0;
520  
        }
520  
        }
521  
        else
521  
        else
522  
        {
522  
        {
523  
            self->reverse_op_.gai_error = result;
523  
            self->reverse_op_.gai_error = result;
524  
        }
524  
        }
525  
    }
525  
    }
526  

526  

527  
    // Move ref to stack before post — post may trigger destroy_impl
527  
    // Move ref to stack before post — post may trigger destroy_impl
528  
    // which erases the last shared_ptr, destroying *self (and *pw)
528  
    // which erases the last shared_ptr, destroying *self (and *pw)
529  
    auto ref = std::move(pw->ref_);
529  
    auto ref = std::move(pw->ref_);
530  
    self->svc_.post(&self->reverse_op_);
530  
    self->svc_.post(&self->reverse_op_);
531  
}
531  
}
532  

532  

533  
// posix_resolver_service implementation
533  
// posix_resolver_service implementation
534  

534  

535  
inline void
535  
inline void
536  
posix_resolver_service::shutdown()
536  
posix_resolver_service::shutdown()
537  
{
537  
{
538  
    std::lock_guard<std::mutex> lock(mutex_);
538  
    std::lock_guard<std::mutex> lock(mutex_);
539  

539  

540  
    // Cancel all resolvers (sets cancelled flag checked by pool threads)
540  
    // Cancel all resolvers (sets cancelled flag checked by pool threads)
541  
    for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
541  
    for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
542  
         impl       = resolver_list_.pop_front())
542  
         impl       = resolver_list_.pop_front())
543  
    {
543  
    {
544  
        impl->cancel();
544  
        impl->cancel();
545  
    }
545  
    }
546  

546  

547  
    // Clear the map which releases shared_ptrs.
547  
    // Clear the map which releases shared_ptrs.
548  
    // The thread pool service shuts down separately via
548  
    // The thread pool service shuts down separately via
549  
    // execution_context service ordering.
549  
    // execution_context service ordering.
550  
    resolver_ptrs_.clear();
550  
    resolver_ptrs_.clear();
551  
}
551  
}
552  

552  

553  
inline io_object::implementation*
553  
inline io_object::implementation*
554  
posix_resolver_service::construct()
554  
posix_resolver_service::construct()
555  
{
555  
{
556  
    auto ptr   = std::make_shared<posix_resolver>(*this);
556  
    auto ptr   = std::make_shared<posix_resolver>(*this);
557  
    auto* impl = ptr.get();
557  
    auto* impl = ptr.get();
558  

558  

559  
    {
559  
    {
560  
        std::lock_guard<std::mutex> lock(mutex_);
560  
        std::lock_guard<std::mutex> lock(mutex_);
561  
        resolver_list_.push_back(impl);
561  
        resolver_list_.push_back(impl);
562  
        resolver_ptrs_[impl] = std::move(ptr);
562  
        resolver_ptrs_[impl] = std::move(ptr);
563  
    }
563  
    }
564  

564  

565  
    return impl;
565  
    return impl;
566  
}
566  
}
567  

567  

568  
inline void
568  
inline void
569  
posix_resolver_service::destroy_impl(posix_resolver& impl)
569  
posix_resolver_service::destroy_impl(posix_resolver& impl)
570  
{
570  
{
571  
    std::lock_guard<std::mutex> lock(mutex_);
571  
    std::lock_guard<std::mutex> lock(mutex_);
572  
    resolver_list_.remove(&impl);
572  
    resolver_list_.remove(&impl);
573  
    resolver_ptrs_.erase(&impl);
573  
    resolver_ptrs_.erase(&impl);
574  
}
574  
}
575  

575  

576  
inline void
576  
inline void
577  
posix_resolver_service::post(scheduler_op* op)
577  
posix_resolver_service::post(scheduler_op* op)
578  
{
578  
{
579  
    sched_->post(op);
579  
    sched_->post(op);
580  
}
580  
}
581  

581  

582  
inline void
582  
inline void
583  
posix_resolver_service::work_started() noexcept
583  
posix_resolver_service::work_started() noexcept
584  
{
584  
{
585  
    sched_->work_started();
585  
    sched_->work_started();
586  
}
586  
}
587  

587  

588  
inline void
588  
inline void
589  
posix_resolver_service::work_finished() noexcept
589  
posix_resolver_service::work_finished() noexcept
590  
{
590  
{
591  
    sched_->work_finished();
591  
    sched_->work_finished();
592  
}
592  
}
593  

593  

594  
// Free function to get/create the resolver service
594  
// Free function to get/create the resolver service
595  

595  

596  
inline posix_resolver_service&
596  
inline posix_resolver_service&
597  
get_resolver_service(capy::execution_context& ctx, scheduler& sched)
597  
get_resolver_service(capy::execution_context& ctx, scheduler& sched)
598  
{
598  
{
599  
    return ctx.make_service<posix_resolver_service>(sched);
599  
    return ctx.make_service<posix_resolver_service>(sched);
600  
}
600  
}
601  

601  

602  
} // namespace boost::corosio::detail
602  
} // namespace boost::corosio::detail
603  

603  

604  
#endif // BOOST_COROSIO_POSIX
604  
#endif // BOOST_COROSIO_POSIX
605  

605  

606  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
606  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP