src/corosio/src/tcp_acceptor.cpp

100.0% Lines (65 / 65) 100.0% Functions (12 / 12)
tcp_acceptor.cpp
f(x) Functions (12)
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 #endif
23
24 #include <boost/corosio/detail/except.hpp>
25
26 #include "src/detail/use_backend_service.hpp"
27
28 namespace boost::corosio {
29
30 #if BOOST_COROSIO_HAS_IOCP
31 namespace {
32
33 // On Windows SO_REUSEADDR grants bind-over ("hijack") rights instead
34 // of TIME_WAIT reuse, so a second listener would share the port
35 // silently. SO_EXCLUSIVEADDRUSE restores the POSIX contract: the
36 // collision surfaces as WSAEADDRINUSE (errc::address_in_use).
37 struct exclusive_address_use
38 {
39 int value_ = 1;
40
41 static int level(family) noexcept
42 {
43 return SOL_SOCKET;
44 }
45 static int name(family) noexcept
46 {
47 return SO_EXCLUSIVEADDRUSE;
48 }
49 void const* data(family) const noexcept
50 {
51 return &value_;
52 }
53 std::size_t size(family) const noexcept
54 {
55 return sizeof(value_);
56 }
57 };
58
59 } // namespace
60 #endif
61
62 678x tcp_acceptor::~tcp_acceptor()
63 {
64 678x close();
65 678x }
66
67 639x tcp_acceptor::tcp_acceptor(capy::execution_context& ctx)
68 639x : io_object(handle(
69 ctx,
70 detail::use_backend_service<
71 detail::tcp_acceptor_service_of,
72 #if BOOST_COROSIO_HAS_IOCP
73 detail::win_tcp_acceptor_service
74 #else
75 detail::tcp_acceptor_service
76 #endif
77 639x >(ctx)))
78 {
79 639x }
80
81 96x tcp_acceptor::tcp_acceptor(
82 96x capy::execution_context& ctx, endpoint ep, int backlog)
83 96x : tcp_acceptor(ctx)
84 {
85 96x if (auto ec = open(ep.address().family()))
86 2x detail::throw_system_error(ec, "tcp_acceptor");
87 #if BOOST_COROSIO_HAS_IOCP
88 set_option(exclusive_address_use{});
89 #else
90 94x set_option(socket_option::reuse_address(true));
91 #endif
92 94x if (auto ec = bind(ep))
93 4x detail::throw_system_error(ec, "tcp_acceptor");
94 90x if (auto ec = listen(backlog))
95 5x detail::throw_system_error(ec, "tcp_acceptor");
96 96x }
97
98 std::error_code
99 645x tcp_acceptor::open(family f) noexcept
100 {
101 645x if (is_open())
102 2x return {};
103
104 #if BOOST_COROSIO_HAS_IOCP
105 auto& svc = static_cast<detail::win_tcp_acceptor_service&>(h_.service());
106 #else
107 643x auto& svc = static_cast<detail::tcp_acceptor_service&>(h_.service());
108 #endif
109 1286x std::error_code ec = svc.open_acceptor_socket(
110 643x *static_cast<tcp_acceptor::implementation*>(h_.get()),
111 detail::native_family(f), SOCK_STREAM, IPPROTO_TCP);
112 643x return ec;
113 }
114
115 std::error_code
116 23x tcp_acceptor::assign(native_handle_type fd) noexcept
117 {
118 23x if (is_open())
119 6x return make_error_code(error::already_open);
120 #if BOOST_COROSIO_HAS_IOCP
121 auto& svc = static_cast<detail::win_tcp_acceptor_service&>(h_.service());
122 #else
123 17x auto& svc = static_cast<detail::tcp_acceptor_service&>(h_.service());
124 #endif
125 17x std::error_code ec = svc.assign_socket(
126 17x *static_cast<tcp_acceptor::implementation*>(h_.get()), fd);
127 17x return ec;
128 }
129
130 native_handle_type
131 14x tcp_acceptor::release()
132 {
133 14x if (!is_open())
134 2x detail::throw_system_error(
135 2x make_error_code(std::errc::bad_file_descriptor),
136 "tcp_acceptor::release");
137 12x return get().release_socket();
138 }
139
140 native_handle_type
141 32x tcp_acceptor::native_handle() const noexcept
142 {
143 32x if (!is_open())
144 {
145 #if BOOST_COROSIO_HAS_IOCP
146 return static_cast<native_handle_type>(~0ull); // INVALID_SOCKET
147 #else
148 4x return -1;
149 #endif
150 }
151 28x return get().native_handle();
152 }
153
154 std::error_code
155 619x tcp_acceptor::bind(endpoint ep) noexcept
156 {
157 619x if (!is_open())
158 2x return make_error_code(std::errc::bad_file_descriptor);
159 #if BOOST_COROSIO_HAS_IOCP
160 auto& svc = static_cast<detail::win_tcp_acceptor_service&>(h_.service());
161 #else
162 617x auto& svc = static_cast<detail::tcp_acceptor_service&>(h_.service());
163 #endif
164 617x return svc.bind_acceptor(
165 1234x *static_cast<tcp_acceptor::implementation*>(h_.get()), ep);
166 }
167
168 std::error_code
169 590x tcp_acceptor::listen(int backlog) noexcept
170 {
171 590x if (!is_open())
172 2x return make_error_code(std::errc::bad_file_descriptor);
173 #if BOOST_COROSIO_HAS_IOCP
174 auto& svc = static_cast<detail::win_tcp_acceptor_service&>(h_.service());
175 #else
176 588x auto& svc = static_cast<detail::tcp_acceptor_service&>(h_.service());
177 #endif
178 588x return svc.listen_acceptor(
179 1176x *static_cast<tcp_acceptor::implementation*>(h_.get()), backlog);
180 }
181
182 void
183 1089x tcp_acceptor::close() noexcept
184 {
185 1089x if (!is_open())
186 462x return;
187 627x h_.service().close(h_);
188 }
189
190 void
191 17x tcp_acceptor::cancel() noexcept
192 {
193 17x if (!is_open())
194 2x return;
195 15x get().cancel();
196 }
197
198 endpoint
199 559x tcp_acceptor::local_endpoint() const noexcept
200 {
201 559x if (!is_open())
202 2x return endpoint{};
203 557x return get().local_endpoint();
204 }
205
206 } // namespace boost::corosio
207