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: 98.9 % 278 275 3
Test Date: 2026-09-25 21:36:35 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        2241 :     posix_resolver_service(capy::execution_context& ctx, scheduler& sched)
      39            4482 :         : sched_(&sched)
      40            2241 :         , pool_(ctx)
      41                 :     {
      42            2241 :     }
      43                 : 
      44            4482 :     ~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              63 :     void destroy(io_object::implementation* p) override
      52                 :     {
      53              63 :         auto& impl = static_cast<posix_resolver&>(*p);
      54              63 :         impl.cancel();
      55              63 :         destroy_impl(impl);
      56              63 :     }
      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              52 :     thread_pool& pool()
      77                 :     {
      78              52 :         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              54 :     bool resolver_unavailable() const noexcept
      85                 :     {
      86              54 :         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              33 : posix_resolver_detail::flags_to_hints(resolve_flags flags)
     118                 : {
     119              33 :     int hints = 0;
     120                 : 
     121              33 :     if ((flags & resolve_flags::passive) != resolve_flags::none)
     122               1 :         hints |= AI_PASSIVE;
     123              33 :     if ((flags & resolve_flags::numeric_host) != resolve_flags::none)
     124              18 :         hints |= AI_NUMERICHOST;
     125              33 :     if ((flags & resolve_flags::numeric_service) != resolve_flags::none)
     126              12 :         hints |= AI_NUMERICSERV;
     127              33 :     if ((flags & resolve_flags::address_configured) != resolve_flags::none)
     128               1 :         hints |= AI_ADDRCONFIG;
     129              33 :     if ((flags & resolve_flags::v4_mapped) != resolve_flags::none)
     130               1 :         hints |= AI_V4MAPPED;
     131              33 :     if ((flags & resolve_flags::all_matching) != resolve_flags::none)
     132               1 :         hints |= AI_ALL;
     133                 : 
     134              33 :     return hints;
     135                 : }
     136                 : 
     137                 : inline int
     138              17 : posix_resolver_detail::flags_to_ni_flags(reverse_flags flags)
     139                 : {
     140              17 :     int ni_flags = 0;
     141                 : 
     142              17 :     if ((flags & reverse_flags::numeric_host) != reverse_flags::none)
     143               7 :         ni_flags |= NI_NUMERICHOST;
     144              17 :     if ((flags & reverse_flags::numeric_service) != reverse_flags::none)
     145               7 :         ni_flags |= NI_NUMERICSERV;
     146              17 :     if ((flags & reverse_flags::name_required) != reverse_flags::none)
     147               1 :         ni_flags |= NI_NAMEREQD;
     148              17 :     if ((flags & reverse_flags::datagram_service) != reverse_flags::none)
     149               1 :         ni_flags |= NI_DGRAM;
     150                 : 
     151              17 :     return ni_flags;
     152                 : }
     153                 : 
     154                 : inline std::vector<endpoint>
     155              21 : posix_resolver_detail::convert_results(struct addrinfo* ai)
     156                 : {
     157              21 :     std::vector<endpoint> endpoints;
     158              21 :     endpoints.reserve(4); // Most lookups return 1-4 addresses
     159                 : 
     160              42 :     for (auto* p = ai; p != nullptr; p = p->ai_next)
     161                 :     {
     162              21 :         if (p->ai_family == AF_INET)
     163                 :         {
     164              18 :             auto* addr = reinterpret_cast<sockaddr_in*>(p->ai_addr);
     165              18 :             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              21 :     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              64 : inline posix_resolver::posix_resolver(posix_resolver_service& svc) noexcept
     244              64 :     : svc_(svc)
     245                 : {
     246              64 : }
     247                 : 
     248                 : // posix_resolver::resolve_op implementation
     249                 : 
     250                 : inline void
     251              34 : posix_resolver::resolve_op::reset() noexcept
     252                 : {
     253              34 :     host.clear();
     254              34 :     service.clear();
     255              34 :     flags          = resolve_flags::none;
     256              34 :     stored_results = std::vector<endpoint>{};
     257              34 :     gai_error      = 0;
     258              34 :     cancelled.store(false, std::memory_order_relaxed);
     259              34 :     stop_cb.reset();
     260              34 :     ec_out = nullptr;
     261              34 :     out    = nullptr;
     262              34 : }
     263                 : 
     264                 : inline void
     265              32 : posix_resolver::resolve_op::operator()()
     266                 : {
     267              32 :     stop_cb.reset(); // Disconnect stop callback
     268                 : 
     269              32 :     bool const was_cancelled = cancelled.load(std::memory_order_acquire);
     270                 : 
     271              32 :     if (ec_out)
     272                 :     {
     273              32 :         if (was_cancelled)
     274 MIS           0 :             *ec_out = capy::error::canceled;
     275 HIT          32 :         else if (gai_error != 0)
     276              11 :             *ec_out = posix_resolver_detail::make_gai_error(gai_error);
     277                 :         else
     278              21 :             *ec_out = {}; // Clear on success
     279                 :     }
     280                 : 
     281              32 :     if (out && !was_cancelled && gai_error == 0)
     282              21 :         *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              32 :     auto prevent_destroy = std::move(impl_ptr);
     287              32 :     ex.on_work_finished();
     288              32 :     cont.h = h;
     289              32 :     dispatch_coro(ex, cont).resume();
     290              32 : }
     291                 : 
     292                 : inline void
     293               1 : posix_resolver::resolve_op::destroy()
     294                 : {
     295               1 :     stop_cb.reset();
     296               1 :     auto local_ex = ex;
     297                 :     // May destroy the implementation, and with it this op.
     298               1 :     impl_ptr.reset();
     299               1 :     local_ex.on_work_finished();
     300               1 : }
     301                 : 
     302                 : // posix_resolver::reverse_resolve_op implementation
     303                 : 
     304                 : inline void
     305              18 : posix_resolver::reverse_resolve_op::reset() noexcept
     306                 : {
     307              18 :     ep    = endpoint{};
     308              18 :     flags = reverse_flags::none;
     309              18 :     stored_host.clear();
     310              18 :     stored_service.clear();
     311              18 :     gai_error = 0;
     312              18 :     cancelled.store(false, std::memory_order_relaxed);
     313              18 :     stop_cb.reset();
     314              18 :     ec_out     = nullptr;
     315              18 :     result_out = nullptr;
     316              18 : }
     317                 : 
     318                 : inline void
     319              16 : posix_resolver::reverse_resolve_op::operator()()
     320                 : {
     321              16 :     stop_cb.reset(); // Disconnect stop callback
     322                 : 
     323              16 :     bool const was_cancelled = cancelled.load(std::memory_order_acquire);
     324                 : 
     325              16 :     if (ec_out)
     326                 :     {
     327              16 :         if (was_cancelled)
     328 MIS           0 :             *ec_out = capy::error::canceled;
     329 HIT          16 :         else if (gai_error != 0)
     330               6 :             *ec_out = posix_resolver_detail::make_gai_error(gai_error);
     331                 :         else
     332              10 :             *ec_out = {}; // Clear on success
     333                 :     }
     334                 : 
     335              16 :     if (result_out && !was_cancelled && gai_error == 0)
     336                 :     {
     337              10 :         *result_out =
     338              10 :             endpoint_name{std::move(stored_host), std::move(stored_service)};
     339                 :     }
     340                 : 
     341                 :     // Hold the keepalive across the dispatch: it may be the last
     342                 :     // reference to the implementation this op is embedded in.
     343              16 :     auto prevent_destroy = std::move(impl_ptr);
     344              16 :     ex.on_work_finished();
     345              16 :     cont.h = h;
     346              16 :     dispatch_coro(ex, cont).resume();
     347              16 : }
     348                 : 
     349                 : inline void
     350               1 : posix_resolver::reverse_resolve_op::destroy()
     351                 : {
     352               1 :     stop_cb.reset();
     353               1 :     auto local_ex = ex;
     354                 :     // May destroy the implementation, and with it this op.
     355               1 :     impl_ptr.reset();
     356               1 :     local_ex.on_work_finished();
     357               1 : }
     358                 : 
     359                 : // posix_resolver implementation
     360                 : 
     361                 : inline std::coroutine_handle<>
     362              35 : posix_resolver::resolve(
     363                 :     std::coroutine_handle<> h,
     364                 :     capy::executor_ref ex,
     365                 :     std::string_view host,
     366                 :     std::string_view service,
     367                 :     resolve_flags flags,
     368                 :     std::stop_token token,
     369                 :     std::error_code* ec,
     370                 :     std::vector<endpoint>* out)
     371                 : {
     372              35 :     if (svc_.resolver_unavailable())
     373                 :     {
     374               1 :         *ec        = std::make_error_code(std::errc::operation_not_supported);
     375               1 :         op_.cont.h = h;
     376               1 :         return dispatch_coro(ex, op_.cont);
     377                 :     }
     378                 : 
     379              34 :     auto& op = op_;
     380              34 :     op.reset();
     381              34 :     op.h       = h;
     382              34 :     op.ex      = ex;
     383              34 :     op.ec_out  = ec;
     384              34 :     op.out     = out;
     385              34 :     op.host    = host;
     386              34 :     op.service = service;
     387              34 :     op.flags   = flags;
     388              34 :     op.start(token);
     389                 : 
     390                 :     // Keep io_context alive while resolution is pending
     391              34 :     op.ex.on_work_started();
     392                 : 
     393                 :     // Prevent impl destruction while work is in flight
     394              34 :     resolve_pool_op_.resolver_ = this;
     395              34 :     resolve_pool_op_.ref_      = this->shared_from_this();
     396              34 :     resolve_pool_op_.func_     = &posix_resolver::do_resolve_work;
     397              34 :     if (auto pec = svc_.pool().post(&resolve_pool_op_))
     398                 :     {
     399                 :         // The pool is shutting down, or the system refused it a thread.
     400                 :         // Nothing of this resolve went cross-thread, so it answers here
     401                 :         // like the no-resolver exit above rather than through a
     402                 :         // completion the scheduler has to carry back.
     403               1 :         resolve_pool_op_.ref_.reset();
     404               1 :         op.stop_cb.reset();
     405               1 :         op.ex.on_work_finished();
     406               1 :         *ec       = pec;
     407               1 :         op.cont.h = h;
     408               1 :         return dispatch_coro(ex, op.cont);
     409                 :     }
     410              33 :     return std::noop_coroutine();
     411                 : }
     412                 : 
     413                 : inline std::coroutine_handle<>
     414              19 : posix_resolver::reverse_resolve(
     415                 :     std::coroutine_handle<> h,
     416                 :     capy::executor_ref ex,
     417                 :     endpoint const& ep,
     418                 :     reverse_flags flags,
     419                 :     std::stop_token token,
     420                 :     std::error_code* ec,
     421                 :     endpoint_name* result_out)
     422                 : {
     423              19 :     if (svc_.resolver_unavailable())
     424                 :     {
     425               1 :         *ec = std::make_error_code(std::errc::operation_not_supported);
     426               1 :         reverse_op_.cont.h = h;
     427               1 :         return dispatch_coro(ex, reverse_op_.cont);
     428                 :     }
     429                 : 
     430              18 :     auto& op = reverse_op_;
     431              18 :     op.reset();
     432              18 :     op.h          = h;
     433              18 :     op.ex         = ex;
     434              18 :     op.ec_out     = ec;
     435              18 :     op.result_out = result_out;
     436              18 :     op.ep         = ep;
     437              18 :     op.flags      = flags;
     438              18 :     op.start(token);
     439                 : 
     440                 :     // Keep io_context alive while resolution is pending
     441              18 :     op.ex.on_work_started();
     442                 : 
     443                 :     // Prevent impl destruction while work is in flight
     444              18 :     reverse_pool_op_.resolver_ = this;
     445              18 :     reverse_pool_op_.ref_      = this->shared_from_this();
     446              18 :     reverse_pool_op_.func_     = &posix_resolver::do_reverse_resolve_work;
     447              18 :     if (auto pec = svc_.pool().post(&reverse_pool_op_))
     448                 :     {
     449                 :         // The pool is shutting down, or the system refused it a thread.
     450                 :         // Nothing of this resolve went cross-thread, so it answers here
     451                 :         // like the no-resolver exit above rather than through a
     452                 :         // completion the scheduler has to carry back.
     453               1 :         reverse_pool_op_.ref_.reset();
     454               1 :         op.stop_cb.reset();
     455               1 :         op.ex.on_work_finished();
     456               1 :         *ec       = pec;
     457               1 :         op.cont.h = h;
     458               1 :         return dispatch_coro(ex, op.cont);
     459                 :     }
     460              17 :     return std::noop_coroutine();
     461                 : }
     462                 : 
     463                 : inline void
     464              71 : posix_resolver::cancel() noexcept
     465                 : {
     466              71 :     op_.request_cancel();
     467              71 :     reverse_op_.request_cancel();
     468              71 : }
     469                 : 
     470                 : inline void
     471              33 : posix_resolver::do_resolve_work(pool_work_item* w) noexcept
     472                 : {
     473              33 :     auto* pw   = static_cast<pool_op*>(w);
     474              33 :     auto* self = pw->resolver_;
     475                 : 
     476              33 :     struct addrinfo hints{};
     477              33 :     hints.ai_family   = AF_UNSPEC;
     478              33 :     hints.ai_socktype = SOCK_STREAM;
     479              33 :     hints.ai_flags    = posix_resolver_detail::flags_to_hints(self->op_.flags);
     480                 : 
     481              33 :     struct addrinfo* ai = nullptr;
     482              99 :     int result          = ::getaddrinfo(
     483              66 :         self->op_.host.empty() ? nullptr : self->op_.host.c_str(),
     484              61 :         self->op_.service.empty() ? nullptr : self->op_.service.c_str(), &hints,
     485                 :         &ai);
     486                 : 
     487              33 :     if (!self->op_.cancelled.load(std::memory_order_acquire))
     488                 :     {
     489              32 :         if (result == 0 && ai)
     490                 :         {
     491                 :             self->op_.stored_results =
     492              21 :                 posix_resolver_detail::convert_results(ai);
     493              21 :             self->op_.gai_error = 0;
     494                 :         }
     495                 :         else
     496                 :         {
     497              11 :             self->op_.gai_error = result;
     498                 :         }
     499                 :     }
     500                 : 
     501              33 :     if (ai)
     502              22 :         ::freeaddrinfo(ai);
     503                 : 
     504                 :     // Hand the keepalive to the op: the completion waits in the
     505                 :     // scheduler's queue, and the implementation embedding it must
     506                 :     // outlive that wait. Nothing may touch *self after the post.
     507              33 :     self->op_.impl_ptr = std::move(pw->ref_);
     508              33 :     self->svc_.post(&self->op_);
     509              33 : }
     510                 : 
     511                 : inline void
     512              17 : posix_resolver::do_reverse_resolve_work(pool_work_item* w) noexcept
     513                 : {
     514              17 :     auto* pw   = static_cast<pool_op*>(w);
     515              17 :     auto* self = pw->resolver_;
     516                 : 
     517              17 :     sockaddr_storage ss{};
     518                 :     socklen_t ss_len;
     519                 : 
     520              17 :     if (self->reverse_op_.ep.is_v4())
     521                 :     {
     522              15 :         auto sa = to_sockaddr_in(self->reverse_op_.ep);
     523              15 :         std::memcpy(&ss, &sa, sizeof(sa));
     524              15 :         ss_len = sizeof(sockaddr_in);
     525                 :     }
     526                 :     else
     527                 :     {
     528               2 :         auto sa = to_sockaddr_in6(self->reverse_op_.ep);
     529               2 :         std::memcpy(&ss, &sa, sizeof(sa));
     530               2 :         ss_len = sizeof(sockaddr_in6);
     531                 :     }
     532                 : 
     533                 :     char host[NI_MAXHOST];
     534                 :     char service[NI_MAXSERV];
     535                 : 
     536              17 :     int result = ::getnameinfo(
     537                 :         reinterpret_cast<sockaddr*>(&ss), ss_len, host, sizeof(host), service,
     538                 :         sizeof(service),
     539                 :         posix_resolver_detail::flags_to_ni_flags(self->reverse_op_.flags));
     540                 : 
     541              17 :     if (!self->reverse_op_.cancelled.load(std::memory_order_acquire))
     542                 :     {
     543              16 :         if (result == 0)
     544                 :         {
     545              10 :             self->reverse_op_.stored_host    = host;
     546              10 :             self->reverse_op_.stored_service = service;
     547              10 :             self->reverse_op_.gai_error      = 0;
     548                 :         }
     549                 :         else
     550                 :         {
     551               6 :             self->reverse_op_.gai_error = result;
     552                 :         }
     553                 :     }
     554                 : 
     555                 :     // Hand the keepalive to the op: the completion waits in the
     556                 :     // scheduler's queue, and the implementation embedding it must
     557                 :     // outlive that wait. Nothing may touch *self after the post.
     558              17 :     self->reverse_op_.impl_ptr = std::move(pw->ref_);
     559              17 :     self->svc_.post(&self->reverse_op_);
     560              17 : }
     561                 : 
     562                 : // posix_resolver_service implementation
     563                 : 
     564                 : inline void
     565            2241 : posix_resolver_service::shutdown()
     566                 : {
     567            2241 :     std::lock_guard<std::mutex> lock(mutex_);
     568                 : 
     569                 :     // Cancel all resolvers (sets cancelled flag checked by pool threads)
     570            2242 :     for (auto* impl = resolver_list_.pop_front(); impl != nullptr;
     571               1 :          impl       = resolver_list_.pop_front())
     572                 :     {
     573               1 :         impl->cancel();
     574                 :     }
     575                 : 
     576                 :     // Clear the map which releases shared_ptrs.
     577                 :     // The thread pool service shuts down separately via
     578                 :     // execution_context service ordering.
     579            2241 :     resolver_ptrs_.clear();
     580            2241 : }
     581                 : 
     582                 : inline io_object::implementation*
     583              64 : posix_resolver_service::construct()
     584                 : {
     585              64 :     auto ptr   = std::make_shared<posix_resolver>(*this);
     586              64 :     auto* impl = ptr.get();
     587                 : 
     588                 :     {
     589              64 :         std::lock_guard<std::mutex> lock(mutex_);
     590              64 :         resolver_list_.push_back(impl);
     591              64 :         resolver_ptrs_[impl] = std::move(ptr);
     592              64 :     }
     593                 : 
     594              64 :     return impl;
     595              64 : }
     596                 : 
     597                 : inline void
     598              63 : posix_resolver_service::destroy_impl(posix_resolver& impl)
     599                 : {
     600              63 :     std::lock_guard<std::mutex> lock(mutex_);
     601              63 :     resolver_list_.remove(&impl);
     602              63 :     resolver_ptrs_.erase(&impl);
     603              63 : }
     604                 : 
     605                 : inline void
     606              50 : posix_resolver_service::post(scheduler_op* op)
     607                 : {
     608              50 :     sched_->post(op);
     609              50 : }
     610                 : 
     611                 : // Free function to get/create the resolver service
     612                 : 
     613                 : inline posix_resolver_service&
     614            2241 : get_resolver_service(capy::execution_context& ctx, scheduler& sched)
     615                 : {
     616            2241 :     return ctx.make_service<posix_resolver_service>(sched);
     617                 : }
     618                 : 
     619                 : } // namespace boost::corosio::detail
     620                 : 
     621                 : #endif // BOOST_COROSIO_POSIX
     622                 : 
     623                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_SERVICE_HPP
        

Generated by: LCOV version 2.3