src/corosio/src/tcp_acceptor.cpp

100.0% Lines (70 / 70) 100.0% Functions (13 / 13)
tcp_acceptor.cpp
f(x) Functions (13)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Steve Gerbino
4 // Copyright (c) 2026 Michael Vandeberg
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // Official repository: https://github.com/cppalliance/corosio
10 //
11
12 #include <boost/corosio/tcp_acceptor.hpp>
13 #include <boost/corosio/error.hpp>
14 #include <boost/corosio/native/detail/endpoint_convert.hpp>
15 #include <boost/corosio/socket_option.hpp>
16 #include <boost/corosio/detail/platform.hpp>
17
18 #if BOOST_COROSIO_HAS_IOCP
19 #include <boost/corosio/native/detail/iocp/win_tcp_acceptor_service.hpp>
20 #else
21 #include <boost/corosio/detail/tcp_acceptor_service.hpp>
22 #include <boost/corosio/detail/tcp_service.hpp>
23 #endif
24
25 #include <boost/corosio/detail/except.hpp>
26
27 #include "src/detail/use_backend_service.hpp"
28
29 namespace boost::corosio {
30
31 namespace {
32
33 #if BOOST_COROSIO_HAS_IOCP
34 using tcp_peer_service = detail::win_tcp_service;
35 #else
36 using tcp_peer_service = detail::tcp_service;
37 #endif
38
39 } // namespace
40
41 #if BOOST_COROSIO_HAS_IOCP
42 namespace {
43
44 // On Windows SO_REUSEADDR grants bind-over ("hijack") rights instead
45 // of TIME_WAIT reuse, so a second listener would share the port
46 // silently. SO_EXCLUSIVEADDRUSE restores the POSIX contract: the
47 // collision surfaces as WSAEADDRINUSE (errc::address_in_use).
48 struct exclusive_address_use
49 {
50 int value_ = 1;
51
52 static int level(family) noexcept
53 {
54 return SOL_SOCKET;
55 }
56 static int name(family) noexcept
57 {
58 return SO_EXCLUSIVEADDRUSE;
59 }
60 void const* data(family) const noexcept
61 {
62 return &value_;
63 }
64 std::size_t size(family) const noexcept
65 {
66 return sizeof(value_);
67 }
68 };
69
70 } // namespace
71 #endif
72
73 2324x tcp_acceptor::~tcp_acceptor()
74 {
75 2324x close();
76 2324x }
77
78 2278x tcp_acceptor::tcp_acceptor(capy::execution_context& ctx)
79 2278x : io_object(handle(
80 ctx,
81 detail::use_backend_service<
82 detail::tcp_acceptor_service_of,
83 #if BOOST_COROSIO_HAS_IOCP
84 detail::win_tcp_acceptor_service
85 #else
86 detail::tcp_acceptor_service
87 #endif
88 2278x >(ctx)))
89 {
90 2278x }
91
92 194x tcp_acceptor::tcp_acceptor(
93 194x capy::execution_context& ctx, endpoint ep, int backlog)
94 194x : tcp_acceptor(ctx)
95 {
96 194x if (auto ec = open(ep.address().family()))
97 2x detail::throw_system_error(ec, "tcp_acceptor");
98 #if BOOST_COROSIO_HAS_IOCP
99 set_option(exclusive_address_use{});
100 #else
101 192x set_option(socket_option::reuse_address(true));
102 #endif
103 192x if (auto ec = bind(ep))
104 4x detail::throw_system_error(ec, "tcp_acceptor");
105 188x if (auto ec = listen(backlog))
106 5x detail::throw_system_error(ec, "tcp_acceptor");
107 194x }
108
109 std::error_code
110 2248x tcp_acceptor::open(family f) noexcept
111 {
112 2248x if (is_open())
113 2x return {};
114
115 #if BOOST_COROSIO_HAS_IOCP
116 auto& svc = static_cast<detail::win_tcp_acceptor_service&>(h_.service());
117 #else
118 2246x auto& svc = static_cast<detail::tcp_acceptor_service&>(h_.service());
119 #endif
120 4492x std::error_code ec = svc.open_acceptor_socket(
121 2246x *static_cast<tcp_acceptor::implementation*>(h_.get()),
122 detail::native_family(f), SOCK_STREAM, IPPROTO_TCP);
123 2246x return ec;
124 }
125
126 std::error_code
127 23x tcp_acceptor::assign(native_handle_type fd) noexcept
128 {
129 23x if (is_open())
130 6x return make_error_code(error::already_open);
131 #if BOOST_COROSIO_HAS_IOCP
132 auto& svc = static_cast<detail::win_tcp_acceptor_service&>(h_.service());
133 #else
134 17x auto& svc = static_cast<detail::tcp_acceptor_service&>(h_.service());
135 #endif
136 17x std::error_code ec = svc.assign_socket(
137 17x *static_cast<tcp_acceptor::implementation*>(h_.get()), fd);
138 17x return ec;
139 }
140
141 native_handle_type
142 14x tcp_acceptor::release()
143 {
144 14x if (!is_open())
145 2x detail::throw_system_error(
146 2x make_error_code(std::errc::bad_file_descriptor),
147 "tcp_acceptor::release");
148 12x return get().release_socket();
149 }
150
151 native_handle_type
152 34x tcp_acceptor::native_handle() const noexcept
153 {
154 34x if (!is_open())
155 {
156 #if BOOST_COROSIO_HAS_IOCP
157 return static_cast<native_handle_type>(~0ull); // INVALID_SOCKET
158 #else
159 4x return -1;
160 #endif
161 }
162 30x return get().native_handle();
163 }
164
165 std::error_code
166 2222x tcp_acceptor::bind(endpoint ep) noexcept
167 {
168 2222x if (!is_open())
169 2x return make_error_code(std::errc::bad_file_descriptor);
170 #if BOOST_COROSIO_HAS_IOCP
171 auto& svc = static_cast<detail::win_tcp_acceptor_service&>(h_.service());
172 #else
173 2220x auto& svc = static_cast<detail::tcp_acceptor_service&>(h_.service());
174 #endif
175 2220x return svc.bind_acceptor(
176 4440x *static_cast<tcp_acceptor::implementation*>(h_.get()), ep);
177 }
178
179 std::error_code
180 2193x tcp_acceptor::listen(int backlog) noexcept
181 {
182 2193x if (!is_open())
183 2x return make_error_code(std::errc::bad_file_descriptor);
184 #if BOOST_COROSIO_HAS_IOCP
185 auto& svc = static_cast<detail::win_tcp_acceptor_service&>(h_.service());
186 #else
187 2191x auto& svc = static_cast<detail::tcp_acceptor_service&>(h_.service());
188 #endif
189 2191x return svc.listen_acceptor(
190 4382x *static_cast<tcp_acceptor::implementation*>(h_.get()), backlog);
191 }
192
193 void
194 3579x tcp_acceptor::close() noexcept
195 {
196 3579x if (!is_open())
197 2153x return;
198 1426x h_.service().close(h_);
199 }
200
201 void
202 29x tcp_acceptor::cancel() noexcept
203 {
204 29x if (!is_open())
205 2x return;
206 27x get().cancel();
207 }
208
209 endpoint
210 1324x tcp_acceptor::local_endpoint() const noexcept
211 {
212 1324x if (!is_open())
213 2x return endpoint{};
214 1322x return get().local_endpoint();
215 }
216
217 void
218 4x tcp_acceptor::discard_peer(
219 tcp_acceptor& acc, io_object::implementation* impl) noexcept
220 {
221 // Its own handle closes and releases the peer, as the socket the
222 // awaiter would have received does.
223 4x auto& ctx = acc.context();
224 handle h(
225 ctx,
226 4x detail::use_backend_service<detail::tcp_service_of, tcp_peer_service>(
227 ctx),
228 4x impl);
229 4x }
230
231 } // namespace boost::corosio
232