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

Generated by: LCOV version 2.3