LCOV - code coverage report
Current view: top level - corosio/native/detail/reactor - reactor_service_finals.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 87.0 % 108 94 14
Test Date: 2026-04-10 22:36:12 Functions: 96.8 % 62 60 2

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Michael Vandeberg
       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_SERVICE_FINALS_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP
      12                 : 
      13                 : /* Parameterized final service types for reactor backends.
      14                 : 
      15                 :    One service template per protocol (TCP, local stream, UDP, local
      16                 :    datagram, TCP acceptor, local stream acceptor) because each abstract
      17                 :    service base declares different virtual methods.
      18                 : */
      19                 : 
      20                 : #include <boost/corosio/native/detail/reactor/reactor_socket_finals.hpp>
      21                 : #include <boost/corosio/native/detail/reactor/reactor_socket_service.hpp>
      22                 : #include <boost/corosio/native/detail/reactor/reactor_acceptor_service.hpp>
      23                 : #include <boost/corosio/detail/tcp_service.hpp>
      24                 : #include <boost/corosio/detail/udp_service.hpp>
      25                 : #include <boost/corosio/detail/local_stream_service.hpp>
      26                 : #include <boost/corosio/detail/local_datagram_service.hpp>
      27                 : 
      28                 : #include <boost/corosio/native/detail/endpoint_convert.hpp>
      29                 : #include <boost/corosio/native/detail/make_err.hpp>
      30                 : 
      31                 : #include <system_error>
      32                 : #include <type_traits>
      33                 : 
      34                 : #include <sys/socket.h>
      35                 : #include <unistd.h>
      36                 : 
      37                 : namespace boost::corosio::detail {
      38                 : 
      39                 : // ============================================================
      40                 : // Shared socket creation helpers
      41                 : // ============================================================
      42                 : 
      43                 : template<class Traits, class SocketFinal>
      44                 : std::error_code
      45 HIT        8135 : do_open_socket(
      46                 :     SocketFinal* socket_impl,
      47                 :     int family, int type, int protocol,
      48                 :     bool is_ip) noexcept
      49                 : {
      50            8135 :     socket_impl->close_socket();
      51                 : 
      52            8135 :     int fd = Traits::create_socket(family, type, protocol);
      53            8135 :     if (fd < 0)
      54 MIS           0 :         return make_err(errno);
      55                 : 
      56                 :     std::error_code ec = is_ip
      57 HIT        8135 :         ? Traits::configure_ip_socket(fd, family)
      58              28 :         : Traits::configure_local_socket(fd);
      59                 : 
      60            8135 :     if (ec)
      61                 :     {
      62 MIS           0 :         ::close(fd);
      63               0 :         return ec;
      64                 :     }
      65                 : 
      66 HIT        8135 :     socket_impl->init_and_register(fd);
      67            8135 :     return {};
      68                 : }
      69                 : 
      70                 : template<class Traits, class SocketFinal>
      71                 : std::error_code
      72              32 : do_assign_fd(
      73                 :     SocketFinal* socket_impl,
      74                 :     int fd,
      75                 :     int expected_type) noexcept
      76                 : {
      77              32 :     socket_impl->close_socket();
      78                 : 
      79                 :     // Validate that fd is actually an AF_UNIX socket of the expected
      80                 :     // type BEFORE applying any backend setup. Otherwise we could
      81                 :     // adopt (or worse, mutate flags on) a foreign fd — e.g., an
      82                 :     // AF_INET SOCK_STREAM fd passed into local_stream_socket::assign,
      83                 :     // or a SOCK_DGRAM fd passed into a stream socket. The caller
      84                 :     // retains ownership of fd on any error from this function, so
      85                 :     // doing the checks first leaves a rejected fd unmodified.
      86                 :     {
      87              32 :         sockaddr_storage st{};
      88              32 :         socklen_t st_len = sizeof(st);
      89              32 :         if (::getsockname(
      90              32 :                 fd, reinterpret_cast<sockaddr*>(&st), &st_len) != 0)
      91 MIS           0 :             return make_err(errno);
      92 HIT          32 :         if (st.ss_family != AF_UNIX)
      93 MIS           0 :             return make_err(EAFNOSUPPORT);
      94                 : 
      95 HIT          32 :         int sock_type = 0;
      96              32 :         socklen_t opt_len = sizeof(sock_type);
      97              32 :         if (::getsockopt(
      98              32 :                 fd, SOL_SOCKET, SO_TYPE, &sock_type, &opt_len) != 0)
      99 MIS           0 :             return make_err(errno);
     100 HIT          32 :         if (sock_type != expected_type)
     101 MIS           0 :             return make_err(EPROTOTYPE);
     102                 :     }
     103                 : 
     104                 :     // Apply backend fd setup (O_NONBLOCK, FD_CLOEXEC, SO_NOSIGPIPE on kqueue,
     105                 :     // FD_SETSIZE validation on select). On failure, the caller retains
     106                 :     // ownership of fd — assign_socket reports the error and assign() throws
     107                 :     // without closing.
     108 HIT          32 :     std::error_code ec = Traits::configure_local_socket(fd);
     109              32 :     if (ec)
     110 MIS           0 :         return ec;
     111                 : 
     112 HIT          32 :     socket_impl->init_and_register(fd);
     113                 : 
     114                 :     // Best-effort: refresh endpoint caches so local_endpoint() and
     115                 :     // remote_endpoint() reflect the actual fd state. Failures (e.g.
     116                 :     // ENOTCONN from getpeername on an unconnected socket) are ignored;
     117                 :     // for unnamed socketpair() fds the queries succeed but yield empty
     118                 :     // endpoints, which is the correct cached state.
     119                 :     using endpoint_type = std::remove_cvref_t<
     120                 :         decltype(socket_impl->local_endpoint())>;
     121                 : 
     122              32 :     endpoint_type local_ep{};
     123              32 :     sockaddr_storage local_storage{};
     124              32 :     socklen_t local_len = sizeof(local_storage);
     125              32 :     if (::getsockname(
     126              32 :             fd, reinterpret_cast<sockaddr*>(&local_storage), &local_len) == 0)
     127              32 :         local_ep = from_sockaddr_as(local_storage, local_len, endpoint_type{});
     128                 : 
     129              32 :     endpoint_type remote_ep{};
     130              32 :     sockaddr_storage peer_storage{};
     131              32 :     socklen_t peer_len = sizeof(peer_storage);
     132              32 :     if (::getpeername(
     133              32 :             fd, reinterpret_cast<sockaddr*>(&peer_storage), &peer_len) == 0)
     134              32 :         remote_ep = from_sockaddr_as(peer_storage, peer_len, endpoint_type{});
     135                 : 
     136              32 :     socket_impl->set_endpoints(local_ep, remote_ep);
     137                 : 
     138              32 :     return {};
     139                 : }
     140                 : 
     141                 : template<class Traits, class AccFinal>
     142                 : std::error_code
     143             158 : do_open_acceptor(
     144                 :     AccFinal* acc_impl,
     145                 :     int family, int type, int protocol,
     146                 :     bool is_ip) noexcept
     147                 : {
     148             158 :     acc_impl->close_socket();
     149                 : 
     150             158 :     int fd = Traits::create_socket(family, type, protocol);
     151             158 :     if (fd < 0)
     152 MIS           0 :         return make_err(errno);
     153                 : 
     154                 :     std::error_code ec = is_ip
     155 HIT         158 :         ? Traits::configure_ip_acceptor(fd, family)
     156              12 :         : Traits::configure_local_socket(fd);
     157                 : 
     158             158 :     if (ec)
     159                 :     {
     160 MIS           0 :         ::close(fd);
     161               0 :         return ec;
     162                 :     }
     163                 : 
     164 HIT         158 :     acc_impl->init_acceptor_fd(fd);
     165             158 :     return {};
     166                 : }
     167                 : 
     168                 : // ============================================================
     169                 : // TCP service
     170                 : // ============================================================
     171                 : 
     172                 : template<class Traits, class SocketFinal>
     173                 : class reactor_tcp_service_final final
     174                 :     : public reactor_socket_service<
     175                 :           reactor_tcp_service_final<Traits, SocketFinal>,
     176                 :           tcp_service,
     177                 :           typename Traits::scheduler_type,
     178                 :           SocketFinal>
     179                 : {
     180                 :     using base_service = reactor_socket_service<
     181                 :         reactor_tcp_service_final, tcp_service,
     182                 :         typename Traits::scheduler_type, SocketFinal>;
     183                 :     friend base_service;
     184                 : 
     185                 : public:
     186             591 :     explicit reactor_tcp_service_final(capy::execution_context& ctx)
     187             591 :         : base_service(ctx) {}
     188                 : 
     189            8023 :     std::error_code open_socket(
     190                 :         tcp_socket::implementation& impl,
     191                 :         int family, int type, int protocol) override
     192                 :     {
     193            8023 :         return do_open_socket<Traits>(
     194                 :             static_cast<SocketFinal*>(&impl),
     195            8023 :             family, type, protocol, true);
     196                 :     }
     197                 : 
     198              12 :     std::error_code bind_socket(
     199                 :         tcp_socket::implementation& impl, endpoint ep) override
     200                 :     {
     201              12 :         return static_cast<SocketFinal*>(&impl)->do_bind(ep);
     202                 :     }
     203                 : 
     204 MIS           0 :     void pre_shutdown(SocketFinal* impl) noexcept
     205                 :     {
     206               0 :         impl->hook_.pre_shutdown(impl->native_handle());
     207               0 :     }
     208                 : 
     209 HIT       24038 :     void pre_destroy(SocketFinal* impl) noexcept
     210                 :     {
     211           24038 :         impl->hook_.pre_destroy(impl->native_handle());
     212           24038 :     }
     213                 : };
     214                 : 
     215                 : // ============================================================
     216                 : // Local stream service
     217                 : // ============================================================
     218                 : 
     219                 : template<class Traits, class SocketFinal>
     220                 : class reactor_local_stream_service_final final
     221                 :     : public reactor_socket_service<
     222                 :           reactor_local_stream_service_final<Traits, SocketFinal>,
     223                 :           local_stream_service,
     224                 :           typename Traits::scheduler_type,
     225                 :           SocketFinal>
     226                 : {
     227                 :     using base_service = reactor_socket_service<
     228                 :         reactor_local_stream_service_final, local_stream_service,
     229                 :         typename Traits::scheduler_type, SocketFinal>;
     230                 :     friend base_service;
     231                 : 
     232                 : public:
     233             591 :     explicit reactor_local_stream_service_final(capy::execution_context& ctx)
     234             591 :         : base_service(ctx) {}
     235                 : 
     236               8 :     std::error_code open_socket(
     237                 :         local_stream_socket::implementation& impl,
     238                 :         int family, int type, int protocol) override
     239                 :     {
     240               8 :         return do_open_socket<Traits>(
     241                 :             static_cast<SocketFinal*>(&impl),
     242               8 :             family, type, protocol, false);
     243                 :     }
     244                 : 
     245              16 :     std::error_code assign_socket(
     246                 :         local_stream_socket::implementation& impl, int fd) override
     247                 :     {
     248              16 :         return do_assign_fd<Traits>(
     249              16 :             static_cast<SocketFinal*>(&impl), fd, SOCK_STREAM);
     250                 :     }
     251                 : };
     252                 : 
     253                 : // ============================================================
     254                 : // UDP service
     255                 : // ============================================================
     256                 : 
     257                 : template<class Traits, class SocketFinal>
     258                 : class reactor_udp_service_final final
     259                 :     : public reactor_socket_service<
     260                 :           reactor_udp_service_final<Traits, SocketFinal>,
     261                 :           udp_service,
     262                 :           typename Traits::scheduler_type,
     263                 :           SocketFinal>
     264                 : {
     265                 :     using base_service = reactor_socket_service<
     266                 :         reactor_udp_service_final, udp_service,
     267                 :         typename Traits::scheduler_type, SocketFinal>;
     268                 :     friend base_service;
     269                 : 
     270                 : public:
     271             591 :     explicit reactor_udp_service_final(capy::execution_context& ctx)
     272             591 :         : base_service(ctx) {}
     273                 : 
     274              84 :     std::error_code open_datagram_socket(
     275                 :         udp_socket::implementation& impl,
     276                 :         int family, int type, int protocol) override
     277                 :     {
     278              84 :         return do_open_socket<Traits>(
     279                 :             static_cast<SocketFinal*>(&impl),
     280              84 :             family, type, protocol, true);
     281                 :     }
     282                 : 
     283              52 :     std::error_code bind_datagram(
     284                 :         udp_socket::implementation& impl, endpoint ep) override
     285                 :     {
     286              52 :         return static_cast<SocketFinal*>(&impl)->do_bind(ep);
     287                 :     }
     288                 : };
     289                 : 
     290                 : // ============================================================
     291                 : // Local datagram service
     292                 : // ============================================================
     293                 : 
     294                 : template<class Traits, class SocketFinal>
     295                 : class reactor_local_dgram_service_final final
     296                 :     : public reactor_socket_service<
     297                 :           reactor_local_dgram_service_final<Traits, SocketFinal>,
     298                 :           local_datagram_service,
     299                 :           typename Traits::scheduler_type,
     300                 :           SocketFinal>
     301                 : {
     302                 :     using base_service = reactor_socket_service<
     303                 :         reactor_local_dgram_service_final, local_datagram_service,
     304                 :         typename Traits::scheduler_type, SocketFinal>;
     305                 :     friend base_service;
     306                 : 
     307                 : public:
     308             591 :     explicit reactor_local_dgram_service_final(capy::execution_context& ctx)
     309             591 :         : base_service(ctx) {}
     310                 : 
     311              20 :     std::error_code open_socket(
     312                 :         local_datagram_socket::implementation& impl,
     313                 :         int family, int type, int protocol) override
     314                 :     {
     315              20 :         return do_open_socket<Traits>(
     316                 :             static_cast<SocketFinal*>(&impl),
     317              20 :             family, type, protocol, false);
     318                 :     }
     319                 : 
     320              16 :     std::error_code assign_socket(
     321                 :         local_datagram_socket::implementation& impl, int fd) override
     322                 :     {
     323              16 :         return do_assign_fd<Traits>(
     324              16 :             static_cast<SocketFinal*>(&impl), fd, SOCK_DGRAM);
     325                 :     }
     326                 : 
     327              16 :     std::error_code bind_socket(
     328                 :         local_datagram_socket::implementation& impl,
     329                 :         corosio::local_endpoint ep) override
     330                 :     {
     331              16 :         return static_cast<SocketFinal*>(&impl)->do_bind(ep);
     332                 :     }
     333                 : };
     334                 : 
     335                 : // ============================================================
     336                 : // Acceptor service
     337                 : // ============================================================
     338                 : 
     339                 : template<class Traits, class ServiceBase, class AccFinal,
     340                 :          class StreamServiceFinal, class Endpoint>
     341                 : class reactor_acceptor_service_final final
     342                 :     : public reactor_acceptor_service<
     343                 :           reactor_acceptor_service_final<Traits, ServiceBase, AccFinal,
     344                 :                                          StreamServiceFinal, Endpoint>,
     345                 :           ServiceBase,
     346                 :           typename Traits::scheduler_type,
     347                 :           AccFinal,
     348                 :           StreamServiceFinal>
     349                 : {
     350                 :     using base_service = reactor_acceptor_service<
     351                 :         reactor_acceptor_service_final,
     352                 :         ServiceBase,
     353                 :         typename Traits::scheduler_type,
     354                 :         AccFinal,
     355                 :         StreamServiceFinal>;
     356                 :     friend base_service;
     357                 : 
     358                 : public:
     359            1182 :     explicit reactor_acceptor_service_final(capy::execution_context& ctx)
     360            1182 :         : base_service(ctx)
     361                 :     {
     362                 :         // Look up the concrete stream service directly by its type.
     363                 :         // Avoids dynamic_cast which can fail across template boundaries
     364                 :         // on some platforms (FreeBSD clang RTTI/visibility).
     365            1182 :         this->stream_svc_ =
     366            1182 :             this->ctx_.template find_service<StreamServiceFinal>();
     367            1182 :     }
     368                 : 
     369             158 :     std::error_code open_acceptor_socket(
     370                 :         typename AccFinal::impl_base_type& impl,
     371                 :         int family, int type, int protocol) override
     372                 :     {
     373             158 :         return do_open_acceptor<Traits>(
     374                 :             static_cast<AccFinal*>(&impl),
     375                 :             family, type, protocol,
     376             158 :             std::is_same_v<Endpoint, endpoint>);
     377                 :     }
     378                 : 
     379             156 :     std::error_code bind_acceptor(
     380                 :         typename AccFinal::impl_base_type& impl,
     381                 :         Endpoint ep) override
     382                 :     {
     383             156 :         return static_cast<AccFinal*>(&impl)->do_bind(ep);
     384                 :     }
     385                 : 
     386             140 :     std::error_code listen_acceptor(
     387                 :         typename AccFinal::impl_base_type& impl,
     388                 :         int backlog) override
     389                 :     {
     390             140 :         return static_cast<AccFinal*>(&impl)->do_listen(backlog);
     391                 :     }
     392                 : };
     393                 : 
     394                 : } // namespace boost::corosio::detail
     395                 : 
     396                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_REACTOR_REACTOR_SERVICE_FINALS_HPP
        

Generated by: LCOV version 2.3