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_ops.hpp>
19   #include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp> 19   #include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
20   20  
21   #include <system_error> 21   #include <system_error>
22   #include <tuple> 22   #include <tuple>
23   23  
24   #include <errno.h> 24   #include <errno.h>
25   #include <netinet/in.h> 25   #include <netinet/in.h>
26   #include <sys/socket.h> 26   #include <sys/socket.h>
27   #include <sys/uio.h> 27   #include <sys/uio.h>
28   #include <unistd.h> 28   #include <unistd.h>
29   29  
30   /* epoll backend traits. 30   /* epoll backend traits.
31   31  
32   Captures the platform-specific behavior of the Linux epoll backend: 32   Captures the platform-specific behavior of the Linux epoll backend:
33   atomic SOCK_NONBLOCK|SOCK_CLOEXEC on socket(), accept4() for 33   atomic SOCK_NONBLOCK|SOCK_CLOEXEC on socket(), accept4() for
34   accepted connections, and sendmsg(MSG_NOSIGNAL) for writes. 34   accepted connections, and sendmsg(MSG_NOSIGNAL) for writes.
35   */ 35   */
36   36  
37   namespace boost::corosio::detail { 37   namespace boost::corosio::detail {
38   38  
39   class epoll_scheduler; 39   class epoll_scheduler;
40   40  
41   struct epoll_traits 41   struct epoll_traits
42   { 42   {
43   using scheduler_type = epoll_scheduler; 43   using scheduler_type = epoll_scheduler;
44   using desc_state_type = reactor_descriptor_state; 44   using desc_state_type = reactor_descriptor_state;
45   45  
46   static constexpr bool needs_park_notification = false; 46   static constexpr bool needs_park_notification = false;
47   47  
48   // No extra per-socket state or lifecycle hooks needed for epoll. 48   // No extra per-socket state or lifecycle hooks needed for epoll.
49   struct stream_socket_hook 49   struct stream_socket_hook
50   { 50   {
HITCBC 51   197 std::error_code on_set_option( 51   197 std::error_code on_set_option(
52   int fd, 52   int fd,
53   int level, 53   int level,
54   int optname, 54   int optname,
55   void const* data, 55   void const* data,
56   std::size_t size) noexcept 56   std::size_t size) noexcept
57   { 57   {
HITCBC 58   197 if (::setsockopt( 58   197 if (::setsockopt(
HITCBC 59   197 fd, level, optname, data, static_cast<socklen_t>(size)) != 59   197 fd, level, optname, data, static_cast<socklen_t>(size)) !=
60   0) 60   0)
HITCBC 61   5 return make_err(errno); 61   5 return make_err(errno);
HITCBC 62   192 return {}; 62   192 return {};
63   } 63   }
HITCBC 64   23733 static void pre_shutdown(int) noexcept {} 64   23445 static void pre_shutdown(int) noexcept {}
HITCBC 65   7748 static void pre_destroy(int) noexcept {} 65   7652 static void pre_destroy(int) noexcept {}
66   }; 66   };
67   67  
68   struct write_policy 68   struct write_policy
69   { 69   {
HITCBC 70   77 static ssize_t write(int fd, iovec* iovecs, int count) noexcept 70   77 static ssize_t write(int fd, iovec* iovecs, int count) noexcept
71   { 71   {
HITCBC 72   77 msghdr msg{}; 72   77 msghdr msg{};
HITCBC 73   77 msg.msg_iov = iovecs; 73   77 msg.msg_iov = iovecs;
HITCBC 74   77 msg.msg_iovlen = static_cast<std::size_t>(count); 74   77 msg.msg_iovlen = static_cast<std::size_t>(count);
75   75  
76   ssize_t n; 76   ssize_t n;
77   do 77   do
78   { 78   {
HITCBC 79   78 n = ::sendmsg(fd, &msg, MSG_NOSIGNAL); 79   78 n = ::sendmsg(fd, &msg, MSG_NOSIGNAL);
80   } 80   }
HITCBC 81   78 while (n < 0 && errno == EINTR); 81   78 while (n < 0 && errno == EINTR);
HITCBC 82   77 return n; 82   77 return n;
83   } 83   }
84   84  
85   static ssize_t 85   static ssize_t
HITCBC 86   104512 write_one(int fd, void const* data, std::size_t size) noexcept 86   104492 write_one(int fd, void const* data, std::size_t size) noexcept
87   { 87   {
88   ssize_t n; 88   ssize_t n;
89   do 89   do
90   { 90   {
HITCBC 91   104513 n = ::send(fd, data, size, MSG_NOSIGNAL); 91   104493 n = ::send(fd, data, size, MSG_NOSIGNAL);
92   } 92   }
HITCBC 93   104513 while (n < 0 && errno == EINTR); 93   104493 while (n < 0 && errno == EINTR);
HITCBC 94   104512 return n; 94   104492 return n;
95   } 95   }
96   }; 96   };
97   97  
98   using descriptor_write_policy = detail::descriptor_write_policy; 98   using descriptor_write_policy = detail::descriptor_write_policy;
99   99  
100   struct accept_policy 100   struct accept_policy
101   { 101   {
102   static int 102   static int
HITCBC 103   5135 do_accept(int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept 103   5071 do_accept(int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept
104   { 104   {
HITCBC 105   5135 addrlen = sizeof(peer); 105   5071 addrlen = sizeof(peer);
106   int new_fd; 106   int new_fd;
107   do 107   do
108   { 108   {
HITCBC 109   5136 new_fd = ::accept4( 109   5072 new_fd = ::accept4(
110   fd, reinterpret_cast<sockaddr*>(&peer), &addrlen, 110   fd, reinterpret_cast<sockaddr*>(&peer), &addrlen,
111   SOCK_NONBLOCK | SOCK_CLOEXEC); 111   SOCK_NONBLOCK | SOCK_CLOEXEC);
112   } 112   }
HITCBC 113   5136 while (new_fd < 0 && errno == EINTR); 113   5072 while (new_fd < 0 && errno == EINTR);
HITCBC 114   5135 return new_fd; 114   5071 return new_fd;
115   } 115   }
116   }; 116   };
117   117  
118   // 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 119   3225 static int create_socket(int family, int type, int protocol) noexcept 119   3193 static int create_socket(int family, int type, int protocol) noexcept
120   { 120   {
HITCBC 121   3225 return ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); 121   3193 return ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
122   } 122   }
123   123  
124   // Apply protocol-specific options after socket creation. 124   // Apply protocol-specific options after socket creation.
125   // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort). 125   // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort).
HITCBC 126   2743 static std::error_code configure_ip_socket(int fd, int family) noexcept 126   2711 static std::error_code configure_ip_socket(int fd, int family) noexcept
127   { 127   {
HITCBC 128   2743 if (family == AF_INET6) 128   2711 if (family == AF_INET6)
129   { 129   {
HITCBC 130   23 int one = 1; 130   23 int one = 1;
131   std::ignore = 131   std::ignore =
HITCBC 132   23 ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); 132   23 ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
133   } 133   }
HITCBC 134   2743 return {}; 134   2711 return {};
135   } 135   }
136   136  
137   // Apply protocol-specific options for acceptor sockets. 137   // Apply protocol-specific options for acceptor sockets.
138   // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort). 138   // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort).
HITCBC 139   356 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
140   { 140   {
HITCBC 141   356 if (family == AF_INET6) 141   356 if (family == AF_INET6)
142   { 142   {
HITCBC 143   11 int val = 0; 143   11 int val = 0;
144   std::ignore = 144   std::ignore =
HITCBC 145   11 ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); 145   11 ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
146   } 146   }
HITCBC 147   356 return {}; 147   356 return {};
148   } 148   }
149   149  
150   // No extra configuration needed for local (unix) sockets on epoll. 150   // No extra configuration needed for local (unix) sockets on epoll.
HITCBC 151   117 static std::error_code configure_local_socket(int /*fd*/) noexcept 151   117 static std::error_code configure_local_socket(int /*fd*/) noexcept
152   { 152   {
HITCBC 153   117 return {}; 153   117 return {};
154   } 154   }
155   155  
156   // Non-mutating validation for fds adopted via assign(). Used when 156   // Non-mutating validation for fds adopted via assign(). Used when
157   // the caller retains fd ownership responsibility. 157   // the caller retains fd ownership responsibility.
HITCBC 158   164 static std::error_code validate_assigned_fd(int /*fd*/) noexcept 158   164 static std::error_code validate_assigned_fd(int /*fd*/) noexcept
159   { 159   {
HITCBC 160   164 return {}; 160   164 return {};
161   } 161   }
162   }; 162   };
163   163  
164   } // namespace boost::corosio::detail 164   } // namespace boost::corosio::detail
165   165  
166   #endif // BOOST_COROSIO_HAS_EPOLL 166   #endif // BOOST_COROSIO_HAS_EPOLL
167   167  
168   #endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP 168   #endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP