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 -

 
70 -
    std::error_code
 
71 -
    bind_socket(tcp_socket::implementation& impl, endpoint ep) override;
 
72  
        int protocol) override;
69  
        int protocol) override;
73  
};
70  
};
74  

71  

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

80  

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

89  

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

98  

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

104  

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

110  

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

115  

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

117  

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

132  

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

144  

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

160  

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

166  

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

172  

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

179  

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

183  

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

189  

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

209  

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

215  

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

222  

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

224  

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

233  

237 -
}
 
238 -

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

236  

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

238  

249  
#endif // BOOST_COROSIO_HAS_SELECT
239  
#endif // BOOST_COROSIO_HAS_SELECT
250  

240  

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