LCOV - code coverage report
Current view: top level - corosio/native/detail/posix - posix_resolver.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 2 2
Test Date: 2026-09-02 21:27:06 Functions: 100.0 % 2 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_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_HPP
      12                 : 
      13                 : #include <boost/corosio/detail/platform.hpp>
      14                 : 
      15                 : #if BOOST_COROSIO_POSIX
      16                 : 
      17                 : #include <boost/corosio/detail/config.hpp>
      18                 : #include <boost/corosio/resolver.hpp>
      19                 : #include <boost/capy/ex/execution_context.hpp>
      20                 : 
      21                 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
      22                 : #include <boost/corosio/detail/intrusive.hpp>
      23                 : #include <boost/corosio/detail/dispatch_coro.hpp>
      24                 : #include <boost/corosio/detail/scheduler_op.hpp>
      25                 : #include <boost/corosio/detail/thread_pool.hpp>
      26                 : #include <boost/corosio/native/detail/coro_op.hpp>
      27                 : 
      28                 : #include <boost/corosio/detail/scheduler.hpp>
      29                 : #include <boost/corosio/resolver_results.hpp>
      30                 : #include <boost/capy/ex/executor_ref.hpp>
      31                 : #include <coroutine>
      32                 : #include <boost/capy/error.hpp>
      33                 : 
      34                 : #include <netdb.h>
      35                 : #include <netinet/in.h>
      36                 : #include <sys/socket.h>
      37                 : 
      38                 : #include <atomic>
      39                 : #include <memory>
      40                 : #include <optional>
      41                 : #include <stop_token>
      42                 : #include <string>
      43                 : 
      44                 : /*
      45                 :     POSIX Resolver Service
      46                 :     ======================
      47                 : 
      48                 :     POSIX getaddrinfo() is a blocking call that cannot be monitored with
      49                 :     epoll/kqueue/io_uring. Blocking calls are dispatched to a shared
      50                 :     resolver_thread_pool service which reuses threads across operations.
      51                 : 
      52                 :     Cancellation
      53                 :     ------------
      54                 :     getaddrinfo() cannot be interrupted mid-call. We use an atomic flag to
      55                 :     indicate cancellation was requested. The worker thread checks this flag
      56                 :     after getaddrinfo() returns and reports the appropriate error.
      57                 : 
      58                 :     Class Hierarchy
      59                 :     ---------------
      60                 :     - posix_resolver_service (execution_context service, one per context)
      61                 :         - Owns all posix_resolver instances via shared_ptr
      62                 :         - Stores scheduler* for posting completions
      63                 :     - posix_resolver (one per resolver object)
      64                 :         - Contains embedded resolve_op and reverse_resolve_op for reuse
      65                 :         - Uses shared_from_this to prevent premature destruction
      66                 :     - resolve_op (forward resolution state)
      67                 :         - Uses getaddrinfo() to resolve host/service to endpoints
      68                 :     - reverse_resolve_op (reverse resolution state)
      69                 :         - Uses getnameinfo() to resolve endpoint to host/service
      70                 : 
      71                 :     Completion Flow
      72                 :     ---------------
      73                 :     Forward resolution:
      74                 :     1. resolve() sets up op_, posts work to the thread pool
      75                 :     2. Pool thread runs getaddrinfo() (blocking)
      76                 :     3. Pool thread stores results in op_.stored_results
      77                 :     4. Pool thread calls svc_.post(&op_) to queue completion
      78                 :     5. Scheduler invokes op_() which resumes the coroutine
      79                 : 
      80                 :     Reverse resolution follows the same pattern using getnameinfo().
      81                 : 
      82                 :     Single-Inflight Constraint
      83                 :     --------------------------
      84                 :     Each resolver has ONE embedded op_ for forward and ONE reverse_op_ for
      85                 :     reverse resolution. Concurrent operations of the same type on the same
      86                 :     resolver would corrupt state. Users must serialize operations per-resolver.
      87                 : 
      88                 :     Shutdown
      89                 :     --------
      90                 :     The resolver service cancels all resolvers and clears the impl map.
      91                 :     The thread pool service shuts down separately via execution_context
      92                 :     service ordering, joining all worker threads.
      93                 : */
      94                 : 
      95                 : namespace boost::corosio::detail {
      96                 : 
      97                 : struct scheduler;
      98                 : 
      99                 : namespace posix_resolver_detail {
     100                 : 
     101                 : // Convert resolve_flags to addrinfo ai_flags
     102                 : int flags_to_hints(resolve_flags flags);
     103                 : 
     104                 : // Convert reverse_flags to getnameinfo NI_* flags
     105                 : int flags_to_ni_flags(reverse_flags flags);
     106                 : 
     107                 : // Convert addrinfo results to resolver_results
     108                 : resolver_results convert_results(
     109                 :     struct addrinfo* ai, std::string_view host, std::string_view service);
     110                 : 
     111                 : // Convert getaddrinfo error codes to std::error_code
     112                 : std::error_code make_gai_error(int gai_err);
     113                 : 
     114                 : } // namespace posix_resolver_detail
     115                 : 
     116                 : class posix_resolver_service;
     117                 : 
     118                 : /** Resolver implementation for POSIX backends.
     119                 : 
     120                 :     Each resolver instance contains a single embedded operation object (op_)
     121                 :     that is reused for each resolve() call. This design avoids per-operation
     122                 :     heap allocation but imposes a critical constraint:
     123                 : 
     124                 :     @par Single-Inflight Contract
     125                 : 
     126                 :     Only ONE resolve operation may be in progress at a time per resolver
     127                 :     instance. Calling resolve() while a previous resolve() is still pending
     128                 :     results in undefined behavior:
     129                 : 
     130                 :     - The new call overwrites op_ fields (host, service, coroutine handle)
     131                 :     - The worker thread from the first call reads corrupted state
     132                 :     - The wrong coroutine may be resumed, or resumed multiple times
     133                 :     - Data races occur on non-atomic op_ members
     134                 : 
     135                 :     @par Safe Usage Patterns
     136                 : 
     137                 :     @code
     138                 :     // CORRECT: Sequential resolves
     139                 :     auto [ec1, r1] = co_await resolver.resolve("host1", "80");
     140                 :     auto [ec2, r2] = co_await resolver.resolve("host2", "80");
     141                 : 
     142                 :     // CORRECT: Parallel resolves with separate resolver instances
     143                 :     resolver r1(ctx), r2(ctx);
     144                 :     auto [ec1, res1] = co_await r1.resolve("host1", "80");  // in one coroutine
     145                 :     auto [ec2, res2] = co_await r2.resolve("host2", "80");  // in another
     146                 : 
     147                 :     // WRONG: Concurrent resolves on same resolver
     148                 :     // These may run concurrently if launched in parallel - UNDEFINED BEHAVIOR
     149                 :     auto f1 = resolver.resolve("host1", "80");
     150                 :     auto f2 = resolver.resolve("host2", "80");  // BAD: overlaps with f1
     151                 :     @endcode
     152                 : 
     153                 :     @par Thread Safety
     154                 :     Distinct objects: Safe.
     155                 :     Shared objects: Unsafe. See single-inflight contract above.
     156                 : */
     157                 : class posix_resolver final
     158                 :     : public resolver::implementation
     159                 :     , public std::enable_shared_from_this<posix_resolver>
     160                 :     , public intrusive_list<posix_resolver>::node
     161                 : {
     162                 :     friend class posix_resolver_service;
     163                 : 
     164                 : public:
     165                 :     // resolve_op - operation state for a single DNS resolution
     166                 : 
     167                 :     struct resolve_op : coro_op
     168                 :     {
     169                 :         /// Where the endpoints are handed back.
     170                 :         resolver_results* out = nullptr;
     171                 : 
     172                 :         // Input parameters (owned copies for thread safety)
     173                 :         std::string host;
     174                 :         std::string service;
     175                 :         resolve_flags flags = resolve_flags::none;
     176                 : 
     177                 :         // Result storage (populated by worker thread)
     178                 :         resolver_results stored_results;
     179                 :         int gai_error = 0;
     180                 : 
     181 HIT          57 :         resolve_op() = default;
     182                 : 
     183                 :         void reset() noexcept;
     184                 :         void operator()() override;
     185                 :         void destroy() override;
     186                 :     };
     187                 : 
     188                 :     // reverse_resolve_op - operation state for reverse DNS resolution
     189                 : 
     190                 :     struct reverse_resolve_op : coro_op
     191                 :     {
     192                 :         /// Where the name is handed back.
     193                 :         reverse_resolver_result* result_out = nullptr;
     194                 : 
     195                 :         // Input parameters
     196                 :         endpoint ep;
     197                 :         reverse_flags flags = reverse_flags::none;
     198                 : 
     199                 :         // Result storage (populated by worker thread)
     200                 :         std::string stored_host;
     201                 :         std::string stored_service;
     202                 :         int gai_error = 0;
     203                 : 
     204              57 :         reverse_resolve_op() = default;
     205                 : 
     206                 :         void reset() noexcept;
     207                 :         void operator()() override;
     208                 :         void destroy() override;
     209                 :     };
     210                 : 
     211                 :     /// Embedded pool work item for thread pool dispatch.
     212                 :     struct pool_op : pool_work_item
     213                 :     {
     214                 :         /// Resolver that owns this work item.
     215                 :         posix_resolver* resolver_ = nullptr;
     216                 : 
     217                 :         /// Prevent impl destruction while work is in flight.
     218                 :         std::shared_ptr<posix_resolver> ref_;
     219                 :     };
     220                 : 
     221                 :     explicit posix_resolver(posix_resolver_service& svc) noexcept;
     222                 : 
     223                 :     std::coroutine_handle<> resolve(
     224                 :         std::coroutine_handle<>,
     225                 :         capy::executor_ref,
     226                 :         std::string_view host,
     227                 :         std::string_view service,
     228                 :         resolve_flags flags,
     229                 :         std::stop_token,
     230                 :         std::error_code*,
     231                 :         resolver_results*) override;
     232                 : 
     233                 :     std::coroutine_handle<> reverse_resolve(
     234                 :         std::coroutine_handle<>,
     235                 :         capy::executor_ref,
     236                 :         endpoint const& ep,
     237                 :         reverse_flags flags,
     238                 :         std::stop_token,
     239                 :         std::error_code*,
     240                 :         reverse_resolver_result*) override;
     241                 : 
     242                 :     void cancel() noexcept override;
     243                 : 
     244                 :     resolve_op op_;
     245                 :     reverse_resolve_op reverse_op_;
     246                 : 
     247                 :     /// Pool work item for forward resolution.
     248                 :     pool_op resolve_pool_op_;
     249                 : 
     250                 :     /// Pool work item for reverse resolution.
     251                 :     pool_op reverse_pool_op_;
     252                 : 
     253                 :     /// Execute blocking `getaddrinfo()` on a pool thread.
     254                 :     static void do_resolve_work(pool_work_item*) noexcept;
     255                 : 
     256                 :     /// Execute blocking `getnameinfo()` on a pool thread.
     257                 :     static void do_reverse_resolve_work(pool_work_item*) noexcept;
     258                 : 
     259                 : private:
     260                 :     posix_resolver_service& svc_;
     261                 : };
     262                 : 
     263                 : } // namespace boost::corosio::detail
     264                 : 
     265                 : #endif // BOOST_COROSIO_POSIX
     266                 : 
     267                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_POSIX_POSIX_RESOLVER_HPP
        

Generated by: LCOV version 2.3