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/native/native_scheduler.hpp>
18 +
#include <boost/corosio/native/detail/reactor/reactor_scheduler.hpp>
19  
#include <boost/corosio/detail/thread_pool.hpp>
19  
#include <boost/corosio/detail/thread_pool.hpp>
20  

20  

21  
#include <unordered_map>
21  
#include <unordered_map>
22  

22  

23  
namespace boost::corosio::detail {
23  
namespace boost::corosio::detail {
24  

24  

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

26  

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

36  

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

42  

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

44  

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

47  

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

49  

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

56  

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

59  

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

63  

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

69  

70  
    /** Return true if single-threaded mode is active. */
70  
    /** Return true if single-threaded mode is active. */
71  
    bool single_threaded() const noexcept
71  
    bool single_threaded() const noexcept
72  
    {
72  
    {
73 -
        return static_cast<native_scheduler const*>(sched_)
73 +
        return static_cast<reactor_scheduler const*>(sched_)
74 -
            ->single_threaded_;
74 +
            ->is_single_threaded();
75  
    }
75  
    }
76  

76  

77  
private:
77  
private:
78  
    scheduler* sched_;
78  
    scheduler* sched_;
79  
    thread_pool& pool_;
79  
    thread_pool& pool_;
80  
    std::mutex mutex_;
80  
    std::mutex mutex_;
81  
    intrusive_list<posix_resolver> resolver_list_;
81  
    intrusive_list<posix_resolver> resolver_list_;
82  
    std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
82  
    std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
83  
        resolver_ptrs_;
83  
        resolver_ptrs_;
84  
};
84  
};
85  

85  

86  
/** Get or create the resolver service for the given context.
86  
/** Get or create the resolver service for the given context.
87  

87  

88  
    This function is called by the concrete scheduler during initialization
88  
    This function is called by the concrete scheduler during initialization
89  
    to create the resolver service with a reference to itself.
89  
    to create the resolver service with a reference to itself.
90  

90  

91  
    @param ctx Reference to the owning execution_context.
91  
    @param ctx Reference to the owning execution_context.
92  
    @param sched Reference to the scheduler for posting completions.
92  
    @param sched Reference to the scheduler for posting completions.
93  
    @return Reference to the resolver service.
93  
    @return Reference to the resolver service.
94  
*/
94  
*/
95  
posix_resolver_service&
95  
posix_resolver_service&
96  
get_resolver_service(capy::execution_context& ctx, scheduler& sched);
96  
get_resolver_service(capy::execution_context& ctx, scheduler& sched);
97  

97  

98  
// ---------------------------------------------------------------------------
98  
// ---------------------------------------------------------------------------
99  
// Inline implementation
99  
// Inline implementation
100  
// ---------------------------------------------------------------------------
100  
// ---------------------------------------------------------------------------
101  

101  

102  
// posix_resolver_detail helpers
102  
// posix_resolver_detail helpers
103  

103  

104  
inline int
104  
inline int
105  
posix_resolver_detail::flags_to_hints(resolve_flags flags)
105  
posix_resolver_detail::flags_to_hints(resolve_flags flags)
106  
{
106  
{
107  
    int hints = 0;
107  
    int hints = 0;
108  

108  

109  
    if ((flags & resolve_flags::passive) != resolve_flags::none)
109  
    if ((flags & resolve_flags::passive) != resolve_flags::none)
110  
        hints |= AI_PASSIVE;
110  
        hints |= AI_PASSIVE;
111  
    if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
111  
    if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
112  
        hints |= AI_NUMERICHOST;
112  
        hints |= AI_NUMERICHOST;
113  
    if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
113  
    if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
114  
        hints |= AI_NUMERICSERV;
114  
        hints |= AI_NUMERICSERV;
115  
    if ((flags & resolve_flags::address_configured) != resolve_flags::none)
115  
    if ((flags & resolve_flags::address_configured) != resolve_flags::none)
116  
        hints |= AI_ADDRCONFIG;
116  
        hints |= AI_ADDRCONFIG;
117  
    if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
117  
    if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
118  
        hints |= AI_V4MAPPED;
118  
        hints |= AI_V4MAPPED;
119  
    if ((flags & resolve_flags::all_matching) != resolve_flags::none)
119  
    if ((flags & resolve_flags::all_matching) != resolve_flags::none)
120  
        hints |= AI_ALL;
120  
        hints |= AI_ALL;
121  

121  

122  
    return hints;
122  
    return hints;
123  
}
123  
}
124  

124  

125  
inline int
125  
inline int
126  
posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
126  
posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
127  
{
127  
{
128  
    int ni_flags = 0;
128  
    int ni_flags = 0;
129  

129  

130  
    if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
130  
    if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
131  
        ni_flags |= NI_NUMERICHOST;
131  
        ni_flags |= NI_NUMERICHOST;
132  
    if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
132  
    if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
133  
        ni_flags |= NI_NUMERICSERV;
133  
        ni_flags |= NI_NUMERICSERV;
134  
    if ((flags & reverse_flags::name_required) != reverse_flags::none)
134  
    if ((flags & reverse_flags::name_required) != reverse_flags::none)
135  
        ni_flags |= NI_NAMEREQD;
135  
        ni_flags |= NI_NAMEREQD;
136  
    if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
136  
    if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
137  
        ni_flags |= NI_DGRAM;
137  
        ni_flags |= NI_DGRAM;
138  

138  

139  
    return ni_flags;
139  
    return ni_flags;
140  
}
140  
}
141  

141  

142  
inline resolver_results
142  
inline resolver_results
143  
posix_resolver_detail::convert_results(
143  
posix_resolver_detail::convert_results(
144  
    struct addrinfo* ai, std::string_view host, std::string_view service)
144  
    struct addrinfo* ai, std::string_view host, std::string_view service)
145  
{
145  
{
146  
    std::vector<resolver_entry> entries;
146  
    std::vector<resolver_entry> entries;
147  
    entries.reserve(4); // Most lookups return 1-4 addresses
147  
    entries.reserve(4); // Most lookups return 1-4 addresses
148  

148  

149  
    for (auto* p = ai; p != nullptr; p = p->ai_next)
149  
    for (auto* p = ai; p != nullptr; p = p->ai_next)
150  
    {
150  
    {
151  
        if (p->ai_family == AF_INET)
151  
        if (p->ai_family == AF_INET)
152  
        {
152  
        {
153  
            auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
153  
            auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
154  
            auto ep    = from_sockaddr_in(*addr);
154  
            auto ep    = from_sockaddr_in(*addr);
155  
            entries.emplace_back(ep, host, service);
155  
            entries.emplace_back(ep, host, service);
156  
        }
156  
        }
157  
        else if (p->ai_family == AF_INET6)
157  
        else if (p->ai_family == AF_INET6)
158  
        {
158  
        {
159  
            auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
159  
            auto* addr = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
160  
            auto ep    = from_sockaddr_in6(*addr);
160  
            auto ep    = from_sockaddr_in6(*addr);
161  
            entries.emplace_back(ep, host, service);
161  
            entries.emplace_back(ep, host, service);
162  
        }
162  
        }
163  
    }
163  
    }
164  

164  

165  
    return resolver_results(std::move(entries));
165  
    return resolver_results(std::move(entries));
166  
}
166  
}
167  

167  

168  
inline std::error_code
168  
inline std::error_code
169  
posix_resolver_detail::make_gai_error(int gai_err)
169  
posix_resolver_detail::make_gai_error(int gai_err)
170  
{
170  
{
171  
    // Map GAI errors to appropriate generic error codes
171  
    // Map GAI errors to appropriate generic error codes
172  
    switch (gai_err)
172  
    switch (gai_err)
173  
    {
173  
    {
174  
    case EAI_AGAIN:
174  
    case EAI_AGAIN:
175  
        // Temporary failure - try again later
175  
        // Temporary failure - try again later
176  
        return std::error_code(
176  
        return std::error_code(
177  
            static_cast<int>(std::errc::resource_unavailable_try_again),
177  
            static_cast<int>(std::errc::resource_unavailable_try_again),
178  
            std::generic_category());
178  
            std::generic_category());
179  

179  

180  
    case EAI_BADFLAGS:
180  
    case EAI_BADFLAGS:
181  
        // Invalid flags
181  
        // Invalid flags
182  
        return std::error_code(
182  
        return std::error_code(
183  
            static_cast<int>(std::errc::invalid_argument),
183  
            static_cast<int>(std::errc::invalid_argument),
184  
            std::generic_category());
184  
            std::generic_category());
185  

185  

186  
    case EAI_FAIL:
186  
    case EAI_FAIL:
187  
        // Non-recoverable failure
187  
        // Non-recoverable failure
188  
        return std::error_code(
188  
        return std::error_code(
189  
            static_cast<int>(std::errc::io_error), std::generic_category());
189  
            static_cast<int>(std::errc::io_error), std::generic_category());
190  

190  

191  
    case EAI_FAMILY:
191  
    case EAI_FAMILY:
192  
        // Address family not supported
192  
        // Address family not supported
193  
        return std::error_code(
193  
        return std::error_code(
194  
            static_cast<int>(std::errc::address_family_not_supported),
194  
            static_cast<int>(std::errc::address_family_not_supported),
195  
            std::generic_category());
195  
            std::generic_category());
196  

196  

197  
    case EAI_MEMORY:
197  
    case EAI_MEMORY:
198  
        // Memory allocation failure
198  
        // Memory allocation failure
199  
        return std::error_code(
199  
        return std::error_code(
200  
            static_cast<int>(std::errc::not_enough_memory),
200  
            static_cast<int>(std::errc::not_enough_memory),
201  
            std::generic_category());
201  
            std::generic_category());
202  

202  

203  
    case EAI_NONAME:
203  
    case EAI_NONAME:
204  
        // Host or service not found
204  
        // Host or service not found
205  
        return std::error_code(
205  
        return std::error_code(
206  
            static_cast<int>(std::errc::no_such_device_or_address),
206  
            static_cast<int>(std::errc::no_such_device_or_address),
207  
            std::generic_category());
207  
            std::generic_category());
208  

208  

209  
    case EAI_SERVICE:
209  
    case EAI_SERVICE:
210  
        // Service not supported for socket type
210  
        // Service not supported for socket type
211  
        return std::error_code(
211  
        return std::error_code(
212  
            static_cast<int>(std::errc::invalid_argument),
212  
            static_cast<int>(std::errc::invalid_argument),
213  
            std::generic_category());
213  
            std::generic_category());
214  

214  

215  
    case EAI_SOCKTYPE:
215  
    case EAI_SOCKTYPE:
216  
        // Socket type not supported
216  
        // Socket type not supported
217  
        return std::error_code(
217  
        return std::error_code(
218  
            static_cast<int>(std::errc::not_supported),
218  
            static_cast<int>(std::errc::not_supported),
219  
            std::generic_category());
219  
            std::generic_category());
220  

220  

221  
    case EAI_SYSTEM:
221  
    case EAI_SYSTEM:
222  
        // System error - use errno
222  
        // System error - use errno
223  
        return std::error_code(errno, std::generic_category());
223  
        return std::error_code(errno, std::generic_category());
224  

224  

225  
    default:
225  
    default:
226  
        // Unknown error
226  
        // Unknown error
227  
        return std::error_code(
227  
        return std::error_code(
228  
            static_cast<int>(std::errc::io_error), std::generic_category());
228  
            static_cast<int>(std::errc::io_error), std::generic_category());
229  
    }
229  
    }
230  
}
230  
}
231  

231  

232  
// posix_resolver
232  
// posix_resolver
233  

233  

234  
inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
234  
inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
235  
    : svc_(svc)
235  
    : svc_(svc)
236  
{
236  
{
237  
}
237  
}
238  

238  

239  
// posix_resolver::resolve_op implementation
239  
// posix_resolver::resolve_op implementation
240  

240  

241  
inline void
241  
inline void
242  
posix_resolver::resolve_op::reset() noexcept
242  
posix_resolver::resolve_op::reset() noexcept
243  
{
243  
{
244  
    host.clear();
244  
    host.clear();
245  
    service.clear();
245  
    service.clear();
246  
    flags          = resolve_flags::none;
246  
    flags          = resolve_flags::none;
247  
    stored_results = resolver_results{};
247  
    stored_results = resolver_results{};
248  
    gai_error      = 0;
248  
    gai_error      = 0;
249  
    cancelled.store(false, std::memory_order_relaxed);
249  
    cancelled.store(false, std::memory_order_relaxed);
250  
    stop_cb.reset();
250  
    stop_cb.reset();
251  
    ec_out = nullptr;
251  
    ec_out = nullptr;
252  
    out    = nullptr;
252  
    out    = nullptr;
253  
}
253  
}
254  

254  

255  
inline void
255  
inline void
256  
posix_resolver::resolve_op::operator()()
256  
posix_resolver::resolve_op::operator()()
257  
{
257  
{
258  
    stop_cb.reset(); // Disconnect stop callback
258  
    stop_cb.reset(); // Disconnect stop callback
259  

259  

260  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
260  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
261  

261  

262  
    if (ec_out)
262  
    if (ec_out)
263  
    {
263  
    {
264  
        if (was_cancelled)
264  
        if (was_cancelled)
265  
            *ec_out = capy::error::canceled;
265  
            *ec_out = capy::error::canceled;
266  
        else if (gai_error != 0)
266  
        else if (gai_error != 0)
267  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
267  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
268  
        else
268  
        else
269  
            *ec_out = {}; // Clear on success
269  
            *ec_out = {}; // Clear on success
270  
    }
270  
    }
271  

271  

272  
    if (out && !was_cancelled && gai_error == 0)
272  
    if (out && !was_cancelled && gai_error == 0)
273  
        *out = std::move(stored_results);
273  
        *out = std::move(stored_results);
274  

274  

275  
    impl->svc_.work_finished();
275  
    impl->svc_.work_finished();
276  
    cont_op.cont.h = h;
276  
    cont_op.cont.h = h;
277  
    dispatch_coro(ex, cont_op.cont).resume();
277  
    dispatch_coro(ex, cont_op.cont).resume();
278  
}
278  
}
279  

279  

280  
inline void
280  
inline void
281  
posix_resolver::resolve_op::destroy()
281  
posix_resolver::resolve_op::destroy()
282  
{
282  
{
283  
    stop_cb.reset();
283  
    stop_cb.reset();
284  
}
284  
}
285  

285  

286  
inline void
286  
inline void
287  
posix_resolver::resolve_op::request_cancel() noexcept
287  
posix_resolver::resolve_op::request_cancel() noexcept
288  
{
288  
{
289  
    cancelled.store(true, std::memory_order_release);
289  
    cancelled.store(true, std::memory_order_release);
290  
}
290  
}
291  

291  

292  
inline void
292  
inline void
293  
posix_resolver::resolve_op::start(std::stop_token const& token)
293  
posix_resolver::resolve_op::start(std::stop_token const& token)
294  
{
294  
{
295  
    cancelled.store(false, std::memory_order_release);
295  
    cancelled.store(false, std::memory_order_release);
296  
    stop_cb.reset();
296  
    stop_cb.reset();
297  

297  

298  
    if (token.stop_possible())
298  
    if (token.stop_possible())
299  
        stop_cb.emplace(token, canceller{this});
299  
        stop_cb.emplace(token, canceller{this});
300  
}
300  
}
301  

301  

302  
// posix_resolver::reverse_resolve_op implementation
302  
// posix_resolver::reverse_resolve_op implementation
303  

303  

304  
inline void
304  
inline void
305  
posix_resolver::reverse_resolve_op::reset() noexcept
305  
posix_resolver::reverse_resolve_op::reset() noexcept
306  
{
306  
{
307  
    ep    = endpoint{};
307  
    ep    = endpoint{};
308  
    flags = reverse_flags::none;
308  
    flags = reverse_flags::none;
309  
    stored_host.clear();
309  
    stored_host.clear();
310  
    stored_service.clear();
310  
    stored_service.clear();
311  
    gai_error = 0;
311  
    gai_error = 0;
312  
    cancelled.store(false, std::memory_order_relaxed);
312  
    cancelled.store(false, std::memory_order_relaxed);
313  
    stop_cb.reset();
313  
    stop_cb.reset();
314  
    ec_out     = nullptr;
314  
    ec_out     = nullptr;
315  
    result_out = nullptr;
315  
    result_out = nullptr;
316  
}
316  
}
317  

317  

318  
inline void
318  
inline void
319  
posix_resolver::reverse_resolve_op::operator()()
319  
posix_resolver::reverse_resolve_op::operator()()
320  
{
320  
{
321  
    stop_cb.reset(); // Disconnect stop callback
321  
    stop_cb.reset(); // Disconnect stop callback
322  

322  

323  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
323  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
324  

324  

325  
    if (ec_out)
325  
    if (ec_out)
326  
    {
326  
    {
327  
        if (was_cancelled)
327  
        if (was_cancelled)
328  
            *ec_out = capy::error::canceled;
328  
            *ec_out = capy::error::canceled;
329  
        else if (gai_error != 0)
329  
        else if (gai_error != 0)
330  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
330  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
331  
        else
331  
        else
332  
            *ec_out = {}; // Clear on success
332  
            *ec_out = {}; // Clear on success
333  
    }
333  
    }
334  

334  

335  
    if (result_out && !was_cancelled && gai_error == 0)
335  
    if (result_out && !was_cancelled && gai_error == 0)
336  
    {
336  
    {
337  
        *result_out = reverse_resolver_result(
337  
        *result_out = reverse_resolver_result(
338  
            ep, std::move(stored_host), std::move(stored_service));
338  
            ep, std::move(stored_host), std::move(stored_service));
339  
    }
339  
    }
340  

340  

341  
    impl->svc_.work_finished();
341  
    impl->svc_.work_finished();
342  
    cont_op.cont.h = h;
342  
    cont_op.cont.h = h;
343  
    dispatch_coro(ex, cont_op.cont).resume();
343  
    dispatch_coro(ex, cont_op.cont).resume();
344  
}
344  
}
345  

345  

346  
inline void
346  
inline void
347  
posix_resolver::reverse_resolve_op::destroy()
347  
posix_resolver::reverse_resolve_op::destroy()
348  
{
348  
{
349  
    stop_cb.reset();
349  
    stop_cb.reset();
350  
}
350  
}
351  

351  

352  
inline void
352  
inline void
353  
posix_resolver::reverse_resolve_op::request_cancel() noexcept
353  
posix_resolver::reverse_resolve_op::request_cancel() noexcept
354  
{
354  
{
355  
    cancelled.store(true, std::memory_order_release);
355  
    cancelled.store(true, std::memory_order_release);
356  
}
356  
}
357  

357  

358  
inline void
358  
inline void
359  
posix_resolver::reverse_resolve_op::start(std::stop_token const& token)
359  
posix_resolver::reverse_resolve_op::start(std::stop_token const& token)
360  
{
360  
{
361  
    cancelled.store(false, std::memory_order_release);
361  
    cancelled.store(false, std::memory_order_release);
362  
    stop_cb.reset();
362  
    stop_cb.reset();
363  

363  

364  
    if (token.stop_possible())
364  
    if (token.stop_possible())
365  
        stop_cb.emplace(token, canceller{this});
365  
        stop_cb.emplace(token, canceller{this});
366  
}
366  
}
367  

367  

368  
// posix_resolver implementation
368  
// posix_resolver implementation
369  

369  

370  
inline std::coroutine_handle<>
370  
inline std::coroutine_handle<>
371  
posix_resolver::resolve(
371  
posix_resolver::resolve(
372  
    std::coroutine_handle<> h,
372  
    std::coroutine_handle<> h,
373  
    capy::executor_ref ex,
373  
    capy::executor_ref ex,
374  
    std::string_view host,
374  
    std::string_view host,
375  
    std::string_view service,
375  
    std::string_view service,
376  
    resolve_flags flags,
376  
    resolve_flags flags,
377  
    std::stop_token token,
377  
    std::stop_token token,
378  
    std::error_code* ec,
378  
    std::error_code* ec,
379  
    resolver_results* out)
379  
    resolver_results* out)
380  
{
380  
{
381  
    if (svc_.single_threaded())
381  
    if (svc_.single_threaded())
382  
    {
382  
    {
383  
        *ec = std::make_error_code(std::errc::operation_not_supported);
383  
        *ec = std::make_error_code(std::errc::operation_not_supported);
384  
        op_.cont_op.cont.h = h;
384  
        op_.cont_op.cont.h = h;
385  
        return dispatch_coro(ex, op_.cont_op.cont);
385  
        return dispatch_coro(ex, op_.cont_op.cont);
386  
    }
386  
    }
387  

387  

388  
    auto& op = op_;
388  
    auto& op = op_;
389  
    op.reset();
389  
    op.reset();
390  
    op.h       = h;
390  
    op.h       = h;
391  
    op.ex      = ex;
391  
    op.ex      = ex;
392  
    op.impl    = this;
392  
    op.impl    = this;
393  
    op.ec_out  = ec;
393  
    op.ec_out  = ec;
394  
    op.out     = out;
394  
    op.out     = out;
395  
    op.host    = host;
395  
    op.host    = host;
396  
    op.service = service;
396  
    op.service = service;
397  
    op.flags   = flags;
397  
    op.flags   = flags;
398  
    op.start(token);
398  
    op.start(token);
399  

399  

400  
    // Keep io_context alive while resolution is pending
400  
    // Keep io_context alive while resolution is pending
401  
    op.ex.on_work_started();
401  
    op.ex.on_work_started();
402  

402  

403  
    // Prevent impl destruction while work is in flight
403  
    // Prevent impl destruction while work is in flight
404  
    resolve_pool_op_.resolver_ = this;
404  
    resolve_pool_op_.resolver_ = this;
405  
    resolve_pool_op_.ref_      = this->shared_from_this();
405  
    resolve_pool_op_.ref_      = this->shared_from_this();
406  
    resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
406  
    resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
407  
    if (!svc_.pool().post(&resolve_pool_op_))
407  
    if (!svc_.pool().post(&resolve_pool_op_))
408  
    {
408  
    {
409  
        // Pool shut down — complete with cancellation
409  
        // Pool shut down — complete with cancellation
410  
        resolve_pool_op_.ref_.reset();
410  
        resolve_pool_op_.ref_.reset();
411  
        op.cancelled.store(true, std::memory_order_release);
411  
        op.cancelled.store(true, std::memory_order_release);
412  
        svc_.post(&op_);
412  
        svc_.post(&op_);
413  
    }
413  
    }
414  
    return std::noop_coroutine();
414  
    return std::noop_coroutine();
415  
}
415  
}
416  

416  

417  
inline std::coroutine_handle<>
417  
inline std::coroutine_handle<>
418  
posix_resolver::reverse_resolve(
418  
posix_resolver::reverse_resolve(
419  
    std::coroutine_handle<> h,
419  
    std::coroutine_handle<> h,
420  
    capy::executor_ref ex,
420  
    capy::executor_ref ex,
421  
    endpoint const& ep,
421  
    endpoint const& ep,
422  
    reverse_flags flags,
422  
    reverse_flags flags,
423  
    std::stop_token token,
423  
    std::stop_token token,
424  
    std::error_code* ec,
424  
    std::error_code* ec,
425  
    reverse_resolver_result* result_out)
425  
    reverse_resolver_result* result_out)
426  
{
426  
{
427  
    if (svc_.single_threaded())
427  
    if (svc_.single_threaded())
428  
    {
428  
    {
429  
        *ec = std::make_error_code(std::errc::operation_not_supported);
429  
        *ec = std::make_error_code(std::errc::operation_not_supported);
430  
        reverse_op_.cont_op.cont.h = h;
430  
        reverse_op_.cont_op.cont.h = h;
431  
        return dispatch_coro(ex, reverse_op_.cont_op.cont);
431  
        return dispatch_coro(ex, reverse_op_.cont_op.cont);
432  
    }
432  
    }
433  

433  

434  
    auto& op = reverse_op_;
434  
    auto& op = reverse_op_;
435  
    op.reset();
435  
    op.reset();
436  
    op.h          = h;
436  
    op.h          = h;
437  
    op.ex         = ex;
437  
    op.ex         = ex;
438  
    op.impl       = this;
438  
    op.impl       = this;
439  
    op.ec_out     = ec;
439  
    op.ec_out     = ec;
440  
    op.result_out = result_out;
440  
    op.result_out = result_out;
441  
    op.ep         = ep;
441  
    op.ep         = ep;
442  
    op.flags      = flags;
442  
    op.flags      = flags;
443  
    op.start(token);
443  
    op.start(token);
444  

444  

445  
    // Keep io_context alive while resolution is pending
445  
    // Keep io_context alive while resolution is pending
446  
    op.ex.on_work_started();
446  
    op.ex.on_work_started();
447  

447  

448  
    // Prevent impl destruction while work is in flight
448  
    // Prevent impl destruction while work is in flight
449  
    reverse_pool_op_.resolver_ = this;
449  
    reverse_pool_op_.resolver_ = this;
450  
    reverse_pool_op_.ref_      = this->shared_from_this();
450  
    reverse_pool_op_.ref_      = this->shared_from_this();
451  
    reverse_pool_op_.func_     = &posix_resolver::do_reverse_resolve_work;
451  
    reverse_pool_op_.func_     = &posix_resolver::do_reverse_resolve_work;
452  
    if (!svc_.pool().post(&reverse_pool_op_))
452  
    if (!svc_.pool().post(&reverse_pool_op_))
453  
    {
453  
    {
454  
        // Pool shut down — complete with cancellation
454  
        // Pool shut down — complete with cancellation
455  
        reverse_pool_op_.ref_.reset();
455  
        reverse_pool_op_.ref_.reset();
456  
        op.cancelled.store(true, std::memory_order_release);
456  
        op.cancelled.store(true, std::memory_order_release);
457  
        svc_.post(&reverse_op_);
457  
        svc_.post(&reverse_op_);
458  
    }
458  
    }
459  
    return std::noop_coroutine();
459  
    return std::noop_coroutine();
460  
}
460  
}
461  

461  

462  
inline void
462  
inline void
463  
posix_resolver::cancel() noexcept
463  
posix_resolver::cancel() noexcept
464  
{
464  
{
465  
    op_.request_cancel();
465  
    op_.request_cancel();
466  
    reverse_op_.request_cancel();
466  
    reverse_op_.request_cancel();
467  
}
467  
}
468  

468  

469  
inline void
469  
inline void
470  
posix_resolver::do_resolve_work(pool_work_item* w) noexcept
470  
posix_resolver::do_resolve_work(pool_work_item* w) noexcept
471  
{
471  
{
472  
    auto* pw   = static_cast<pool_op*>(w);
472  
    auto* pw   = static_cast<pool_op*>(w);
473  
    auto* self = pw->resolver_;
473  
    auto* self = pw->resolver_;
474  

474  

475  
    struct addrinfo hints{};
475  
    struct addrinfo hints{};
476  
    hints.ai_family   = AF_UNSPEC;
476  
    hints.ai_family   = AF_UNSPEC;
477  
    hints.ai_socktype = SOCK_STREAM;
477  
    hints.ai_socktype = SOCK_STREAM;
478  
    hints.ai_flags    = posix_resolver_detail::flags_to_hints(self->op_.flags);
478  
    hints.ai_flags    = posix_resolver_detail::flags_to_hints(self->op_.flags);
479  

479  

480  
    struct addrinfo* ai = nullptr;
480  
    struct addrinfo* ai = nullptr;
481  
    int result          = ::getaddrinfo(
481  
    int result          = ::getaddrinfo(
482  
        self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
482  
        self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
483  
        self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
483  
        self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
484  
        &ai);
484  
        &ai);
485  

485  

486  
    if (!self->op_.cancelled.load(std::memory_order_acquire))
486  
    if (!self->op_.cancelled.load(std::memory_order_acquire))
487  
    {
487  
    {
488  
        if (result == 0 && ai)
488  
        if (result == 0 && ai)
489  
        {
489  
        {
490  
            self->op_.stored_results = posix_resolver_detail::convert_results(
490  
            self->op_.stored_results = posix_resolver_detail::convert_results(
491  
                ai, self->op_.host, self->op_.service);
491  
                ai, self->op_.host, self->op_.service);
492  
            self->op_.gai_error = 0;
492  
            self->op_.gai_error = 0;
493  
        }
493  
        }
494  
        else
494  
        else
495  
        {
495  
        {
496  
            self->op_.gai_error = result;
496  
            self->op_.gai_error = result;
497  
        }
497  
        }
498  
    }
498  
    }
499  

499  

500  
    if (ai)
500  
    if (ai)
501  
        ::freeaddrinfo(ai);
501  
        ::freeaddrinfo(ai);
502  

502  

503  
    // Move ref to stack before post — post may trigger destroy_impl
503  
    // Move ref to stack before post — post may trigger destroy_impl
504  
    // which erases the last shared_ptr, destroying *self (and *pw)
504  
    // which erases the last shared_ptr, destroying *self (and *pw)
505  
    auto ref = std::move(pw->ref_);
505  
    auto ref = std::move(pw->ref_);
506  
    self->svc_.post(&self->op_);
506  
    self->svc_.post(&self->op_);
507  
}
507  
}
508  

508  

509  
inline void
509  
inline void
510  
posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
510  
posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
511  
{
511  
{
512  
    auto* pw   = static_cast<pool_op*>(w);
512  
    auto* pw   = static_cast<pool_op*>(w);
513  
    auto* self = pw->resolver_;
513  
    auto* self = pw->resolver_;
514  

514  

515  
    sockaddr_storage ss{};
515  
    sockaddr_storage ss{};
516  
    socklen_t ss_len;
516  
    socklen_t ss_len;
517  

517  

518  
    if (self->reverse_op_.ep.is_v4())
518  
    if (self->reverse_op_.ep.is_v4())
519  
    {
519  
    {
520  
        auto sa = to_sockaddr_in(self->reverse_op_.ep);
520  
        auto sa = to_sockaddr_in(self->reverse_op_.ep);
521  
        std::memcpy(&ss, &sa, sizeof(sa));
521  
        std::memcpy(&ss, &sa, sizeof(sa));
522  
        ss_len = sizeof(sockaddr_in);
522  
        ss_len = sizeof(sockaddr_in);
523  
    }
523  
    }
524  
    else
524  
    else
525  
    {
525  
    {
526  
        auto sa = to_sockaddr_in6(self->reverse_op_.ep);
526  
        auto sa = to_sockaddr_in6(self->reverse_op_.ep);
527  
        std::memcpy(&ss, &sa, sizeof(sa));
527  
        std::memcpy(&ss, &sa, sizeof(sa));
528  
        ss_len = sizeof(sockaddr_in6);
528  
        ss_len = sizeof(sockaddr_in6);
529  
    }
529  
    }
530  

530  

531  
    char host[NI_MAXHOST];
531  
    char host[NI_MAXHOST];
532  
    char service[NI_MAXSERV];
532  
    char service[NI_MAXSERV];
533  

533  

534  
    int result = ::getnameinfo(
534  
    int result = ::getnameinfo(
535  
        reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
535  
        reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
536  
        sizeof(service),
536  
        sizeof(service),
537  
        posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
537  
        posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
538  

538  

539  
    if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
539  
    if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
540  
    {
540  
    {
541  
        if (result == 0)
541  
        if (result == 0)
542  
        {
542  
        {
543  
            self->reverse_op_.stored_host    = host;
543  
            self->reverse_op_.stored_host    = host;
544  
            self->reverse_op_.stored_service = service;
544  
            self->reverse_op_.stored_service = service;
545  
            self->reverse_op_.gai_error      = 0;
545  
            self->reverse_op_.gai_error      = 0;
546  
        }
546  
        }
547  
        else
547  
        else
548  
        {
548  
        {
549  
            self->reverse_op_.gai_error = result;
549  
            self->reverse_op_.gai_error = result;
550  
        }
550  
        }
551  
    }
551  
    }
552  

552  

553  
    // Move ref to stack before post — post may trigger destroy_impl
553  
    // Move ref to stack before post — post may trigger destroy_impl
554  
    // which erases the last shared_ptr, destroying *self (and *pw)
554  
    // which erases the last shared_ptr, destroying *self (and *pw)
555  
    auto ref = std::move(pw->ref_);
555  
    auto ref = std::move(pw->ref_);
556  
    self->svc_.post(&self->reverse_op_);
556  
    self->svc_.post(&self->reverse_op_);
557  
}
557  
}
558  

558  

559  
// posix_resolver_service implementation
559  
// posix_resolver_service implementation
560  

560  

561  
inline void
561  
inline void
562  
posix_resolver_service::shutdown()
562  
posix_resolver_service::shutdown()
563  
{
563  
{
564  
    std::lock_guard<std::mutex> lock(mutex_);
564  
    std::lock_guard<std::mutex> lock(mutex_);
565  

565  

566  
    // Cancel all resolvers (sets cancelled flag checked by pool threads)
566  
    // Cancel all resolvers (sets cancelled flag checked by pool threads)
567  
    for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
567  
    for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
568  
         impl       = resolver_list_.pop_front())
568  
         impl       = resolver_list_.pop_front())
569  
    {
569  
    {
570  
        impl->cancel();
570  
        impl->cancel();
571  
    }
571  
    }
572  

572  

573  
    // Clear the map which releases shared_ptrs.
573  
    // Clear the map which releases shared_ptrs.
574  
    // The thread pool service shuts down separately via
574  
    // The thread pool service shuts down separately via
575  
    // execution_context service ordering.
575  
    // execution_context service ordering.
576  
    resolver_ptrs_.clear();
576  
    resolver_ptrs_.clear();
577  
}
577  
}
578  

578  

579  
inline io_object::implementation*
579  
inline io_object::implementation*
580  
posix_resolver_service::construct()
580  
posix_resolver_service::construct()
581  
{
581  
{
582  
    auto ptr   = std::make_shared<posix_resolver>(*this);
582  
    auto ptr   = std::make_shared<posix_resolver>(*this);
583  
    auto* impl = ptr.get();
583  
    auto* impl = ptr.get();
584  

584  

585  
    {
585  
    {
586  
        std::lock_guard<std::mutex> lock(mutex_);
586  
        std::lock_guard<std::mutex> lock(mutex_);
587  
        resolver_list_.push_back(impl);
587  
        resolver_list_.push_back(impl);
588  
        resolver_ptrs_[impl] = std::move(ptr);
588  
        resolver_ptrs_[impl] = std::move(ptr);
589  
    }
589  
    }
590  

590  

591  
    return impl;
591  
    return impl;
592  
}
592  
}
593  

593  

594  
inline void
594  
inline void
595  
posix_resolver_service::destroy_impl(posix_resolver& impl)
595  
posix_resolver_service::destroy_impl(posix_resolver& impl)
596  
{
596  
{
597  
    std::lock_guard<std::mutex> lock(mutex_);
597  
    std::lock_guard<std::mutex> lock(mutex_);
598  
    resolver_list_.remove(&impl);
598  
    resolver_list_.remove(&impl);
599  
    resolver_ptrs_.erase(&impl);
599  
    resolver_ptrs_.erase(&impl);
600  
}
600  
}
601  

601  

602  
inline void
602  
inline void
603  
posix_resolver_service::post(scheduler_op* op)
603  
posix_resolver_service::post(scheduler_op* op)
604  
{
604  
{
605  
    sched_->post(op);
605  
    sched_->post(op);
606  
}
606  
}
607  

607  

608  
inline void
608  
inline void
609  
posix_resolver_service::work_started() noexcept
609  
posix_resolver_service::work_started() noexcept
610  
{
610  
{
611  
    sched_->work_started();
611  
    sched_->work_started();
612  
}
612  
}
613  

613  

614  
inline void
614  
inline void
615  
posix_resolver_service::work_finished() noexcept
615  
posix_resolver_service::work_finished() noexcept
616  
{
616  
{
617  
    sched_->work_finished();
617  
    sched_->work_finished();
618  
}
618  
}
619  

619  

620  
// Free function to get/create the resolver service
620  
// Free function to get/create the resolver service
621  

621  

622  
inline posix_resolver_service&
622  
inline posix_resolver_service&
623  
get_resolver_service(capy::execution_context& ctx, scheduler& sched)
623  
get_resolver_service(capy::execution_context& ctx, scheduler& sched)
624  
{
624  
{
625  
    return ctx.make_service<posix_resolver_service>(sched);
625  
    return ctx.make_service<posix_resolver_service>(sched);
626  
}
626  
}
627  

627  

628  
} // namespace boost::corosio::detail
628  
} // namespace boost::corosio::detail
629  

629  

630  
#endif // BOOST_COROSIO_POSIX
630  
#endif // BOOST_COROSIO_POSIX
631  

631  

632  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
632  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP