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: 99.3 % 285 283 2
Test Date: 2026-10-17 04:01:32 Functions: 100.0 % 31 31

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

Generated by: LCOV version 2.3