1  
//
1  
//
2  
// Copyright (c) 2026 Steve Gerbino
2  
// Copyright (c) 2026 Steve Gerbino
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_SELECT_SELECT_TCP_SERVICE_HPP
10  
#ifndef BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SERVICE_HPP
11  
#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SERVICE_HPP
12  

12  

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

14  

15  
#if BOOST_COROSIO_HAS_SELECT
15  
#if BOOST_COROSIO_HAS_SELECT
16  

16  

17  
#include <boost/corosio/detail/config.hpp>
17  
#include <boost/corosio/detail/config.hpp>
18  
#include <boost/corosio/detail/tcp_service.hpp>
18  
#include <boost/corosio/detail/tcp_service.hpp>
19  

19  

20  
#include <boost/corosio/native/detail/select/select_tcp_socket.hpp>
20  
#include <boost/corosio/native/detail/select/select_tcp_socket.hpp>
21  
#include <boost/corosio/native/detail/select/select_scheduler.hpp>
21  
#include <boost/corosio/native/detail/select/select_scheduler.hpp>
22  
#include <boost/corosio/native/detail/reactor/reactor_socket_service.hpp>
22  
#include <boost/corosio/native/detail/reactor/reactor_socket_service.hpp>
23  

23  

24  
#include <boost/corosio/native/detail/reactor/reactor_op_complete.hpp>
24  
#include <boost/corosio/native/detail/reactor/reactor_op_complete.hpp>
25  

25  

26  
#include <coroutine>
26  
#include <coroutine>
27  
#include <mutex>
27  
#include <mutex>
28  

28  

29  
#include <errno.h>
29  
#include <errno.h>
30  
#include <fcntl.h>
30  
#include <fcntl.h>
31  
#include <netinet/in.h>
31  
#include <netinet/in.h>
32  
#include <netinet/tcp.h>
32  
#include <netinet/tcp.h>
33  
#include <sys/select.h>
33  
#include <sys/select.h>
34  
#include <sys/socket.h>
34  
#include <sys/socket.h>
35  
#include <unistd.h>
35  
#include <unistd.h>
36  

36  

37  
/*
37  
/*
38  
    Each I/O op tries the syscall speculatively; only registers with
38  
    Each I/O op tries the syscall speculatively; only registers with
39  
    the reactor on EAGAIN. Fd is registered once at open time and
39  
    the reactor on EAGAIN. Fd is registered once at open time and
40  
    stays registered until close. The reactor only marks ready_events_;
40  
    stays registered until close. The reactor only marks ready_events_;
41  
    actual I/O happens in invoke_deferred_io(). cancel() captures
41  
    actual I/O happens in invoke_deferred_io(). cancel() captures
42  
    shared_from_this() into op.impl_ptr to keep the impl alive.
42  
    shared_from_this() into op.impl_ptr to keep the impl alive.
43  
*/
43  
*/
44  

44  

45  
namespace boost::corosio::detail {
45  
namespace boost::corosio::detail {
46  

46  

47  
/** select TCP service implementation.
47  
/** select TCP service implementation.
48  

48  

49  
    Inherits from tcp_service to enable runtime polymorphism.
49  
    Inherits from tcp_service to enable runtime polymorphism.
50  
    Uses key_type = tcp_service for service lookup.
50  
    Uses key_type = tcp_service for service lookup.
51  
*/
51  
*/
52  
class BOOST_COROSIO_DECL select_tcp_service final
52  
class BOOST_COROSIO_DECL select_tcp_service final
53  
    : public reactor_socket_service<
53  
    : public reactor_socket_service<
54  
          select_tcp_service,
54  
          select_tcp_service,
55  
          tcp_service,
55  
          tcp_service,
56  
          select_scheduler,
56  
          select_scheduler,
57  
          select_tcp_socket>
57  
          select_tcp_socket>
58  
{
58  
{
59  
public:
59  
public:
60  
    explicit select_tcp_service(capy::execution_context& ctx)
60  
    explicit select_tcp_service(capy::execution_context& ctx)
61  
        : reactor_socket_service(ctx)
61  
        : reactor_socket_service(ctx)
62  
    {
62  
    {
63  
    }
63  
    }
64  

64  

65  
    std::error_code open_socket(
65  
    std::error_code open_socket(
66  
        tcp_socket::implementation& impl,
66  
        tcp_socket::implementation& impl,
67  
        int family,
67  
        int family,
68  
        int type,
68  
        int type,
69  
        int protocol) override;
69  
        int protocol) override;
 
70 +

 
71 +
    std::error_code
 
72 +
    bind_socket(tcp_socket::implementation& impl, endpoint ep) override;
70  
};
73  
};
71  

74  

72  
inline void
75  
inline void
73  
select_connect_op::cancel() noexcept
76  
select_connect_op::cancel() noexcept
74  
{
77  
{
75  
    if (socket_impl_)
78  
    if (socket_impl_)
76  
        socket_impl_->cancel_single_op(*this);
79  
        socket_impl_->cancel_single_op(*this);
77  
    else
80  
    else
78  
        request_cancel();
81  
        request_cancel();
79  
}
82  
}
80  

83  

81  
inline void
84  
inline void
82  
select_read_op::cancel() noexcept
85  
select_read_op::cancel() noexcept
83  
{
86  
{
84  
    if (socket_impl_)
87  
    if (socket_impl_)
85  
        socket_impl_->cancel_single_op(*this);
88  
        socket_impl_->cancel_single_op(*this);
86  
    else
89  
    else
87  
        request_cancel();
90  
        request_cancel();
88  
}
91  
}
89  

92  

90  
inline void
93  
inline void
91  
select_write_op::cancel() noexcept
94  
select_write_op::cancel() noexcept
92  
{
95  
{
93  
    if (socket_impl_)
96  
    if (socket_impl_)
94  
        socket_impl_->cancel_single_op(*this);
97  
        socket_impl_->cancel_single_op(*this);
95  
    else
98  
    else
96  
        request_cancel();
99  
        request_cancel();
97  
}
100  
}
98  

101  

99  
inline void
102  
inline void
100  
select_op::operator()()
103  
select_op::operator()()
101  
{
104  
{
102  
    complete_io_op(*this);
105  
    complete_io_op(*this);
103  
}
106  
}
104  

107  

105  
inline void
108  
inline void
106  
select_connect_op::operator()()
109  
select_connect_op::operator()()
107  
{
110  
{
108  
    complete_connect_op(*this);
111  
    complete_connect_op(*this);
109  
}
112  
}
110  

113  

111  
inline select_tcp_socket::select_tcp_socket(select_tcp_service& svc) noexcept
114  
inline select_tcp_socket::select_tcp_socket(select_tcp_service& svc) noexcept
112  
    : reactor_stream_socket(svc)
115  
    : reactor_stream_socket(svc)
113  
{
116  
{
114  
}
117  
}
115  

118  

116  
inline select_tcp_socket::~select_tcp_socket() = default;
119  
inline select_tcp_socket::~select_tcp_socket() = default;
117  

120  

118  
inline std::coroutine_handle<>
121  
inline std::coroutine_handle<>
119  
select_tcp_socket::connect(
122  
select_tcp_socket::connect(
120  
    std::coroutine_handle<> h,
123  
    std::coroutine_handle<> h,
121  
    capy::executor_ref ex,
124  
    capy::executor_ref ex,
122  
    endpoint ep,
125  
    endpoint ep,
123  
    std::stop_token token,
126  
    std::stop_token token,
124  
    std::error_code* ec)
127  
    std::error_code* ec)
125  
{
128  
{
126  
    auto result = do_connect(h, ex, ep, token, ec);
129  
    auto result = do_connect(h, ex, ep, token, ec);
127  
    // Rebuild fd_sets so select() watches for writability
130  
    // Rebuild fd_sets so select() watches for writability
128  
    if (result == std::noop_coroutine())
131  
    if (result == std::noop_coroutine())
129  
        svc_.scheduler().notify_reactor();
132  
        svc_.scheduler().notify_reactor();
130  
    return result;
133  
    return result;
131  
}
134  
}
132  

135  

133  
inline std::coroutine_handle<>
136  
inline std::coroutine_handle<>
134  
select_tcp_socket::read_some(
137  
select_tcp_socket::read_some(
135  
    std::coroutine_handle<> h,
138  
    std::coroutine_handle<> h,
136  
    capy::executor_ref ex,
139  
    capy::executor_ref ex,
137  
    buffer_param param,
140  
    buffer_param param,
138  
    std::stop_token token,
141  
    std::stop_token token,
139  
    std::error_code* ec,
142  
    std::error_code* ec,
140  
    std::size_t* bytes_out)
143  
    std::size_t* bytes_out)
141  
{
144  
{
142  
    return do_read_some(h, ex, param, token, ec, bytes_out);
145  
    return do_read_some(h, ex, param, token, ec, bytes_out);
143  
}
146  
}
144  

147  

145  
inline std::coroutine_handle<>
148  
inline std::coroutine_handle<>
146  
select_tcp_socket::write_some(
149  
select_tcp_socket::write_some(
147  
    std::coroutine_handle<> h,
150  
    std::coroutine_handle<> h,
148  
    capy::executor_ref ex,
151  
    capy::executor_ref ex,
149  
    buffer_param param,
152  
    buffer_param param,
150  
    std::stop_token token,
153  
    std::stop_token token,
151  
    std::error_code* ec,
154  
    std::error_code* ec,
152  
    std::size_t* bytes_out)
155  
    std::size_t* bytes_out)
153  
{
156  
{
154  
    auto result = do_write_some(h, ex, param, token, ec, bytes_out);
157  
    auto result = do_write_some(h, ex, param, token, ec, bytes_out);
155  
    // Rebuild fd_sets so select() watches for writability
158  
    // Rebuild fd_sets so select() watches for writability
156  
    if (result == std::noop_coroutine())
159  
    if (result == std::noop_coroutine())
157  
        svc_.scheduler().notify_reactor();
160  
        svc_.scheduler().notify_reactor();
158  
    return result;
161  
    return result;
159  
}
162  
}
160  

163  

161  
inline void
164  
inline void
162  
select_tcp_socket::cancel() noexcept
165  
select_tcp_socket::cancel() noexcept
163  
{
166  
{
164  
    do_cancel();
167  
    do_cancel();
165  
}
168  
}
166  

169  

167  
inline void
170  
inline void
168  
select_tcp_socket::close_socket() noexcept
171  
select_tcp_socket::close_socket() noexcept
169  
{
172  
{
170  
    do_close_socket();
173  
    do_close_socket();
171  
}
174  
}
172  

175  

173  
inline std::error_code
176  
inline std::error_code
174  
select_tcp_service::open_socket(
177  
select_tcp_service::open_socket(
175  
    tcp_socket::implementation& impl, int family, int type, int protocol)
178  
    tcp_socket::implementation& impl, int family, int type, int protocol)
176  
{
179  
{
177  
    auto* select_impl = static_cast<select_tcp_socket*>(&impl);
180  
    auto* select_impl = static_cast<select_tcp_socket*>(&impl);
178  
    select_impl->close_socket();
181  
    select_impl->close_socket();
179  

182  

180  
    int fd = ::socket(family, type, protocol);
183  
    int fd = ::socket(family, type, protocol);
181  
    if (fd < 0)
184  
    if (fd < 0)
182  
        return make_err(errno);
185  
        return make_err(errno);
183  

186  

184  
    if (family == AF_INET6)
187  
    if (family == AF_INET6)
185  
    {
188  
    {
186  
        int one = 1;
189  
        int one = 1;
187  
        ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
190  
        ::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
188  
    }
191  
    }
189  

192  

190  
    int flags = ::fcntl(fd, F_GETFL, 0);
193  
    int flags = ::fcntl(fd, F_GETFL, 0);
191  
    if (flags == -1)
194  
    if (flags == -1)
192  
    {
195  
    {
193  
        int errn = errno;
196  
        int errn = errno;
194  
        ::close(fd);
197  
        ::close(fd);
195  
        return make_err(errn);
198  
        return make_err(errn);
196  
    }
199  
    }
197  
    if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
200  
    if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
198  
    {
201  
    {
199  
        int errn = errno;
202  
        int errn = errno;
200  
        ::close(fd);
203  
        ::close(fd);
201  
        return make_err(errn);
204  
        return make_err(errn);
202  
    }
205  
    }
203  
    if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
206  
    if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
204  
    {
207  
    {
205  
        int errn = errno;
208  
        int errn = errno;
206  
        ::close(fd);
209  
        ::close(fd);
207  
        return make_err(errn);
210  
        return make_err(errn);
208  
    }
211  
    }
209  

212  

210  
    if (fd >= FD_SETSIZE)
213  
    if (fd >= FD_SETSIZE)
211  
    {
214  
    {
212  
        ::close(fd);
215  
        ::close(fd);
213  
        return make_err(EMFILE);
216  
        return make_err(EMFILE);
214  
    }
217  
    }
215  

218  

216  
#ifdef SO_NOSIGPIPE
219  
#ifdef SO_NOSIGPIPE
217  
    {
220  
    {
218  
        int one = 1;
221  
        int one = 1;
219  
        ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
222  
        ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
220  
    }
223  
    }
221  
#endif
224  
#endif
222  

225  

223  
    select_impl->fd_ = fd;
226  
    select_impl->fd_ = fd;
224  

227  

225  
    select_impl->desc_state_.fd = fd;
228  
    select_impl->desc_state_.fd = fd;
226  
    {
229  
    {
227  
        std::lock_guard lock(select_impl->desc_state_.mutex);
230  
        std::lock_guard lock(select_impl->desc_state_.mutex);
228  
        select_impl->desc_state_.read_op    = nullptr;
231  
        select_impl->desc_state_.read_op    = nullptr;
229  
        select_impl->desc_state_.write_op   = nullptr;
232  
        select_impl->desc_state_.write_op   = nullptr;
230  
        select_impl->desc_state_.connect_op = nullptr;
233  
        select_impl->desc_state_.connect_op = nullptr;
231  
    }
234  
    }
232  
    scheduler().register_descriptor(fd, &select_impl->desc_state_);
235  
    scheduler().register_descriptor(fd, &select_impl->desc_state_);
233  

236  

234  
    return {};
237  
    return {};
 
238 +
}
 
239 +

 
240 +
inline std::error_code
 
241 +
select_tcp_service::bind_socket(
 
242 +
    tcp_socket::implementation& impl, endpoint ep)
 
243 +
{
 
244 +
    return static_cast<select_tcp_socket*>(&impl)->do_bind(ep);
235  
}
245  
}
236  

246  

237  
} // namespace boost::corosio::detail
247  
} // namespace boost::corosio::detail
238  

248  

239  
#endif // BOOST_COROSIO_HAS_SELECT
249  
#endif // BOOST_COROSIO_HAS_SELECT
240  

250  

241  
#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SERVICE_HPP
251  
#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TCP_SERVICE_HPP