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: 87.5 % 80 70 10
Test Date: 2026-03-13 23:11:04 Functions: 85.7 % 28 24 4

           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                 : */
      43                 : template<
      44                 :     class Derived,
      45                 :     class Service,
      46                 :     class Op,
      47                 :     class AcceptOp,
      48                 :     class DescState>
      49                 : class reactor_acceptor
      50                 :     : public tcp_acceptor::implementation
      51                 :     , public std::enable_shared_from_this<Derived>
      52                 :     , public intrusive_list<Derived>::node
      53                 : {
      54                 :     friend Derived;
      55                 : 
      56 HIT         141 :     explicit reactor_acceptor(Service& svc) noexcept : svc_(svc) {}
      57                 : 
      58                 : protected:
      59                 :     Service& svc_;
      60                 :     int fd_ = -1;
      61                 :     endpoint local_endpoint_;
      62                 : 
      63                 : public:
      64                 :     /// Pending accept operation slot.
      65                 :     AcceptOp acc_;
      66                 : 
      67                 :     /// Per-descriptor state for persistent reactor registration.
      68                 :     DescState desc_state_;
      69                 : 
      70             141 :     ~reactor_acceptor() override = default;
      71                 : 
      72                 :     /// Return the underlying file descriptor.
      73                 :     int native_handle() const noexcept
      74                 :     {
      75                 :         return fd_;
      76                 :     }
      77                 : 
      78                 :     /// Return the cached local endpoint.
      79            7607 :     endpoint local_endpoint() const noexcept override
      80                 :     {
      81            7607 :         return local_endpoint_;
      82                 :     }
      83                 : 
      84                 :     /// Return true if the acceptor has an open file descriptor.
      85            8467 :     bool is_open() const noexcept override
      86                 :     {
      87            8467 :         return fd_ >= 0;
      88                 :     }
      89                 : 
      90                 :     /// Set a socket option.
      91             135 :     std::error_code set_option(
      92                 :         int level,
      93                 :         int optname,
      94                 :         void const* data,
      95                 :         std::size_t size) noexcept override
      96                 :     {
      97             135 :         if (::setsockopt(
      98             135 :                 fd_, level, optname, data, static_cast<socklen_t>(size)) != 0)
      99 MIS           0 :             return make_err(errno);
     100 HIT         135 :         return {};
     101                 :     }
     102                 : 
     103                 :     /// Get a socket option.
     104                 :     std::error_code
     105 MIS           0 :     get_option(int level, int optname, void* data, std::size_t* size)
     106                 :         const noexcept override
     107                 :     {
     108               0 :         socklen_t len = static_cast<socklen_t>(*size);
     109               0 :         if (::getsockopt(fd_, level, optname, data, &len) != 0)
     110               0 :             return make_err(errno);
     111               0 :         *size = static_cast<std::size_t>(len);
     112               0 :         return {};
     113                 :     }
     114                 : 
     115                 :     /// Cache the local endpoint.
     116 HIT         132 :     void set_local_endpoint(endpoint ep) noexcept
     117                 :     {
     118             132 :         local_endpoint_ = ep;
     119             132 :     }
     120                 : 
     121                 :     /// Return a reference to the owning service.
     122            7481 :     Service& service() noexcept
     123                 :     {
     124            7481 :         return svc_;
     125                 :     }
     126                 : 
     127                 :     /** Cancel a single pending operation.
     128                 : 
     129                 :         Claims the operation from the read_op descriptor slot
     130                 :         under the mutex and posts it to the scheduler as cancelled.
     131                 : 
     132                 :         @param op The operation to cancel.
     133                 :     */
     134                 :     void cancel_single_op(Op& op) noexcept;
     135                 : 
     136                 :     /** Cancel the pending accept operation.
     137                 : 
     138                 :         Invoked by the derived class's cancel() override.
     139                 :     */
     140                 :     void do_cancel() noexcept;
     141                 : 
     142                 :     /** Close the acceptor and cancel pending operations.
     143                 : 
     144                 :         Invoked by the derived class's close_socket(). The
     145                 :         derived class may add backend-specific cleanup after
     146                 :         calling this method.
     147                 :     */
     148                 :     void do_close_socket() noexcept;
     149                 : 
     150                 :     /** Bind the acceptor socket to an endpoint.
     151                 : 
     152                 :         Caches the resolved local endpoint (including ephemeral
     153                 :         port) after a successful bind.
     154                 : 
     155                 :         @param ep The endpoint to bind to.
     156                 :         @return The error code from bind(), or success.
     157                 :     */
     158                 :     std::error_code do_bind(endpoint ep);
     159                 : 
     160                 :     /** Start listening on the acceptor socket.
     161                 : 
     162                 :         Registers the file descriptor with the reactor after
     163                 :         a successful listen() call.
     164                 : 
     165                 :         @param backlog The listen backlog.
     166                 :         @return The error code from listen(), or success.
     167                 :     */
     168                 :     std::error_code do_listen(int backlog);
     169                 : };
     170                 : 
     171                 : template<
     172                 :     class Derived,
     173                 :     class Service,
     174                 :     class Op,
     175                 :     class AcceptOp,
     176                 :     class DescState>
     177                 : void
     178              10 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::cancel_single_op(
     179                 :     Op& op) noexcept
     180                 : {
     181              10 :     auto self = this->weak_from_this().lock();
     182              10 :     if (!self)
     183 MIS           0 :         return;
     184                 : 
     185 HIT          10 :     op.request_cancel();
     186                 : 
     187              10 :     reactor_op_base* claimed = nullptr;
     188                 :     {
     189              10 :         std::lock_guard lock(desc_state_.mutex);
     190              10 :         if (desc_state_.read_op == &op)
     191               8 :             claimed = std::exchange(desc_state_.read_op, nullptr);
     192              10 :     }
     193              10 :     if (claimed)
     194                 :     {
     195               8 :         op.impl_ptr = self;
     196               8 :         svc_.post(&op);
     197               8 :         svc_.work_finished();
     198                 :     }
     199              10 : }
     200                 : 
     201                 : template<
     202                 :     class Derived,
     203                 :     class Service,
     204                 :     class Op,
     205                 :     class AcceptOp,
     206                 :     class DescState>
     207                 : void
     208               4 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::
     209                 :     do_cancel() noexcept
     210                 : {
     211               4 :     cancel_single_op(acc_);
     212               4 : }
     213                 : 
     214                 : template<
     215                 :     class Derived,
     216                 :     class Service,
     217                 :     class Op,
     218                 :     class AcceptOp,
     219                 :     class DescState>
     220                 : void
     221             558 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::
     222                 :     do_close_socket() noexcept
     223                 : {
     224             558 :     auto self = this->weak_from_this().lock();
     225             558 :     if (self)
     226                 :     {
     227             558 :         acc_.request_cancel();
     228                 : 
     229             558 :         reactor_op_base* claimed = nullptr;
     230                 :         {
     231             558 :             std::lock_guard lock(desc_state_.mutex);
     232             558 :             claimed = std::exchange(desc_state_.read_op, nullptr);
     233             558 :             desc_state_.read_ready  = false;
     234             558 :             desc_state_.write_ready = false;
     235                 : 
     236             558 :             if (desc_state_.is_enqueued_.load(std::memory_order_acquire))
     237 MIS           0 :                 desc_state_.impl_ref_ = self;
     238 HIT         558 :         }
     239                 : 
     240             558 :         if (claimed)
     241                 :         {
     242               4 :             acc_.impl_ptr = self;
     243               4 :             svc_.post(&acc_);
     244               4 :             svc_.work_finished();
     245                 :         }
     246                 :     }
     247                 : 
     248             558 :     if (fd_ >= 0)
     249                 :     {
     250             138 :         if (desc_state_.registered_events != 0)
     251             132 :             svc_.scheduler().deregister_descriptor(fd_);
     252             138 :         ::close(fd_);
     253             138 :         fd_ = -1;
     254                 :     }
     255                 : 
     256             558 :     desc_state_.fd                = -1;
     257             558 :     desc_state_.registered_events = 0;
     258                 : 
     259             558 :     local_endpoint_ = endpoint{};
     260             558 : }
     261                 : 
     262                 : template<
     263                 :     class Derived,
     264                 :     class Service,
     265                 :     class Op,
     266                 :     class AcceptOp,
     267                 :     class DescState>
     268                 : std::error_code
     269             136 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::do_bind(
     270                 :     endpoint ep)
     271                 : {
     272             136 :     sockaddr_storage storage{};
     273             136 :     socklen_t addrlen = to_sockaddr(ep, storage);
     274             136 :     if (::bind(fd_, reinterpret_cast<sockaddr*>(&storage), addrlen) < 0)
     275               4 :         return make_err(errno);
     276                 : 
     277                 :     // Cache local endpoint (resolves ephemeral port)
     278             132 :     sockaddr_storage local{};
     279             132 :     socklen_t local_len = sizeof(local);
     280             132 :     if (::getsockname(fd_, reinterpret_cast<sockaddr*>(&local), &local_len) ==
     281                 :         0)
     282             132 :         set_local_endpoint(from_sockaddr(local));
     283                 : 
     284             132 :     return {};
     285                 : }
     286                 : 
     287                 : template<
     288                 :     class Derived,
     289                 :     class Service,
     290                 :     class Op,
     291                 :     class AcceptOp,
     292                 :     class DescState>
     293                 : std::error_code
     294             132 : reactor_acceptor<Derived, Service, Op, AcceptOp, DescState>::do_listen(
     295                 :     int backlog)
     296                 : {
     297             132 :     if (::listen(fd_, backlog) < 0)
     298 MIS           0 :         return make_err(errno);
     299                 : 
     300 HIT         132 :     svc_.scheduler().register_descriptor(fd_, &desc_state_);
     301             132 :     return {};
     302                 : }
     303                 : 
     304                 : } // namespace boost::corosio::detail
     305                 : 
     306                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_ACCEPTOR_HPP
        

Generated by: LCOV version 2.3