LCOV - code coverage report
Current view: top level - corosio/native - native_tcp_acceptor.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 94.0 % 67 63 4
Test Date: 2026-08-21 20:48:07 Functions: 100.0 % 36 36

           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_NATIVE_TCP_ACCEPTOR_HPP
      11                 : #define BOOST_COROSIO_NATIVE_NATIVE_TCP_ACCEPTOR_HPP
      12                 : 
      13                 : #include <boost/corosio/tcp_acceptor.hpp>
      14                 : #include <boost/corosio/backend.hpp>
      15                 : 
      16                 : #ifndef BOOST_COROSIO_MRDOCS
      17                 : #if BOOST_COROSIO_HAS_EPOLL
      18                 : #include <boost/corosio/native/detail/epoll/epoll_types.hpp>
      19                 : #endif
      20                 : 
      21                 : #if BOOST_COROSIO_HAS_SELECT
      22                 : #include <boost/corosio/native/detail/select/select_types.hpp>
      23                 : #endif
      24                 : 
      25                 : #if BOOST_COROSIO_HAS_KQUEUE
      26                 : #include <boost/corosio/native/detail/kqueue/kqueue_types.hpp>
      27                 : #endif
      28                 : 
      29                 : #if BOOST_COROSIO_HAS_IOCP
      30                 : #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
      31                 : #endif
      32                 : 
      33                 : #if BOOST_COROSIO_HAS_IO_URING
      34                 : #include <boost/corosio/native/detail/io_uring/io_uring_types.hpp>
      35                 : #endif
      36                 : #endif // !BOOST_COROSIO_MRDOCS
      37                 : 
      38                 : namespace boost::corosio {
      39                 : 
      40                 : /** An asynchronous TCP acceptor with devirtualized accept operations.
      41                 : 
      42                 :     This class template inherits from @ref tcp_acceptor and shadows
      43                 :     the `accept` operation with a version that calls the backend
      44                 :     implementation directly, allowing the compiler to inline through
      45                 :     the entire call chain.
      46                 : 
      47                 :     Non-async operations (`listen`, `close`, `cancel`) remain
      48                 :     unchanged and dispatch through the compiled library.
      49                 : 
      50                 :     A `native_tcp_acceptor` IS-A `tcp_acceptor` and can be passed
      51                 :     to any function expecting `tcp_acceptor&`.
      52                 : 
      53                 :     @tparam Backend A backend tag value (e.g., `epoll`).
      54                 : 
      55                 :     @par Thread Safety
      56                 :     Same as @ref tcp_acceptor.
      57                 : 
      58                 :     @see tcp_acceptor, epoll_t, iocp_t
      59                 : */
      60                 : template<auto Backend>
      61                 : class native_tcp_acceptor : public tcp_acceptor
      62                 : {
      63                 :     using backend_type = decltype(Backend);
      64                 :     using impl_type    = typename backend_type::tcp_acceptor_type;
      65                 :     using service_type = typename backend_type::tcp_acceptor_service_type;
      66                 : 
      67 HIT          15 :     impl_type& get_impl() noexcept
      68                 :     {
      69              15 :         return *static_cast<impl_type*>(h_.get());
      70                 :     }
      71                 : 
      72                 :     struct native_wait_awaitable
      73                 :     {
      74                 :         native_tcp_acceptor& acc_;
      75                 :         wait_type w_;
      76                 :         std::stop_token token_;
      77                 :         mutable std::error_code ec_;
      78                 : 
      79               4 :         native_wait_awaitable(native_tcp_acceptor& acc, wait_type w) noexcept
      80               4 :             : acc_(acc)
      81               4 :             , w_(w)
      82                 :         {
      83               4 :         }
      84                 : 
      85               4 :         bool await_ready() const noexcept
      86                 :         {
      87                 :             // A pre-set ec_ means the initiator failed before
      88                 :             // dispatch (e.g. a closed object).
      89               4 :             return static_cast<bool>(ec_) || token_.stop_requested();
      90                 :         }
      91                 : 
      92               4 :         [[nodiscard]] capy::io_result<> await_resume() const noexcept
      93                 :         {
      94               4 :             if (token_.stop_requested())
      95 MIS           0 :                 return {make_error_code(std::errc::operation_canceled)};
      96 HIT           4 :             return {ec_};
      97                 :         }
      98                 : 
      99               4 :         auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
     100                 :             -> std::coroutine_handle<>
     101                 :         {
     102               4 :             token_ = env->stop_token;
     103              12 :             return acc_.get_impl().wait(
     104              12 :                 h, env->executor, w_, token_, &ec_);
     105                 :         }
     106                 :     };
     107                 : 
     108                 :     struct native_accept_awaitable
     109                 :     {
     110                 :         native_tcp_acceptor& acc_;
     111                 :         tcp_socket& peer_;
     112                 :         std::stop_token token_;
     113                 :         mutable std::error_code ec_;
     114                 :         mutable io_object::implementation* peer_impl_ = nullptr;
     115                 : 
     116              11 :         native_accept_awaitable(
     117                 :             native_tcp_acceptor& acc, tcp_socket& peer) noexcept
     118              11 :             : acc_(acc)
     119              11 :             , peer_(peer)
     120                 :         {
     121              11 :         }
     122                 : 
     123              11 :         bool await_ready() const noexcept
     124                 :         {
     125                 :             // A pre-set ec_ means the initiator failed before
     126                 :             // dispatch (e.g. a closed object).
     127              11 :             return static_cast<bool>(ec_) || token_.stop_requested();
     128                 :         }
     129                 : 
     130              11 :         [[nodiscard]] capy::io_result<> await_resume() const noexcept
     131                 :         {
     132              11 :             if (token_.stop_requested())
     133 MIS           0 :                 return {make_error_code(std::errc::operation_canceled)};
     134 HIT          11 :             if (!ec_)
     135               9 :                 acc_.reset_peer_impl(peer_, peer_impl_);
     136              11 :             return {ec_};
     137                 :         }
     138                 : 
     139               9 :         auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
     140                 :             -> std::coroutine_handle<>
     141                 :         {
     142               9 :             token_ = env->stop_token;
     143              27 :             return acc_.get_impl().accept(
     144              27 :                 h, env->executor, token_, &ec_, &peer_impl_);
     145                 :         }
     146                 :     };
     147                 : 
     148                 :     struct native_accept_value_awaitable
     149                 :     {
     150                 :         native_tcp_acceptor& acc_;
     151                 :         tcp_socket peer_;
     152                 :         std::stop_token token_;
     153                 :         mutable std::error_code ec_;
     154                 :         mutable io_object::implementation* peer_impl_ = nullptr;
     155                 : 
     156               4 :         explicit native_accept_value_awaitable(native_tcp_acceptor& acc)
     157               4 :             : acc_(acc)
     158               4 :             , peer_(acc.context())
     159                 :         {
     160               4 :         }
     161                 : 
     162               4 :         bool await_ready() const noexcept
     163                 :         {
     164                 :             // A pre-set ec_ means the initiator failed before
     165                 :             // dispatch (e.g. a closed object).
     166               4 :             return static_cast<bool>(ec_) || token_.stop_requested();
     167                 :         }
     168                 : 
     169               4 :         [[nodiscard]] capy::io_result<tcp_socket> await_resume() noexcept
     170                 :         {
     171               4 :             if (token_.stop_requested())
     172 MIS           0 :                 return {make_error_code(std::errc::operation_canceled),
     173               0 :                         std::move(peer_)};
     174 HIT           4 :             if (!ec_ && peer_impl_)
     175               2 :                 acc_.reset_peer_impl(peer_, peer_impl_);
     176               4 :             return {ec_, std::move(peer_)};
     177                 :         }
     178                 : 
     179               2 :         auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
     180                 :             -> std::coroutine_handle<>
     181                 :         {
     182               2 :             token_ = env->stop_token;
     183               6 :             return acc_.get_impl().accept(
     184               6 :                 h, env->executor, token_, &ec_, &peer_impl_);
     185                 :         }
     186                 :     };
     187                 : 
     188                 : public:
     189                 :     /** Construct a native acceptor from an execution context.
     190                 : 
     191                 :         @param ctx The execution context that will own this acceptor.
     192                 :     */
     193              27 :     explicit native_tcp_acceptor(capy::execution_context& ctx)
     194              27 :         : tcp_acceptor(create_handle<service_type>(ctx))
     195                 :     {
     196              27 :     }
     197                 : 
     198                 :     /** Construct a native acceptor from an executor.
     199                 : 
     200                 :         @param ex The executor whose context will own the acceptor.
     201                 :     */
     202                 :     template<class Ex>
     203                 :         requires(!std::same_as<std::remove_cvref_t<Ex>, native_tcp_acceptor>) &&
     204                 :         capy::Executor<Ex>
     205                 :     explicit native_tcp_acceptor(Ex const& ex)
     206                 :         : native_tcp_acceptor(ex.context())
     207                 :     {
     208                 :     }
     209                 : 
     210                 :     /** Move construct.
     211                 : 
     212                 :         @param other The acceptor to move from.
     213                 : 
     214                 :         @pre No awaitables returned by @p other's methods exist.
     215                 :         @pre The execution context associated with @p other must
     216                 :             outlive this acceptor.
     217                 :     */
     218               4 :     native_tcp_acceptor(native_tcp_acceptor&&) noexcept = default;
     219                 : 
     220                 :     /** Move assign.
     221                 : 
     222                 :         @param other The acceptor to move from.
     223                 : 
     224                 :         @pre No awaitables returned by either `*this` or @p other's
     225                 :             methods exist.
     226                 :         @pre The execution context associated with @p other must
     227                 :             outlive this acceptor.
     228                 :     */
     229                 :     native_tcp_acceptor& operator=(native_tcp_acceptor&&) noexcept = default;
     230                 : 
     231                 :     native_tcp_acceptor(native_tcp_acceptor const&)            = delete;
     232                 :     native_tcp_acceptor& operator=(native_tcp_acceptor const&) = delete;
     233                 : 
     234                 :     /** Asynchronously accept an incoming connection.
     235                 : 
     236                 :         Calls the backend implementation directly, bypassing virtual
     237                 :         dispatch. Otherwise identical to @ref tcp_acceptor::accept.
     238                 : 
     239                 :         @param peer The socket to receive the accepted connection.
     240                 : 
     241                 :         @return An awaitable yielding `io_result<>`.
     242                 : 
     243                 :         A closed acceptor reports `errc::bad_file_descriptor`.
     244                 : 
     245                 :         Both this acceptor and @p peer must outlive the returned
     246                 :         awaitable.
     247                 :     */
     248              11 :     [[nodiscard]] auto accept(tcp_socket& peer)
     249                 :     {
     250              11 :         native_accept_awaitable aw(*this, peer);
     251              11 :         if (!is_open())
     252               2 :             aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
     253              11 :         return aw;
     254                 :     }
     255                 : 
     256                 :     /** Asynchronously accept an incoming connection, returning the peer.
     257                 : 
     258                 :         Calls the backend implementation directly, bypassing virtual
     259                 :         dispatch. Otherwise identical to @ref tcp_acceptor::accept().
     260                 : 
     261                 :         @return An awaitable yielding `io_result<tcp_socket>`.
     262                 : 
     263                 :         A closed acceptor reports `errc::bad_file_descriptor`.
     264                 : 
     265                 :         @throws std::logic_error If the acceptor has been moved from.
     266                 : 
     267                 :         This acceptor must outlive the returned awaitable.
     268                 :     */
     269               6 :     [[nodiscard]] auto accept()
     270                 :     {
     271                 :         // The awaitable builds the peer from context(), which a
     272                 :         // moved-from acceptor no longer has.
     273               6 :         if (!h_)
     274               2 :             detail::throw_logic_error("accept: acceptor moved-from");
     275               4 :         native_accept_value_awaitable aw(*this);
     276               4 :         if (!is_open())
     277               2 :             aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
     278               4 :         return aw;
     279                 :     }
     280                 : 
     281                 :     /** Asynchronously wait for the acceptor to be ready.
     282                 : 
     283                 :         Calls the backend implementation directly, bypassing virtual
     284                 :         dispatch. Otherwise identical to @ref tcp_acceptor::wait.
     285                 : 
     286                 :         @param w The wait direction (typically `wait_type::read`).
     287                 : 
     288                 :         @return An awaitable yielding `io_result<>`.
     289                 :     */
     290               4 :     [[nodiscard]] auto wait(wait_type w)
     291                 :     {
     292               4 :         return native_wait_awaitable(*this, w);
     293                 :     }
     294                 : };
     295                 : 
     296                 : } // namespace boost::corosio
     297                 : 
     298                 : #endif
        

Generated by: LCOV version 2.3