LCOV - code coverage report
Current view: top level - corosio/native/detail/select - select_op.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 55.6 % 36 20 16
Test Date: 2026-04-15 17:16:49 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_SELECT_SELECT_OP_HPP
      11                 : #define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_OP_HPP
      12                 : 
      13                 : #include <boost/corosio/detail/platform.hpp>
      14                 : 
      15                 : #if BOOST_COROSIO_HAS_SELECT
      16                 : 
      17                 : #include <boost/corosio/native/detail/reactor/reactor_op.hpp>
      18                 : #include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
      19                 : 
      20                 : #include <errno.h>
      21                 : #include <fcntl.h>
      22                 : #include <sys/socket.h>
      23                 : #include <unistd.h>
      24                 : 
      25                 : /*
      26                 :     File descriptors are registered with the select scheduler once (via
      27                 :     select_descriptor_state) and stay registered until closed.
      28                 : 
      29                 :     select() is level-triggered but the descriptor_state pattern
      30                 :     (designed for edge-triggered) works correctly: is_enqueued_ CAS
      31                 :     prevents double-enqueue, add_ready_events is idempotent, and
      32                 :     EAGAIN ops stay parked until the next select() re-reports readiness.
      33                 : 
      34                 :     cancel() captures shared_from_this() into op.impl_ptr to prevent
      35                 :     use-after-free when the socket is closed with pending ops.
      36                 : 
      37                 :     Writes use sendmsg(MSG_NOSIGNAL) on Linux. On macOS/BSD where
      38                 :     MSG_NOSIGNAL may be absent, SO_NOSIGPIPE is set at socket creation
      39                 :     and accepted-socket setup instead.
      40                 : */
      41                 : 
      42                 : namespace boost::corosio::detail {
      43                 : 
      44                 : // Forward declarations
      45                 : class select_tcp_socket;
      46                 : class select_tcp_acceptor;
      47                 : struct select_op;
      48                 : 
      49                 : // Forward declaration
      50                 : class select_scheduler;
      51                 : 
      52                 : /// Per-descriptor state for persistent select registration.
      53                 : struct select_descriptor_state final : reactor_descriptor_state
      54                 : {};
      55                 : 
      56                 : /// select base operation — thin wrapper over reactor_op.
      57                 : struct select_op : reactor_op<select_tcp_socket, select_tcp_acceptor>
      58                 : {
      59                 :     void operator()() override;
      60                 : };
      61                 : 
      62                 : /// select connect operation.
      63                 : struct select_connect_op final : reactor_connect_op<select_op>
      64                 : {
      65                 :     void operator()() override;
      66                 :     void cancel() noexcept override;
      67                 : };
      68                 : 
      69                 : /// select scatter-read operation.
      70                 : struct select_read_op final : reactor_read_op<select_op>
      71                 : {
      72                 :     void cancel() noexcept override;
      73                 : };
      74                 : 
      75                 : /** Provides sendmsg() with EINTR retry for select writes.
      76                 : 
      77                 :     Uses MSG_NOSIGNAL where available (Linux). On platforms without
      78                 :     it (macOS/BSD), SO_NOSIGPIPE is set at socket creation time
      79                 :     and flags=0 is used here.
      80                 : */
      81                 : struct select_write_policy
      82                 : {
      83 HIT      102802 :     static ssize_t write(int fd, iovec* iovecs, int count) noexcept
      84                 :     {
      85          102802 :         msghdr msg{};
      86          102802 :         msg.msg_iov    = iovecs;
      87          102802 :         msg.msg_iovlen = static_cast<std::size_t>(count);
      88                 : 
      89                 : #ifdef MSG_NOSIGNAL
      90          102802 :         constexpr int send_flags = MSG_NOSIGNAL;
      91                 : #else
      92                 :         constexpr int send_flags = 0;
      93                 : #endif
      94                 : 
      95                 :         ssize_t n;
      96                 :         do
      97                 :         {
      98          102802 :             n = ::sendmsg(fd, &msg, send_flags);
      99                 :         }
     100          102802 :         while (n < 0 && errno == EINTR);
     101          102802 :         return n;
     102                 :     }
     103                 : };
     104                 : 
     105                 : /// select gather-write operation.
     106                 : struct select_write_op final : reactor_write_op<select_op, select_write_policy>
     107                 : {
     108                 :     void cancel() noexcept override;
     109                 : };
     110                 : 
     111                 : /** Provides accept() + fcntl(O_NONBLOCK|FD_CLOEXEC) with FD_SETSIZE check.
     112                 : 
     113                 :     Uses accept() instead of accept4() for broader POSIX compatibility.
     114                 : */
     115                 : struct select_accept_policy
     116                 : {
     117            3149 :     static int do_accept(
     118                 :         int fd, sockaddr_storage& peer, socklen_t& addrlen_out) noexcept
     119                 :     {
     120            3149 :         addrlen_out = sizeof(peer);
     121                 :         int new_fd;
     122                 :         do
     123                 :         {
     124            3149 :             addrlen_out = sizeof(peer);
     125            3149 :             new_fd = ::accept(
     126                 :                 fd, reinterpret_cast<sockaddr*>(&peer), &addrlen_out);
     127                 :         }
     128            3149 :         while (new_fd < 0 && errno == EINTR);
     129                 : 
     130            3149 :         if (new_fd < 0)
     131 MIS           0 :             return new_fd;
     132                 : 
     133 HIT        3149 :         if (new_fd >= FD_SETSIZE)
     134                 :         {
     135 MIS           0 :             ::close(new_fd);
     136               0 :             errno = EINVAL;
     137               0 :             return -1;
     138                 :         }
     139                 : 
     140 HIT        3149 :         int flags = ::fcntl(new_fd, F_GETFL, 0);
     141            3149 :         if (flags == -1)
     142                 :         {
     143 MIS           0 :             int err = errno;
     144               0 :             ::close(new_fd);
     145               0 :             errno = err;
     146               0 :             return -1;
     147                 :         }
     148                 : 
     149 HIT        3149 :         if (::fcntl(new_fd, F_SETFL, flags | O_NONBLOCK) == -1)
     150                 :         {
     151 MIS           0 :             int err = errno;
     152               0 :             ::close(new_fd);
     153               0 :             errno = err;
     154               0 :             return -1;
     155                 :         }
     156                 : 
     157 HIT        3149 :         if (::fcntl(new_fd, F_SETFD, FD_CLOEXEC) == -1)
     158                 :         {
     159 MIS           0 :             int err = errno;
     160               0 :             ::close(new_fd);
     161               0 :             errno = err;
     162               0 :             return -1;
     163                 :         }
     164                 : 
     165                 : #ifdef SO_NOSIGPIPE
     166                 :         int one = 1;
     167                 :         if (::setsockopt(new_fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) ==
     168                 :             -1)
     169                 :         {
     170                 :             int err = errno;
     171                 :             ::close(new_fd);
     172                 :             errno = err;
     173                 :             return -1;
     174                 :         }
     175                 : #endif
     176                 : 
     177 HIT        3149 :         return new_fd;
     178                 :     }
     179                 : };
     180                 : 
     181                 : /// select accept operation.
     182                 : struct select_accept_op final
     183                 :     : reactor_accept_op<select_op, select_accept_policy>
     184                 : {
     185                 :     void operator()() override;
     186                 :     void cancel() noexcept override;
     187                 : };
     188                 : 
     189                 : } // namespace boost::corosio::detail
     190                 : 
     191                 : #endif // BOOST_COROSIO_HAS_SELECT
     192                 : 
     193                 : #endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_OP_HPP
        

Generated by: LCOV version 2.3