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: 81.7 % 289 236 53
Test Date: 2026-02-18 18:41:52 Functions: 93.9 % 33 31 2

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

Generated by: LCOV version 2.3