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_EPOLL_EPOLL_TRAITS_HPP
 
11 +
#define BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP
 
12 +

 
13 +
#include <boost/corosio/detail/platform.hpp>
 
14 +

 
15 +
#if BOOST_COROSIO_HAS_EPOLL
 
16 +

 
17 +
#include <boost/corosio/native/detail/make_err.hpp>
 
18 +
#include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
 
19 +

 
20 +
#include <system_error>
 
21 +

 
22 +
#include <errno.h>
 
23 +
#include <netinet/in.h>
 
24 +
#include <sys/socket.h>
 
25 +

 
26 +
/* epoll backend traits.
 
27 +

 
28 +
   Captures the platform-specific behavior of the Linux epoll backend:
 
29 +
   atomic SOCK_NONBLOCK|SOCK_CLOEXEC on socket(), accept4() for
 
30 +
   accepted connections, and sendmsg(MSG_NOSIGNAL) for writes.
 
31 +
*/
 
32 +

 
33 +
namespace boost::corosio::detail {
 
34 +

 
35 +
class epoll_scheduler;
 
36 +

 
37 +
struct epoll_traits
 
38 +
{
 
39 +
    using scheduler_type    = epoll_scheduler;
 
40 +
    using desc_state_type   = reactor_descriptor_state;
 
41 +

 
42 +
    static constexpr bool needs_write_notification = false;
 
43 +

 
44 +
    // No extra per-socket state or lifecycle hooks needed for epoll.
 
45 +
    struct stream_socket_hook
 
46 +
    {
 
47 +
        std::error_code on_set_option(
 
48 +
            int fd, int level, int optname,
 
49 +
            void const* data, std::size_t size) noexcept
 
50 +
        {
 
51 +
            if (::setsockopt(
 
52 +
                    fd, level, optname, data,
 
53 +
                    static_cast<socklen_t>(size)) != 0)
 
54 +
                return make_err(errno);
 
55 +
            return {};
 
56 +
        }
 
57 +
        static void pre_shutdown(int) noexcept {}
 
58 +
        static void pre_destroy(int) noexcept {}
 
59 +
    };
 
60 +

 
61 +
    struct write_policy
 
62 +
    {
 
63 +
        static ssize_t write(int fd, iovec* iovecs, int count) noexcept
 
64 +
        {
 
65 +
            msghdr msg{};
 
66 +
            msg.msg_iov    = iovecs;
 
67 +
            msg.msg_iovlen = static_cast<std::size_t>(count);
 
68 +

 
69 +
            ssize_t n;
 
70 +
            do
 
71 +
            {
 
72 +
                n = ::sendmsg(fd, &msg, MSG_NOSIGNAL);
 
73 +
            }
 
74 +
            while (n < 0 && errno == EINTR);
 
75 +
            return n;
 
76 +
        }
 
77 +
    };
 
78 +

 
79 +
    struct accept_policy
 
80 +
    {
 
81 +
        static int do_accept(
 
82 +
            int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept
 
83 +
        {
 
84 +
            addrlen = sizeof(peer);
 
85 +
            int new_fd;
 
86 +
            do
 
87 +
            {
 
88 +
                new_fd = ::accept4(
 
89 +
                    fd, reinterpret_cast<sockaddr*>(&peer), &addrlen,
 
90 +
                    SOCK_NONBLOCK | SOCK_CLOEXEC);
 
91 +
            }
 
92 +
            while (new_fd < 0 && errno == EINTR);
 
93 +
            return new_fd;
 
94 +
        }
 
95 +
    };
 
96 +

 
97 +
    // Create a nonblocking, close-on-exec socket using Linux's atomic flags.
 
98 +
    static int create_socket(int family, int type, int protocol) noexcept
 
99 +
    {
 
100 +
        return ::socket(family, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
 
101 +
    }
 
102 +

 
103 +
    // Apply protocol-specific options after socket creation.
 
104 +
    // For IP sockets, sets IPV6_V6ONLY on AF_INET6 (best-effort).
 
105 +
    static std::error_code
 
106 +
    configure_ip_socket(int fd, int family) noexcept
 
107 +
    {
 
108 +
        if (family == AF_INET6)
 
109 +
        {
 
110 +
            int one = 1;
 
111 +
            (void)::setsockopt(
 
112 +
                fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
 
113 +
        }
 
114 +
        return {};
 
115 +
    }
 
116 +

 
117 +
    // Apply protocol-specific options for acceptor sockets.
 
118 +
    // For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack, best-effort).
 
119 +
    static std::error_code
 
120 +
    configure_ip_acceptor(int fd, int family) noexcept
 
121 +
    {
 
122 +
        if (family == AF_INET6)
 
123 +
        {
 
124 +
            int val = 0;
 
125 +
            (void)::setsockopt(
 
126 +
                fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
 
127 +
        }
 
128 +
        return {};
 
129 +
    }
 
130 +

 
131 +
    // No extra configuration needed for local (unix) sockets on epoll.
 
132 +
    static std::error_code
 
133 +
    configure_local_socket(int /*fd*/) noexcept
 
134 +
    {
 
135 +
        return {};
 
136 +
    }
 
137 +

 
138 +
    // Non-mutating validation for fds adopted via assign(). Used when
 
139 +
    // the caller retains fd ownership responsibility.
 
140 +
    static std::error_code
 
141 +
    validate_assigned_fd(int /*fd*/) noexcept
 
142 +
    {
 
143 +
        return {};
 
144 +
    }
 
145 +
};
 
146 +

 
147 +
} // namespace boost::corosio::detail
 
148 +

 
149 +
#endif // BOOST_COROSIO_HAS_EPOLL
 
150 +

 
151 +
#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP