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_state.hpp>
18  
#include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
19  

19  

20  
#include <system_error>
20  
#include <system_error>
21  

21  

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

25  

26  
/* epoll backend traits.
26  
/* epoll backend traits.
27  

27  

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

32  

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

34  

35  
class epoll_scheduler;
35  
class epoll_scheduler;
36  

36  

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

41  

42  
    static constexpr bool needs_write_notification = false;
42  
    static constexpr bool needs_write_notification = false;
43  

43  

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

60  

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

68  

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

78  

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

96  

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

102  

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

116  

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

130  

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

137  

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

146  

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

148  

149  
#endif // BOOST_COROSIO_HAS_EPOLL
149  
#endif // BOOST_COROSIO_HAS_EPOLL
150  

150  

151  
#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP
151  
#endif // BOOST_COROSIO_NATIVE_DETAIL_EPOLL_EPOLL_TRAITS_HPP