100.00% Lines (39/39) 100.00% Functions (11/11)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Michael Vandeberg 2   // Copyright (c) 2026 Michael Vandeberg
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP 10   #ifndef BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP
11   #define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP 11   #define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP
12   12  
13   #include <boost/corosio/detail/platform.hpp> 13   #include <boost/corosio/detail/platform.hpp>
14   14  
15   #if BOOST_COROSIO_HAS_EPOLL 15   #if BOOST_COROSIO_HAS_EPOLL
16   16  
17   #include <boost/corosio/native/detail/make_err.hpp> 17   #include <boost/corosio/native/detail/make_err.hpp>
  18 + #include <boost/corosio/native/detail/reactor/reactor_descriptor_ops.hpp>
18   #include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp> 19   #include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
19   20  
20   #include <system_error> 21   #include <system_error>
21   #include <tuple> 22   #include <tuple>
22   23  
23   #include <errno.h> 24   #include <errno.h>
24   #include <netinet/in.h> 25   #include <netinet/in.h>
25   #include <sys/socket.h> 26   #include <sys/socket.h>
  27 + #include <sys/uio.h>
  28 + #include <unistd.h>
26   29  
27   /* epoll backend traits. 30   /* epoll backend traits.
28   31  
29   Captures the platform-specific behavior of the Linux epoll backend: 32   Captures the platform-specific behavior of the Linux epoll backend:
30   atomic SOCK_NONBLOCK|SOCK_CLOEXEC on socket(), accept4() for 33   atomic SOCK_NONBLOCK|SOCK_CLOEXEC on socket(), accept4() for
31   accepted connections, and sendmsg(MSG_NOSIGNAL) for writes. 34   accepted connections, and sendmsg(MSG_NOSIGNAL) for writes.
32   */ 35   */
33   36  
34   namespace boost::corosio::detail { 37   namespace boost::corosio::detail {
35   38  
36   class epoll_scheduler; 39   class epoll_scheduler;
37   40  
38   struct epoll_traits 41   struct epoll_traits
39   { 42   {
40   using scheduler_type = epoll_scheduler; 43   using scheduler_type = epoll_scheduler;
41   using desc_state_type = reactor_descriptor_state; 44   using desc_state_type = reactor_descriptor_state;
42   45  
43 - static constexpr bool needs_write_notification = false; 46 + static constexpr bool needs_park_notification = false;
44   47  
45   // No extra per-socket state or lifecycle hooks needed for epoll. 48   // No extra per-socket state or lifecycle hooks needed for epoll.
46   struct stream_socket_hook 49   struct stream_socket_hook
47   { 50   {
HITCBC 48   191 std::error_code on_set_option( 51   197 std::error_code on_set_option(
49   int fd, 52   int fd,
50   int level, 53   int level,
51   int optname, 54   int optname,
52   void const* data, 55   void const* data,
53   std::size_t size) noexcept 56   std::size_t size) noexcept
54   { 57   {
HITCBC 55   191 if (::setsockopt( 58   197 if (::setsockopt(
HITCBC 56   191 fd, level, optname, data, static_cast<socklen_t>(size)) != 59   197 fd, level, optname, data, static_cast<socklen_t>(size)) !=
57   0) 60   0)
HITCBC 58   5 return make_err(errno); 61   5 return make_err(errno);
HITCBC 59   186 return {}; 62   192 return {};
60   } 63   }
HITCBC 61   23728 static void pre_shutdown(int) noexcept {} 64   23373 static void pre_shutdown(int) noexcept {}
HITCBC 62   7723 static void pre_destroy(int) noexcept {} 65   7628 static void pre_destroy(int) noexcept {}
63   }; 66   };
64   67  
65   struct write_policy 68   struct write_policy
66   { 69   {
HITCBC 67   75 static ssize_t write(int fd, iovec* iovecs, int count) noexcept 70   77 static ssize_t write(int fd, iovec* iovecs, int count) noexcept
68   { 71   {
HITCBC 69   75 msghdr msg{}; 72   77 msghdr msg{};
HITCBC 70   75 msg.msg_iov = iovecs; 73   77 msg.msg_iov = iovecs;
HITCBC 71   75 msg.msg_iovlen = static_cast<std::size_t>(count); 74   77 msg.msg_iovlen = static_cast<std::size_t>(count);
72   75  
73   ssize_t n; 76   ssize_t n;
74   do 77   do
75   { 78   {
HITCBC 76   76 n = ::sendmsg(fd, &msg, MSG_NOSIGNAL); 79   78 n = ::sendmsg(fd, &msg, MSG_NOSIGNAL);
77   } 80   }
HITCBC 78   76 while (n < 0 && errno == EINTR); 81   78 while (n < 0 && errno == EINTR);
HITCBC 79   75 return n; 82   77 return n;
80   } 83   }
81   84  
82   static ssize_t 85   static ssize_t
HITCBC 83   105763 write_one(int fd, void const* data, std::size_t size) noexcept 86   99522 write_one(int fd, void const* data, std::size_t size) noexcept
84   { 87   {
85   ssize_t n; 88   ssize_t n;
86   do 89   do
87   { 90   {
HITCBC 88   105764 n = ::send(fd, data, size, MSG_NOSIGNAL); 91   99523 n = ::send(fd, data, size, MSG_NOSIGNAL);
89   } 92   }
HITCBC 90   105764 while (n < 0 && errno == EINTR); 93   99523 while (n < 0 && errno == EINTR);
HITCBC 91   105763 return n; 94   99522 return n;
92   } 95   }
93   }; 96   };
  97 +
  98 + using descriptor_write_policy = detail::descriptor_write_policy;
94   99  
95   struct accept_policy 100   struct accept_policy
96   { 101   {
97   static int 102   static int
HITCBC 98   5118 do_accept(int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept 103   5055 do_accept(int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept
99   { 104   {
HITCBC 100   5118 addrlen = sizeof(peer); 105   5055 addrlen = sizeof(peer);
101   int new_fd; 106   int new_fd;
102   do 107   do
103   { 108   {
HITCBC 104   5119 new_fd = ::accept4( 109   5056 new_fd = ::accept4(
105   fd, reinterpret_cast<sockaddr*>(&peer), &addrlen, 110   fd, reinterpret_cast<sockaddr*>(&peer), &addrlen,
106   SOCK_NONBLOCK | SOCK_CLOEXEC); 111   SOCK_NONBLOCK | SOCK_CLOEXEC);
107   } 112   }
HITCBC 108   5119 while (new_fd < 0 && errno == EINTR); 113   5056 while (new_fd < 0 && errno == EINTR);
HITCBC 109   5118 return new_fd; 114   5055 return new_fd;
110   } 115   }
111   }; 116   };
112   117  
113   // Create a nonblocking, close-on-exec socket using Linux's atomic flags. 118   // Create a nonblocking, close-on-exec socket using Linux's atomic flags.
HITCBC 114   3206 static int create_socket(int family, int type, int protocol) noexcept 119   3185 static int create_socket(int family, int type, int protocol) noexcept
115   { 120   {
HITCBC 116   3206 return ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); 121   3185 return ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
117   } 122   }
118   123  
119   // Apply protocol-specific options after socket creation. 124   // Apply protocol-specific options after socket creation.
120   // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort). 125   // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort).
HITCBC 121   2732 static std::error_code configure_ip_socket(int fd, int family) noexcept 126   2703 static std::error_code configure_ip_socket(int fd, int family) noexcept
122   { 127   {
HITCBC 123   2732 if (family == AF_INET6) 128   2703 if (family == AF_INET6)
124   { 129   {
HITCBC 125   23 int one = 1; 130   23 int one = 1;
126   std::ignore = 131   std::ignore =
HITCBC 127   23 ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); 132   23 ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
128   } 133   }
HITCBC 129   2732 return {}; 134   2703 return {};
130   } 135   }
131   136  
132   // Apply protocol-specific options for acceptor sockets. 137   // Apply protocol-specific options for acceptor sockets.
133   // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort). 138   // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort).
HITCBC 134   349 static std::error_code configure_ip_acceptor(int fd, int family) noexcept 139   356 static std::error_code configure_ip_acceptor(int fd, int family) noexcept
135   { 140   {
HITCBC 136   349 if (family == AF_INET6) 141   356 if (family == AF_INET6)
137   { 142   {
HITCBC 138   11 int val = 0; 143   11 int val = 0;
139   std::ignore = 144   std::ignore =
HITCBC 140   11 ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); 145   11 ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
141   } 146   }
HITCBC 142   349 return {}; 147   356 return {};
143   } 148   }
144   149  
145   // No extra configuration needed for local (unix) sockets on epoll. 150   // No extra configuration needed for local (unix) sockets on epoll.
HITCBC 146   116 static std::error_code configure_local_socket(int /*fd*/) noexcept 151   117 static std::error_code configure_local_socket(int /*fd*/) noexcept
147   { 152   {
HITCBC 148   116 return {}; 153   117 return {};
149   } 154   }
150   155  
151   // Non-mutating validation for fds adopted via assign(). Used when 156   // Non-mutating validation for fds adopted via assign(). Used when
152   // the caller retains fd ownership responsibility. 157   // the caller retains fd ownership responsibility.
HITCBC 153   168 static std::error_code validate_assigned_fd(int /*fd*/) noexcept 158   164 static std::error_code validate_assigned_fd(int /*fd*/) noexcept
154   { 159   {
HITCBC 155   168 return {}; 160   164 return {};
156   } 161   }
157   }; 162   };
158   163  
159   } // namespace boost::corosio::detail 164   } // namespace boost::corosio::detail
160   165  
161   #endif // BOOST_COROSIO_HAS_EPOLL 166   #endif // BOOST_COROSIO_HAS_EPOLL
162   167  
163   #endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP 168   #endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP