LCOV - code coverage report
Current view: top level - corosio/native/detail/posix - posix_resolver_service.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 95.4 % 281 268 13
Test Date: 2026-09-02 21:27:06 Functions: 100.0 % 27 27

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

Generated by: LCOV version 2.3