LCOV - code coverage report
Current view: top level - corosio/native/detail/reactor - reactor_acceptor.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 66.0 % 106 70 36
Test Date: 2026-04-13 22:45:57 Functions: 72.4 % 58 42 16

           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_REACTOR_REACTOR_ACCEPTOR_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_ACCEPTOR_HPP
      12                 : 
      13                 : #include <boost/corosio/tcp_acceptor.hpp>
      14                 : #include <boost/corosio/detail/intrusive.hpp>
      15                 : #include <boost/corosio/native/detail/reactor/reactor_op_base.hpp>
      16                 : #include <boost/corosio/native/detail/make_err.hpp>
      17                 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
      18                 : 
      19                 : #include <memory>
      20                 : #include <mutex>
      21                 : #include <utility>
      22                 : 
      23                 : #include <errno.h>
      24                 : #include <netinet/in.h>
      25                 : #include <sys/socket.h>
      26                 : #include <unistd.h>
      27                 : 
      28                 : namespace boost::corosio::detail {
      29                 : 
      30                 : /** CRTP base for reactor-backed acceptor implementations.
      31                 : 
      32                 :     Provides shared data members, trivial virtual overrides, and
      33                 :     non-virtual helper methods for cancellation and close. Concrete
      34                 :     backends inherit and add `cancel()`, `close_socket()`, and
      35                 :     `accept()` overrides that delegate to the `do_*` helpers.
      36                 : 
      37                 :     @tparam Derived   The concrete acceptor type (CRTP).
      38                 :     @tparam Service   The backend's acceptor service type.
      39                 :     @tparam Op        The backend's base op type.
      40                 :     @tparam AcceptOp  The backend's accept op type.
      41                 :     @tparam DescState The backend's descriptor_state type.
      42                 :     @tparam ImplBase  The public vtable base
      43                 :                       (tcp_acceptor::implementation or
      44                 :                        local_stream_acceptor::implementation).
      45                 :     @tparam Endpoint  The endpoint type (endpoint or local_endpoint).
      46                 : */
      47                 : template<
      48                 :     class Derived,
      49                 :     class Service,
      50                 :     class Op,
      51                 :     class AcceptOp,
      52                 :     class DescState,
      53                 :     class ImplBase = tcp_acceptor::implementation,
      54                 :     class Endpoint = endpoint>
      55                 : class reactor_acceptor
      56                 :     : public ImplBase
      57                 :     , public std::enable_shared_from_this<Derived>
      58                 :     , public intrusive_list<Derived>::node
      59                 : {
      60                 :     friend Derived;
      61                 : 
      62 HIT         161 :     explicit reactor_acceptor(Service& svc) noexcept : svc_(svc) {}
      63                 : 
      64                 : protected:
      65                 :     Service& svc_;
      66                 :     int fd_ = -1;
      67                 :     Endpoint local_endpoint_;
      68                 : 
      69                 : public:
      70                 :     /// Pending accept operation slot.
      71                 :     AcceptOp acc_;
      72                 : 
      73                 :     /// Per-descriptor state for persistent reactor registration.
      74                 :     DescState desc_state_;
      75                 : 
      76             161 :     ~reactor_acceptor() override = default;
      77                 : 
      78                 :     /// Return the underlying file descriptor.
      79                 :     int native_handle() const noexcept
      80                 :     {
      81                 :         return fd_;
      82                 :     }
      83                 : 
      84                 :     /// Return the cached local endpoint.
      85            8244 :     Endpoint local_endpoint() const noexcept override
      86                 :     {
      87            8244 :         return local_endpoint_;
      88                 :     }
      89                 : 
      90                 :     /// Return true if the acceptor has an open file descriptor.
      91            9178 :     bool is_open() const noexcept override
      92                 :     {
      93            9178 :         return fd_ >= 0;
      94                 :     }
      95                 : 
      96                 :     /// Set a socket option.
      97             139 :     std::error_code set_option(
      98                 :         int level,
      99                 :         int optname,
     100                 :         void const* data,
     101                 :         std::size_t size) noexcept override
     102                 :     {
     103             139 :         if (::setsockopt(
     104             139 :                 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
     105 MIS           0 :             return make_err(errno);
     106 HIT         139 :         return {};
     107                 :     }
     108                 : 
     109                 :     /// Get a socket option.
     110                 :     std::error_code
     111 MIS           0 :     get_option(int level, int optname, void* data, std::size_t* size)
     112                 :         const noexcept override
     113                 :     {
     114               0 :         socklen_t len = static_cast<socklen_t>(*size);
     115               0 :         if (::getsockopt(fd_, level, optname, data, &len) != 0)
     116               0 :             return make_err(errno);
     117               0 :         *size = static_cast<std::size_t>(len);
     118               0 :         return {};
     119                 :     }
     120                 : 
     121                 :     /// Cache the local endpoint.
     122 HIT         146 :     void set_local_endpoint(Endpoint ep) noexcept
     123                 :     {
     124             146 :         local_endpoint_ = std::move(ep);
     125             146 :     }
     126                 : 
     127                 :     /// Return a reference to the owning service.
     128            8114 :     Service& service() noexcept
     129                 :     {
     130            8114 :         return svc_;
     131                 :     }
     132                 : 
     133                 :     /** Cancel a single pending operation.
     134                 : 
     135                 :         Claims the operation from the read_op descriptor slot
     136                 :         under the mutex and posts it to the scheduler as cancelled.
     137                 : 
     138                 :         @param op The operation to cancel.
     139                 :     */
     140                 :     void cancel_single_op(Op& op) noexcept;
     141                 : 
     142                 :     /** Cancel the pending accept operation.
     143                 : 
     144                 :         Invoked by the derived class's cancel() override.
     145                 :     */
     146                 :     void do_cancel() noexcept;
     147                 : 
     148                 :     /** Close the acceptor and cancel pending operations.
     149                 : 
     150                 :         Invoked by the derived class's close_socket(). The
     151                 :         derived class may add backend-specific cleanup after
     152                 :         calling this method.
     153                 :     */
     154                 :     void do_close_socket() noexcept;
     155                 : 
     156                 :     /** Release the acceptor without closing the fd. */
     157                 :     native_handle_type do_release_socket() noexcept;
     158                 : 
     159                 :     /** Bind the acceptor socket to an endpoint.
     160                 : 
     161                 :         Caches the resolved local endpoint (including ephemeral
     162                 :         port) after a successful bind.
     163                 : 
     164                 :         @param ep The endpoint to bind to.
     165                 :         @return The error code from bind(), or success.
     166                 :     */
     167                 :     std::error_code do_bind(Endpoint const& ep);
     168                 : 
     169                 :     /** Start listening on the acceptor socket.
     170                 : 
     171                 :         Registers the file descriptor with the reactor after
     172                 :         a successful listen() call.
     173                 : 
     174                 :         @param backlog The listen backlog.
     175                 :         @return The error code from listen(), or success.
     176                 :     */
     177                 :     std::error_code do_listen(int backlog);
     178                 : };
     179                 : 
     180                 : template<
     181                 :     class Derived,
     182                 :     class Service,
     183                 :     class Op,
     184                 :     class AcceptOp,
     185                 :     class DescState,
     186                 :     class ImplBase,
     187                 :     class Endpoint>
     188                 : void
     189              10 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState, ImplBase, Endpoint>::
     190                 :     cancel_single_op(Op& op) noexcept
     191                 : {
     192              10 :     auto self = this->weak_from_this().lock();
     193              10 :     if (!self)
     194 MIS           0 :         return;
     195                 : 
     196 HIT          10 :     op.request_cancel();
     197                 : 
     198              10 :     reactor_op_base* claimed = nullptr;
     199                 :     {
     200              10 :         std::lock_guard lock(desc_state_.mutex);
     201              10 :         if (desc_state_.read_op == &op)
     202               8 :             claimed = std::exchange(desc_state_.read_op, nullptr);
     203              10 :     }
     204              10 :     if (claimed)
     205                 :     {
     206               8 :         op.impl_ptr = self;
     207               8 :         svc_.post(&op);
     208               8 :         svc_.work_finished();
     209                 :     }
     210              10 : }
     211                 : 
     212                 : template<
     213                 :     class Derived,
     214                 :     class Service,
     215                 :     class Op,
     216                 :     class AcceptOp,
     217                 :     class DescState,
     218                 :     class ImplBase,
     219                 :     class Endpoint>
     220                 : void
     221               4 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState, ImplBase, Endpoint>::
     222                 :     do_cancel() noexcept
     223                 : {
     224               4 :     cancel_single_op(acc_);
     225               4 : }
     226                 : 
     227                 : template<
     228                 :     class Derived,
     229                 :     class Service,
     230                 :     class Op,
     231                 :     class AcceptOp,
     232                 :     class DescState,
     233                 :     class ImplBase,
     234                 :     class Endpoint>
     235                 : void
     236             634 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState, ImplBase, Endpoint>::
     237                 :     do_close_socket() noexcept
     238                 : {
     239             634 :     auto self = this->weak_from_this().lock();
     240             634 :     if (self)
     241                 :     {
     242             634 :         acc_.request_cancel();
     243                 : 
     244             634 :         reactor_op_base* claimed = nullptr;
     245                 :         {
     246             634 :             std::lock_guard lock(desc_state_.mutex);
     247             634 :             claimed = std::exchange(desc_state_.read_op, nullptr);
     248             634 :             desc_state_.read_ready  = false;
     249             634 :             desc_state_.write_ready = false;
     250                 : 
     251             634 :             if (desc_state_.is_enqueued_.load(std::memory_order_acquire))
     252 MIS           0 :                 desc_state_.impl_ref_ = self;
     253 HIT         634 :         }
     254                 : 
     255             634 :         if (claimed)
     256                 :         {
     257               4 :             acc_.impl_ptr = self;
     258               4 :             svc_.post(&acc_);
     259               4 :             svc_.work_finished();
     260                 :         }
     261                 :     }
     262                 : 
     263             634 :     if (fd_ >= 0)
     264                 :     {
     265             156 :         if (desc_state_.registered_events != 0)
     266             138 :             svc_.scheduler().deregister_descriptor(fd_);
     267             156 :         ::close(fd_);
     268             156 :         fd_ = -1;
     269                 :     }
     270                 : 
     271             634 :     desc_state_.fd                = -1;
     272             634 :     desc_state_.registered_events = 0;
     273                 : 
     274             634 :     local_endpoint_ = Endpoint{};
     275             634 : }
     276                 : 
     277                 : template<
     278                 :     class Derived,
     279                 :     class Service,
     280                 :     class Op,
     281                 :     class AcceptOp,
     282                 :     class DescState,
     283                 :     class ImplBase,
     284                 :     class Endpoint>
     285                 : native_handle_type
     286 MIS           0 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState, ImplBase, Endpoint>::
     287                 :     do_release_socket() noexcept
     288                 : {
     289               0 :     auto self = this->weak_from_this().lock();
     290               0 :     if (self)
     291                 :     {
     292               0 :         acc_.request_cancel();
     293                 : 
     294               0 :         reactor_op_base* claimed = nullptr;
     295                 :         {
     296               0 :             std::lock_guard lock(desc_state_.mutex);
     297               0 :             claimed = std::exchange(desc_state_.read_op, nullptr);
     298               0 :             desc_state_.read_ready  = false;
     299               0 :             desc_state_.write_ready = false;
     300                 : 
     301               0 :             if (desc_state_.is_enqueued_.load(std::memory_order_acquire))
     302               0 :                 desc_state_.impl_ref_ = self;
     303               0 :         }
     304                 : 
     305               0 :         if (claimed)
     306                 :         {
     307               0 :             acc_.impl_ptr = self;
     308               0 :             svc_.post(&acc_);
     309               0 :             svc_.work_finished();
     310                 :         }
     311                 :     }
     312                 : 
     313               0 :     native_handle_type released = fd_;
     314                 : 
     315               0 :     if (fd_ >= 0)
     316                 :     {
     317               0 :         if (desc_state_.registered_events != 0)
     318               0 :             svc_.scheduler().deregister_descriptor(fd_);
     319               0 :         fd_ = -1;
     320                 :     }
     321                 : 
     322               0 :     desc_state_.fd                = -1;
     323               0 :     desc_state_.registered_events = 0;
     324                 : 
     325               0 :     local_endpoint_ = Endpoint{};
     326                 : 
     327               0 :     return released;
     328               0 : }
     329                 : 
     330                 : template<
     331                 :     class Derived,
     332                 :     class Service,
     333                 :     class Op,
     334                 :     class AcceptOp,
     335                 :     class DescState,
     336                 :     class ImplBase,
     337                 :     class Endpoint>
     338                 : std::error_code
     339 HIT         154 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState, ImplBase, Endpoint>::
     340                 :     do_bind(Endpoint const& ep)
     341                 : {
     342             154 :     sockaddr_storage storage{};
     343             154 :     socklen_t addrlen = to_sockaddr(ep, storage);
     344             154 :     if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) < 0)
     345               8 :         return make_err(errno);
     346                 : 
     347                 :     // Cache local endpoint (resolves ephemeral port / path)
     348             146 :     sockaddr_storage local{};
     349             146 :     socklen_t local_len = sizeof(local);
     350             146 :     if (::getsockname(fd_, reinterpret_cast<sockaddr*>(&local), &local_len) ==
     351                 :         0)
     352             146 :         set_local_endpoint(from_sockaddr_as(local, local_len, Endpoint{}));
     353                 : 
     354             146 :     return {};
     355                 : }
     356                 : 
     357                 : template<
     358                 :     class Derived,
     359                 :     class Service,
     360                 :     class Op,
     361                 :     class AcceptOp,
     362                 :     class DescState,
     363                 :     class ImplBase,
     364                 :     class Endpoint>
     365                 : std::error_code
     366             138 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState, ImplBase, Endpoint>::
     367                 :     do_listen(int backlog)
     368                 : {
     369             138 :     if (::listen(fd_, backlog) < 0)
     370 MIS           0 :         return make_err(errno);
     371                 : 
     372 HIT         138 :     svc_.scheduler().register_descriptor(fd_, &desc_state_);
     373             138 :     return {};
     374                 : }
     375                 : 
     376                 : } // namespace boost::corosio::detail
     377                 : 
     378                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_ACCEPTOR_HPP
        

Generated by: LCOV version 2.3