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 +
struct descriptor_state;
 
37 +

 
38 +
struct epoll_traits
 
39 +
{
 
40 +
    using scheduler_type    = epoll_scheduler;
 
41 +
    using desc_state_type   = descriptor_state;
 
42 +

 
43 +
    static constexpr bool needs_write_notification = false;
 
44 +

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

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

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

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

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

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

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

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

 
142 +
} // namespace boost::corosio::detail
 
143 +

 
144 +
#endif // BOOST_COROSIO_HAS_EPOLL
 
145 +

 
146 +
#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP