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/detail/thread_pool.hpp>
19  
#include <boost/corosio/detail/thread_pool.hpp>
19  

20  

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

22  

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

24  

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

26  

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

36  

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

44  

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

46  

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

49  

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

51  

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

58  

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

61  

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

65  

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

71  

 
72 +
    /** Return true if single-threaded mode is active. */
 
73 +
    bool single_threaded() const noexcept
 
74 +
    {
 
75 +
        return single_threaded_;
 
76 +
    }
 
77 +

69  
private:
78  
private:
70  
    scheduler* sched_;
79  
    scheduler* sched_;
71  
    thread_pool& pool_;
80  
    thread_pool& pool_;
 
81 +
    bool single_threaded_;
72  
    std::mutex mutex_;
82  
    std::mutex mutex_;
73  
    intrusive_list<posix_resolver> resolver_list_;
83  
    intrusive_list<posix_resolver> resolver_list_;
74  
    std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
84  
    std::unordered_map<posix_resolver*, std::shared_ptr<posix_resolver>>
75  
        resolver_ptrs_;
85  
        resolver_ptrs_;
76  
};
86  
};
77  

87  

78  
/** Get or create the resolver service for the given context.
88  
/** Get or create the resolver service for the given context.
79  

89  

80  
    This function is called by the concrete scheduler during initialization
90  
    This function is called by the concrete scheduler during initialization
81  
    to create the resolver service with a reference to itself.
91  
    to create the resolver service with a reference to itself.
82  

92  

83  
    @param ctx Reference to the owning execution_context.
93  
    @param ctx Reference to the owning execution_context.
84  
    @param sched Reference to the scheduler for posting completions.
94  
    @param sched Reference to the scheduler for posting completions.
85  
    @return Reference to the resolver service.
95  
    @return Reference to the resolver service.
86  
*/
96  
*/
87  
posix_resolver_service&
97  
posix_resolver_service&
88  
get_resolver_service(capy::execution_context& ctx, scheduler& sched);
98  
get_resolver_service(capy::execution_context& ctx, scheduler& sched);
89  

99  

90  
// ---------------------------------------------------------------------------
100  
// ---------------------------------------------------------------------------
91  
// Inline implementation
101  
// Inline implementation
92  
// ---------------------------------------------------------------------------
102  
// ---------------------------------------------------------------------------
93  

103  

94  
// posix_resolver_detail helpers
104  
// posix_resolver_detail helpers
95  

105  

96  
inline int
106  
inline int
97  
posix_resolver_detail::flags_to_hints(resolve_flags flags)
107  
posix_resolver_detail::flags_to_hints(resolve_flags flags)
98  
{
108  
{
99  
    int hints = 0;
109  
    int hints = 0;
100  

110  

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

123  

114  
    return hints;
124  
    return hints;
115  
}
125  
}
116  

126  

117  
inline int
127  
inline int
118  
posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
128  
posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
119  
{
129  
{
120  
    int ni_flags = 0;
130  
    int ni_flags = 0;
121  

131  

122  
    if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
132  
    if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
123  
        ni_flags |= NI_NUMERICHOST;
133  
        ni_flags |= NI_NUMERICHOST;
124  
    if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
134  
    if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
125  
        ni_flags |= NI_NUMERICSERV;
135  
        ni_flags |= NI_NUMERICSERV;
126  
    if ((flags & reverse_flags::name_required) != reverse_flags::none)
136  
    if ((flags & reverse_flags::name_required) != reverse_flags::none)
127  
        ni_flags |= NI_NAMEREQD;
137  
        ni_flags |= NI_NAMEREQD;
128  
    if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
138  
    if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
129  
        ni_flags |= NI_DGRAM;
139  
        ni_flags |= NI_DGRAM;
130  

140  

131  
    return ni_flags;
141  
    return ni_flags;
132  
}
142  
}
133  

143  

134  
inline resolver_results
144  
inline resolver_results
135  
posix_resolver_detail::convert_results(
145  
posix_resolver_detail::convert_results(
136  
    struct addrinfo* ai, std::string_view host, std::string_view service)
146  
    struct addrinfo* ai, std::string_view host, std::string_view service)
137  
{
147  
{
138  
    std::vector<resolver_entry> entries;
148  
    std::vector<resolver_entry> entries;
139  
    entries.reserve(4); // Most lookups return 1-4 addresses
149  
    entries.reserve(4); // Most lookups return 1-4 addresses
140  

150  

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

166  

157  
    return resolver_results(std::move(entries));
167  
    return resolver_results(std::move(entries));
158  
}
168  
}
159  

169  

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

181  

172  
    case EAI_BADFLAGS:
182  
    case EAI_BADFLAGS:
173  
        // Invalid flags
183  
        // Invalid flags
174  
        return std::error_code(
184  
        return std::error_code(
175  
            static_cast<int>(std::errc::invalid_argument),
185  
            static_cast<int>(std::errc::invalid_argument),
176  
            std::generic_category());
186  
            std::generic_category());
177  

187  

178  
    case EAI_FAIL:
188  
    case EAI_FAIL:
179  
        // Non-recoverable failure
189  
        // Non-recoverable failure
180  
        return std::error_code(
190  
        return std::error_code(
181  
            static_cast<int>(std::errc::io_error), std::generic_category());
191  
            static_cast<int>(std::errc::io_error), std::generic_category());
182  

192  

183  
    case EAI_FAMILY:
193  
    case EAI_FAMILY:
184  
        // Address family not supported
194  
        // Address family not supported
185  
        return std::error_code(
195  
        return std::error_code(
186  
            static_cast<int>(std::errc::address_family_not_supported),
196  
            static_cast<int>(std::errc::address_family_not_supported),
187  
            std::generic_category());
197  
            std::generic_category());
188  

198  

189  
    case EAI_MEMORY:
199  
    case EAI_MEMORY:
190  
        // Memory allocation failure
200  
        // Memory allocation failure
191  
        return std::error_code(
201  
        return std::error_code(
192  
            static_cast<int>(std::errc::not_enough_memory),
202  
            static_cast<int>(std::errc::not_enough_memory),
193  
            std::generic_category());
203  
            std::generic_category());
194  

204  

195  
    case EAI_NONAME:
205  
    case EAI_NONAME:
196  
        // Host or service not found
206  
        // Host or service not found
197  
        return std::error_code(
207  
        return std::error_code(
198  
            static_cast<int>(std::errc::no_such_device_or_address),
208  
            static_cast<int>(std::errc::no_such_device_or_address),
199  
            std::generic_category());
209  
            std::generic_category());
200  

210  

201  
    case EAI_SERVICE:
211  
    case EAI_SERVICE:
202  
        // Service not supported for socket type
212  
        // Service not supported for socket type
203  
        return std::error_code(
213  
        return std::error_code(
204  
            static_cast<int>(std::errc::invalid_argument),
214  
            static_cast<int>(std::errc::invalid_argument),
205  
            std::generic_category());
215  
            std::generic_category());
206  

216  

207  
    case EAI_SOCKTYPE:
217  
    case EAI_SOCKTYPE:
208  
        // Socket type not supported
218  
        // Socket type not supported
209  
        return std::error_code(
219  
        return std::error_code(
210  
            static_cast<int>(std::errc::not_supported),
220  
            static_cast<int>(std::errc::not_supported),
211  
            std::generic_category());
221  
            std::generic_category());
212  

222  

213  
    case EAI_SYSTEM:
223  
    case EAI_SYSTEM:
214  
        // System error - use errno
224  
        // System error - use errno
215  
        return std::error_code(errno, std::generic_category());
225  
        return std::error_code(errno, std::generic_category());
216  

226  

217  
    default:
227  
    default:
218  
        // Unknown error
228  
        // Unknown error
219  
        return std::error_code(
229  
        return std::error_code(
220  
            static_cast<int>(std::errc::io_error), std::generic_category());
230  
            static_cast<int>(std::errc::io_error), std::generic_category());
221  
    }
231  
    }
222  
}
232  
}
223  

233  

224  
// posix_resolver
234  
// posix_resolver
225  

235  

226  
inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
236  
inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
227  
    : svc_(svc)
237  
    : svc_(svc)
228  
{
238  
{
229  
}
239  
}
230  

240  

231  
// posix_resolver::resolve_op implementation
241  
// posix_resolver::resolve_op implementation
232  

242  

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

256  

247  
inline void
257  
inline void
248  
posix_resolver::resolve_op::operator()()
258  
posix_resolver::resolve_op::operator()()
249  
{
259  
{
250  
    stop_cb.reset(); // Disconnect stop callback
260  
    stop_cb.reset(); // Disconnect stop callback
251  

261  

252  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
262  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
253  

263  

254  
    if (ec_out)
264  
    if (ec_out)
255  
    {
265  
    {
256  
        if (was_cancelled)
266  
        if (was_cancelled)
257  
            *ec_out = capy::error::canceled;
267  
            *ec_out = capy::error::canceled;
258  
        else if (gai_error != 0)
268  
        else if (gai_error != 0)
259  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
269  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
260  
        else
270  
        else
261  
            *ec_out = {}; // Clear on success
271  
            *ec_out = {}; // Clear on success
262  
    }
272  
    }
263  

273  

264  
    if (out && !was_cancelled && gai_error == 0)
274  
    if (out && !was_cancelled && gai_error == 0)
265  
        *out = std::move(stored_results);
275  
        *out = std::move(stored_results);
266  

276  

267  
    impl->svc_.work_finished();
277  
    impl->svc_.work_finished();
268  
    cont_op.cont.h = h;
278  
    cont_op.cont.h = h;
269  
    dispatch_coro(ex, cont_op.cont).resume();
279  
    dispatch_coro(ex, cont_op.cont).resume();
270  
}
280  
}
271  

281  

272  
inline void
282  
inline void
273  
posix_resolver::resolve_op::destroy()
283  
posix_resolver::resolve_op::destroy()
274  
{
284  
{
275  
    stop_cb.reset();
285  
    stop_cb.reset();
276  
}
286  
}
277  

287  

278  
inline void
288  
inline void
279  
posix_resolver::resolve_op::request_cancel() noexcept
289  
posix_resolver::resolve_op::request_cancel() noexcept
280  
{
290  
{
281  
    cancelled.store(true, std::memory_order_release);
291  
    cancelled.store(true, std::memory_order_release);
282  
}
292  
}
283  

293  

284  
inline void
294  
inline void
285  
posix_resolver::resolve_op::start(std::stop_token const& token)
295  
posix_resolver::resolve_op::start(std::stop_token const& token)
286  
{
296  
{
287  
    cancelled.store(false, std::memory_order_release);
297  
    cancelled.store(false, std::memory_order_release);
288  
    stop_cb.reset();
298  
    stop_cb.reset();
289  

299  

290  
    if (token.stop_possible())
300  
    if (token.stop_possible())
291  
        stop_cb.emplace(token, canceller{this});
301  
        stop_cb.emplace(token, canceller{this});
292  
}
302  
}
293  

303  

294  
// posix_resolver::reverse_resolve_op implementation
304  
// posix_resolver::reverse_resolve_op implementation
295  

305  

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

319  

310  
inline void
320  
inline void
311  
posix_resolver::reverse_resolve_op::operator()()
321  
posix_resolver::reverse_resolve_op::operator()()
312  
{
322  
{
313  
    stop_cb.reset(); // Disconnect stop callback
323  
    stop_cb.reset(); // Disconnect stop callback
314  

324  

315  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
325  
    bool const was_cancelled = cancelled.load(std::memory_order_acquire);
316  

326  

317  
    if (ec_out)
327  
    if (ec_out)
318  
    {
328  
    {
319  
        if (was_cancelled)
329  
        if (was_cancelled)
320  
            *ec_out = capy::error::canceled;
330  
            *ec_out = capy::error::canceled;
321  
        else if (gai_error != 0)
331  
        else if (gai_error != 0)
322  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
332  
            *ec_out = posix_resolver_detail::make_gai_error(gai_error);
323  
        else
333  
        else
324  
            *ec_out = {}; // Clear on success
334  
            *ec_out = {}; // Clear on success
325  
    }
335  
    }
326  

336  

327  
    if (result_out && !was_cancelled && gai_error == 0)
337  
    if (result_out && !was_cancelled && gai_error == 0)
328  
    {
338  
    {
329  
        *result_out = reverse_resolver_result(
339  
        *result_out = reverse_resolver_result(
330  
            ep, std::move(stored_host), std::move(stored_service));
340  
            ep, std::move(stored_host), std::move(stored_service));
331  
    }
341  
    }
332  

342  

333  
    impl->svc_.work_finished();
343  
    impl->svc_.work_finished();
334  
    cont_op.cont.h = h;
344  
    cont_op.cont.h = h;
335  
    dispatch_coro(ex, cont_op.cont).resume();
345  
    dispatch_coro(ex, cont_op.cont).resume();
336  
}
346  
}
337  

347  

338  
inline void
348  
inline void
339  
posix_resolver::reverse_resolve_op::destroy()
349  
posix_resolver::reverse_resolve_op::destroy()
340  
{
350  
{
341  
    stop_cb.reset();
351  
    stop_cb.reset();
342  
}
352  
}
343  

353  

344  
inline void
354  
inline void
345  
posix_resolver::reverse_resolve_op::request_cancel() noexcept
355  
posix_resolver::reverse_resolve_op::request_cancel() noexcept
346  
{
356  
{
347  
    cancelled.store(true, std::memory_order_release);
357  
    cancelled.store(true, std::memory_order_release);
348  
}
358  
}
349  

359  

350  
inline void
360  
inline void
351  
posix_resolver::reverse_resolve_op::start(std::stop_token const& token)
361  
posix_resolver::reverse_resolve_op::start(std::stop_token const& token)
352  
{
362  
{
353  
    cancelled.store(false, std::memory_order_release);
363  
    cancelled.store(false, std::memory_order_release);
354  
    stop_cb.reset();
364  
    stop_cb.reset();
355  

365  

356  
    if (token.stop_possible())
366  
    if (token.stop_possible())
357  
        stop_cb.emplace(token, canceller{this});
367  
        stop_cb.emplace(token, canceller{this});
358  
}
368  
}
359  

369  

360  
// posix_resolver implementation
370  
// posix_resolver implementation
361  

371  

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

373  
    auto& op = op_;
390  
    auto& op = op_;
374  
    op.reset();
391  
    op.reset();
375  
    op.h       = h;
392  
    op.h       = h;
376  
    op.ex      = ex;
393  
    op.ex      = ex;
377  
    op.impl    = this;
394  
    op.impl    = this;
378  
    op.ec_out  = ec;
395  
    op.ec_out  = ec;
379  
    op.out     = out;
396  
    op.out     = out;
380  
    op.host    = host;
397  
    op.host    = host;
381  
    op.service = service;
398  
    op.service = service;
382  
    op.flags   = flags;
399  
    op.flags   = flags;
383  
    op.start(token);
400  
    op.start(token);
384  

401  

385  
    // Keep io_context alive while resolution is pending
402  
    // Keep io_context alive while resolution is pending
386  
    op.ex.on_work_started();
403  
    op.ex.on_work_started();
387  

404  

388  
    // Prevent impl destruction while work is in flight
405  
    // Prevent impl destruction while work is in flight
389  
    resolve_pool_op_.resolver_ = this;
406  
    resolve_pool_op_.resolver_ = this;
390  
    resolve_pool_op_.ref_      = this->shared_from_this();
407  
    resolve_pool_op_.ref_      = this->shared_from_this();
391  
    resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
408  
    resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
392  
    if (!svc_.pool().post(&resolve_pool_op_))
409  
    if (!svc_.pool().post(&resolve_pool_op_))
393  
    {
410  
    {
394  
        // Pool shut down — complete with cancellation
411  
        // Pool shut down — complete with cancellation
395  
        resolve_pool_op_.ref_.reset();
412  
        resolve_pool_op_.ref_.reset();
396  
        op.cancelled.store(true, std::memory_order_release);
413  
        op.cancelled.store(true, std::memory_order_release);
397  
        svc_.post(&op_);
414  
        svc_.post(&op_);
398  
    }
415  
    }
399  
    return std::noop_coroutine();
416  
    return std::noop_coroutine();
400  
}
417  
}
401  

418  

402  
inline std::coroutine_handle<>
419  
inline std::coroutine_handle<>
403  
posix_resolver::reverse_resolve(
420  
posix_resolver::reverse_resolve(
404  
    std::coroutine_handle<> h,
421  
    std::coroutine_handle<> h,
405  
    capy::executor_ref ex,
422  
    capy::executor_ref ex,
406  
    endpoint const& ep,
423  
    endpoint const& ep,
407  
    reverse_flags flags,
424  
    reverse_flags flags,
408  
    std::stop_token token,
425  
    std::stop_token token,
409  
    std::error_code* ec,
426  
    std::error_code* ec,
410  
    reverse_resolver_result* result_out)
427  
    reverse_resolver_result* result_out)
411  
{
428  
{
 
429 +
    if (svc_.single_threaded())
 
430 +
    {
 
431 +
        *ec = std::make_error_code(std::errc::operation_not_supported);
 
432 +
        reverse_op_.cont_op.cont.h = h;
 
433 +
        return dispatch_coro(ex, reverse_op_.cont_op.cont);
 
434 +
    }
 
435 +

412  
    auto& op = reverse_op_;
436  
    auto& op = reverse_op_;
413  
    op.reset();
437  
    op.reset();
414  
    op.h          = h;
438  
    op.h          = h;
415  
    op.ex         = ex;
439  
    op.ex         = ex;
416  
    op.impl       = this;
440  
    op.impl       = this;
417  
    op.ec_out     = ec;
441  
    op.ec_out     = ec;
418  
    op.result_out = result_out;
442  
    op.result_out = result_out;
419  
    op.ep         = ep;
443  
    op.ep         = ep;
420  
    op.flags      = flags;
444  
    op.flags      = flags;
421  
    op.start(token);
445  
    op.start(token);
422  

446  

423  
    // Keep io_context alive while resolution is pending
447  
    // Keep io_context alive while resolution is pending
424  
    op.ex.on_work_started();
448  
    op.ex.on_work_started();
425  

449  

426  
    // Prevent impl destruction while work is in flight
450  
    // Prevent impl destruction while work is in flight
427  
    reverse_pool_op_.resolver_ = this;
451  
    reverse_pool_op_.resolver_ = this;
428  
    reverse_pool_op_.ref_      = this->shared_from_this();
452  
    reverse_pool_op_.ref_      = this->shared_from_this();
429  
    reverse_pool_op_.func_     = &posix_resolver::do_reverse_resolve_work;
453  
    reverse_pool_op_.func_     = &posix_resolver::do_reverse_resolve_work;
430  
    if (!svc_.pool().post(&reverse_pool_op_))
454  
    if (!svc_.pool().post(&reverse_pool_op_))
431  
    {
455  
    {
432  
        // Pool shut down — complete with cancellation
456  
        // Pool shut down — complete with cancellation
433  
        reverse_pool_op_.ref_.reset();
457  
        reverse_pool_op_.ref_.reset();
434  
        op.cancelled.store(true, std::memory_order_release);
458  
        op.cancelled.store(true, std::memory_order_release);
435  
        svc_.post(&reverse_op_);
459  
        svc_.post(&reverse_op_);
436  
    }
460  
    }
437  
    return std::noop_coroutine();
461  
    return std::noop_coroutine();
438  
}
462  
}
439  

463  

440  
inline void
464  
inline void
441  
posix_resolver::cancel() noexcept
465  
posix_resolver::cancel() noexcept
442  
{
466  
{
443  
    op_.request_cancel();
467  
    op_.request_cancel();
444  
    reverse_op_.request_cancel();
468  
    reverse_op_.request_cancel();
445  
}
469  
}
446  

470  

447  
inline void
471  
inline void
448  
posix_resolver::do_resolve_work(pool_work_item* w) noexcept
472  
posix_resolver::do_resolve_work(pool_work_item* w) noexcept
449  
{
473  
{
450  
    auto* pw   = static_cast<pool_op*>(w);
474  
    auto* pw   = static_cast<pool_op*>(w);
451  
    auto* self = pw->resolver_;
475  
    auto* self = pw->resolver_;
452  

476  

453  
    struct addrinfo hints{};
477  
    struct addrinfo hints{};
454  
    hints.ai_family   = AF_UNSPEC;
478  
    hints.ai_family   = AF_UNSPEC;
455  
    hints.ai_socktype = SOCK_STREAM;
479  
    hints.ai_socktype = SOCK_STREAM;
456  
    hints.ai_flags    = posix_resolver_detail::flags_to_hints(self->op_.flags);
480  
    hints.ai_flags    = posix_resolver_detail::flags_to_hints(self->op_.flags);
457  

481  

458  
    struct addrinfo* ai = nullptr;
482  
    struct addrinfo* ai = nullptr;
459  
    int result          = ::getaddrinfo(
483  
    int result          = ::getaddrinfo(
460  
        self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
484  
        self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
461  
        self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
485  
        self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
462  
        &ai);
486  
        &ai);
463  

487  

464  
    if (!self->op_.cancelled.load(std::memory_order_acquire))
488  
    if (!self->op_.cancelled.load(std::memory_order_acquire))
465  
    {
489  
    {
466  
        if (result == 0 && ai)
490  
        if (result == 0 && ai)
467  
        {
491  
        {
468  
            self->op_.stored_results = posix_resolver_detail::convert_results(
492  
            self->op_.stored_results = posix_resolver_detail::convert_results(
469  
                ai, self->op_.host, self->op_.service);
493  
                ai, self->op_.host, self->op_.service);
470  
            self->op_.gai_error = 0;
494  
            self->op_.gai_error = 0;
471  
        }
495  
        }
472  
        else
496  
        else
473  
        {
497  
        {
474  
            self->op_.gai_error = result;
498  
            self->op_.gai_error = result;
475  
        }
499  
        }
476  
    }
500  
    }
477  

501  

478  
    if (ai)
502  
    if (ai)
479  
        ::freeaddrinfo(ai);
503  
        ::freeaddrinfo(ai);
480  

504  

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

510  

487  
inline void
511  
inline void
488  
posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
512  
posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
489  
{
513  
{
490  
    auto* pw   = static_cast<pool_op*>(w);
514  
    auto* pw   = static_cast<pool_op*>(w);
491  
    auto* self = pw->resolver_;
515  
    auto* self = pw->resolver_;
492  

516  

493  
    sockaddr_storage ss{};
517  
    sockaddr_storage ss{};
494  
    socklen_t ss_len;
518  
    socklen_t ss_len;
495  

519  

496  
    if (self->reverse_op_.ep.is_v4())
520  
    if (self->reverse_op_.ep.is_v4())
497  
    {
521  
    {
498  
        auto sa = to_sockaddr_in(self->reverse_op_.ep);
522  
        auto sa = to_sockaddr_in(self->reverse_op_.ep);
499  
        std::memcpy(&ss, &sa, sizeof(sa));
523  
        std::memcpy(&ss, &sa, sizeof(sa));
500  
        ss_len = sizeof(sockaddr_in);
524  
        ss_len = sizeof(sockaddr_in);
501  
    }
525  
    }
502  
    else
526  
    else
503  
    {
527  
    {
504  
        auto sa = to_sockaddr_in6(self->reverse_op_.ep);
528  
        auto sa = to_sockaddr_in6(self->reverse_op_.ep);
505  
        std::memcpy(&ss, &sa, sizeof(sa));
529  
        std::memcpy(&ss, &sa, sizeof(sa));
506  
        ss_len = sizeof(sockaddr_in6);
530  
        ss_len = sizeof(sockaddr_in6);
507  
    }
531  
    }
508  

532  

509  
    char host[NI_MAXHOST];
533  
    char host[NI_MAXHOST];
510  
    char service[NI_MAXSERV];
534  
    char service[NI_MAXSERV];
511  

535  

512  
    int result = ::getnameinfo(
536  
    int result = ::getnameinfo(
513  
        reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
537  
        reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
514  
        sizeof(service),
538  
        sizeof(service),
515  
        posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
539  
        posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
516  

540  

517  
    if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
541  
    if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
518  
    {
542  
    {
519  
        if (result == 0)
543  
        if (result == 0)
520  
        {
544  
        {
521  
            self->reverse_op_.stored_host    = host;
545  
            self->reverse_op_.stored_host    = host;
522  
            self->reverse_op_.stored_service = service;
546  
            self->reverse_op_.stored_service = service;
523  
            self->reverse_op_.gai_error      = 0;
547  
            self->reverse_op_.gai_error      = 0;
524  
        }
548  
        }
525  
        else
549  
        else
526  
        {
550  
        {
527  
            self->reverse_op_.gai_error = result;
551  
            self->reverse_op_.gai_error = result;
528  
        }
552  
        }
529  
    }
553  
    }
530  

554  

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

560  

537  
// posix_resolver_service implementation
561  
// posix_resolver_service implementation
538  

562  

539  
inline void
563  
inline void
540  
posix_resolver_service::shutdown()
564  
posix_resolver_service::shutdown()
541  
{
565  
{
542  
    std::lock_guard<std::mutex> lock(mutex_);
566  
    std::lock_guard<std::mutex> lock(mutex_);
543  

567  

544  
    // Cancel all resolvers (sets cancelled flag checked by pool threads)
568  
    // Cancel all resolvers (sets cancelled flag checked by pool threads)
545  
    for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
569  
    for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
546  
         impl       = resolver_list_.pop_front())
570  
         impl       = resolver_list_.pop_front())
547  
    {
571  
    {
548  
        impl->cancel();
572  
        impl->cancel();
549  
    }
573  
    }
550  

574  

551  
    // Clear the map which releases shared_ptrs.
575  
    // Clear the map which releases shared_ptrs.
552  
    // The thread pool service shuts down separately via
576  
    // The thread pool service shuts down separately via
553  
    // execution_context service ordering.
577  
    // execution_context service ordering.
554  
    resolver_ptrs_.clear();
578  
    resolver_ptrs_.clear();
555  
}
579  
}
556  

580  

557  
inline io_object::implementation*
581  
inline io_object::implementation*
558  
posix_resolver_service::construct()
582  
posix_resolver_service::construct()
559  
{
583  
{
560  
    auto ptr   = std::make_shared<posix_resolver>(*this);
584  
    auto ptr   = std::make_shared<posix_resolver>(*this);
561  
    auto* impl = ptr.get();
585  
    auto* impl = ptr.get();
562  

586  

563  
    {
587  
    {
564  
        std::lock_guard<std::mutex> lock(mutex_);
588  
        std::lock_guard<std::mutex> lock(mutex_);
565  
        resolver_list_.push_back(impl);
589  
        resolver_list_.push_back(impl);
566  
        resolver_ptrs_[impl] = std::move(ptr);
590  
        resolver_ptrs_[impl] = std::move(ptr);
567  
    }
591  
    }
568  

592  

569  
    return impl;
593  
    return impl;
570  
}
594  
}
571  

595  

572  
inline void
596  
inline void
573  
posix_resolver_service::destroy_impl(posix_resolver& impl)
597  
posix_resolver_service::destroy_impl(posix_resolver& impl)
574  
{
598  
{
575  
    std::lock_guard<std::mutex> lock(mutex_);
599  
    std::lock_guard<std::mutex> lock(mutex_);
576  
    resolver_list_.remove(&impl);
600  
    resolver_list_.remove(&impl);
577  
    resolver_ptrs_.erase(&impl);
601  
    resolver_ptrs_.erase(&impl);
578  
}
602  
}
579  

603  

580  
inline void
604  
inline void
581  
posix_resolver_service::post(scheduler_op* op)
605  
posix_resolver_service::post(scheduler_op* op)
582  
{
606  
{
583  
    sched_->post(op);
607  
    sched_->post(op);
584  
}
608  
}
585  

609  

586  
inline void
610  
inline void
587  
posix_resolver_service::work_started() noexcept
611  
posix_resolver_service::work_started() noexcept
588  
{
612  
{
589  
    sched_->work_started();
613  
    sched_->work_started();
590  
}
614  
}
591  

615  

592  
inline void
616  
inline void
593  
posix_resolver_service::work_finished() noexcept
617  
posix_resolver_service::work_finished() noexcept
594  
{
618  
{
595  
    sched_->work_finished();
619  
    sched_->work_finished();
596  
}
620  
}
597  

621  

598  
// Free function to get/create the resolver service
622  
// Free function to get/create the resolver service
599  

623  

600  
inline posix_resolver_service&
624  
inline posix_resolver_service&
601  
get_resolver_service(capy::execution_context& ctx, scheduler& sched)
625  
get_resolver_service(capy::execution_context& ctx, scheduler& sched)
602  
{
626  
{
603  
    return ctx.make_service<posix_resolver_service>(sched);
627  
    return ctx.make_service<posix_resolver_service>(sched);
604  
}
628  
}
605  

629  

606  
} // namespace boost::corosio::detail
630  
} // namespace boost::corosio::detail
607  

631  

608  
#endif // BOOST_COROSIO_POSIX
632  
#endif // BOOST_COROSIO_POSIX
609  

633  

610  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
634  
#endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP