include/boost/corosio/io/io_object.hpp

100.0% Lines (62 / 62) 100.0% Functions (21 / 21)
io_object.hpp
f(x) Functions (21)
Function Calls Lines Blocks
boost::corosio::io_object::implementation::~implementation() :64 17668x 100.0% 100.0% boost::corosio::io_object::io_service::~io_service() :92 8907x 100.0% 100.0% boost::corosio::io_object::io_service::close(boost::corosio::io_object::handle&) :119 24019x 100.0% 100.0% boost::corosio::io_object::handle::~handle() :133 105857x 100.0% 100.0% boost::corosio::io_object::handle::handle() :143 10x 100.0% 100.0% boost::corosio::io_object::handle::handle(boost::capy::execution_context&, boost::corosio::io_object::io_service&, boost::corosio::io_object::implementation*) :154 8x 100.0% 100.0% boost::corosio::io_object::handle::handle(boost::capy::execution_context&, boost::corosio::io_object::io_service&) :165 48058x 100.0% 100.0% boost::corosio::io_object::handle::handle(boost::corosio::io_object::handle&&) :173 57781x 100.0% 100.0% boost::corosio::io_object::handle::operator=(boost::corosio::io_object::handle&&) :181 43x 100.0% 100.0% boost::corosio::io_object::handle::operator bool() const :203 96285x 100.0% 100.0% boost::corosio::io_object::handle::service() const :209 45186x 100.0% 100.0% boost::corosio::io_object::handle::get() const :215 1140330x 100.0% 100.0% boost::corosio::io_object::handle::reset(boost::corosio::io_object::implementation*) :232 6503x 100.0% 100.0% boost::corosio::io_object::handle::context() const :243 41x 100.0% 100.0% boost::corosio::io_object::context() const :250 41x 100.0% 100.0% boost::corosio::io_object::~io_object() :257 54174x 100.0% 100.0% boost::corosio::io_object::io_object() :260 10x 100.0% 100.0% boost::corosio::io_object::handle boost::corosio::io_object::create_handle<boost::corosio::detail::timer_service>(boost::capy::execution_context&) :273 20405x 100.0% 100.0% boost::corosio::io_object::io_object(boost::corosio::io_object::handle) :283 48058x 100.0% 100.0% boost::corosio::io_object::io_object(boost::corosio::io_object&&) :286 6106x 100.0% 100.0% boost::corosio::io_object::operator=(boost::corosio::io_object&&) :289 4x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Steve Gerbino
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_IO_IO_OBJECT_HPP
12 #define BOOST_COROSIO_IO_IO_OBJECT_HPP
13
14 #include <boost/corosio/detail/config.hpp>
15 #include <boost/corosio/detail/except.hpp>
16 #include <boost/capy/ex/execution_context.hpp>
17
18 #include <atomic>
19 #include <cstddef>
20 #include <utility>
21
22 namespace boost::corosio {
23
24 /** Owns the platform-specific handle and execution context that a derived
25 socket, timer, signal handler, or acceptor type uses to dispatch
26 operations.
27
28 Provides common infrastructure for I/O objects that wrap kernel
29 resources (sockets, timers, signal handlers, acceptors). Derived
30 classes dispatch operations through a platform-specific vtable
31 (IOCP, epoll, kqueue, io_uring).
32
33 @par Semantics
34 Only concrete platform I/O types should inherit from `io_object`.
35 Test mocks, decorators, and stream adapters must not inherit from
36 this class. Use concepts or templates for generic I/O algorithms.
37
38 @par Thread Safety
39 Distinct objects: Safe.
40 Shared objects: Unsafe. All operations on a single I/O object
41 must be serialized.
42
43 @note Intended as a protected base class. The handle member
44 `h_` is accessible to derived classes.
45
46 @see io_stream, tcp_socket, tcp_acceptor
47 */
48 class BOOST_COROSIO_DECL io_object
49 {
50 public:
51 class handle;
52
53 /** Derived types dispatch platform-specific I/O operations through it.
54
55 Reference-counted: the owning service holds one reference
56 (`refs_` starts at 1); in-flight operations hold additional
57 references through `detail::object_ref`. When the count reaches
58 zero, `retire()` runs — services recycle the impl into
59 their free-list there.
60 */
61 struct implementation
62 {
63 /// Destroy the implementation; called only through @ref io_service.
64 17668x virtual ~implementation() = default;
65
66 /** Retire the implementation once the reference count reaches zero.
67
68 Every concrete implementation's override follows the
69 same shape: recycle into the owning service's pool member
70 as the final statement. The service befriends the
71 implementation so it can reach that private member
72 directly. Any required last-rites cleanup — releasing
73 state that is only safe to tear down once idle — runs as
74 ordinary statements immediately before the recycle.
75 Whichever action runs — recycle or delete — must be the
76 final statement: nothing touches the implementation
77 after.
78 */
79 virtual void retire() noexcept = 0;
80
81 /// In-flight + service references; starts at the service's 1.
82 std::atomic<std::size_t> refs_{1};
83 };
84
85 /** Constructs, closes, and destroys platform implementations on
86 behalf of an I/O object. Platform backends implement this
87 interface.
88 */
89 struct BOOST_COROSIO_DECL io_service
90 {
91 /// Destroy the service; the execution context outlives it.
92 8907x virtual ~io_service() = default;
93
94 /** Construct a new implementation instance.
95
96 May return a recycled implementation taken from the
97 service's free-list (populated by `implementation::retire`)
98 instead of allocating new storage.
99 */
100 virtual implementation* construct() = 0;
101
102 /** Close kernel resources and release the service's reference.
103
104 Called whenever a handle relinquishes its current
105 implementation, not only on destruction. Handle
106 destruction, move-assignment onto a handle that already
107 owns one (the replaced implementation is destroyed, the
108 incoming one is not), and `handle::reset()` all invoke
109 this. It closes the underlying descriptor and drops the
110 service's own reference. In-flight operations may still
111 hold references of their own, so the implementation is
112 not necessarily recycled or freed here. That happens in
113 `implementation::retire` whenever the reference count
114 actually reaches zero.
115 */
116 virtual void destroy(implementation* impl) = 0;
117
118 /// Close the I/O object, releasing kernel resources without deallocating.
119 24019x virtual void close([[maybe_unused]] handle& h) {}
120 };
121
122 /** Owns a platform-specific I/O implementation and destroys it
123 when the handle goes out of scope.
124 */
125 class handle
126 {
127 capy::execution_context* ctx_ = nullptr;
128 io_service* svc_ = nullptr;
129 implementation* impl_ = nullptr;
130
131 public:
132 /// Destroy the handle and its implementation.
133 105857x ~handle()
134 {
135 105857x if (impl_)
136 {
137 48024x svc_->close(*this);
138 48024x svc_->destroy(impl_);
139 }
140 105857x }
141
142 /// Construct an empty handle.
143 10x handle() = default;
144
145 /** Construct a handle that takes over an existing implementation.
146
147 @pre @p p, if non-null, was constructed by @p svc and is
148 owned by no other handle.
149
150 @param ctx The context @p svc belongs to.
151 @param svc The service that constructed @p p.
152 @param p The implementation to own.
153 */
154 8x handle(
155 capy::execution_context& ctx,
156 io_service& svc,
157 implementation* p) noexcept
158 8x : ctx_(&ctx)
159 8x , svc_(&svc)
160 8x , impl_(p)
161 {
162 8x }
163
164 /// Construct a handle bound to a context and service.
165 48058x handle(capy::execution_context& ctx, io_service& svc)
166 48058x : ctx_(&ctx)
167 48058x , svc_(&svc)
168 48058x , impl_(svc_->construct())
169 {
170 48058x }
171
172 /// Move construct from another handle.
173 57781x handle(handle&& other) noexcept
174 57781x : ctx_(std::exchange(other.ctx_, nullptr))
175 57781x , svc_(std::exchange(other.svc_, nullptr))
176 57781x , impl_(std::exchange(other.impl_, nullptr))
177 {
178 57781x }
179
180 /// Move assign from another handle.
181 43x handle& operator=(handle&& other) noexcept
182 {
183 43x if (this != &other)
184 {
185 43x if (impl_)
186 {
187 42x svc_->close(*this);
188 42x svc_->destroy(impl_);
189 }
190 43x ctx_ = std::exchange(other.ctx_, nullptr);
191 43x svc_ = std::exchange(other.svc_, nullptr);
192 43x impl_ = std::exchange(other.impl_, nullptr);
193 }
194 43x return *this;
195 }
196
197 /// Copy construction is disabled; the implementation is uniquely owned.
198 handle(handle const&) = delete;
199 /// Copy assignment is disabled; the implementation is uniquely owned.
200 handle& operator=(handle const&) = delete;
201
202 /// Return true if the handle owns an implementation.
203 96285x explicit operator bool() const noexcept
204 {
205 96285x return impl_ != nullptr;
206 }
207
208 /// Return the associated I/O service.
209 45186x io_service& service() const noexcept
210 {
211 45186x return *svc_;
212 }
213
214 /// Return the platform implementation.
215 1140330x implementation* get() const noexcept
216 {
217 1140330x return impl_;
218 }
219
220 /** Replace the implementation, destroying the old one.
221
222 @pre The handle is already bound to a service (constructed
223 via the service-taking constructor, not the default
224 one) — the old implementation is destroyed through
225 that service.
226 @pre @p p, if non-null, was constructed by this handle's
227 own service. The service that destroys it later must
228 be the one that knows how to close it.
229
230 @param p The new implementation to own. May be nullptr.
231 */
232 6503x void reset(implementation* p) noexcept
233 {
234 6503x if (impl_)
235 {
236 6503x svc_->close(*this);
237 6503x svc_->destroy(impl_);
238 }
239 6503x impl_ = p;
240 6503x }
241
242 /// Return the execution context.
243 41x capy::execution_context& context() const noexcept
244 {
245 41x return *ctx_;
246 }
247 };
248
249 /// Return the execution context.
250 41x capy::execution_context& context() const noexcept
251 {
252 41x return h_.context();
253 }
254
255 protected:
256 /// Destroy the object; protected, so only a derived type destroys one.
257 54174x virtual ~io_object() = default;
258
259 /// Default construct for virtual base initialization.
260 10x io_object() noexcept = default;
261
262 /** Create a handle bound to a service found in the context.
263
264 @tparam Service The service type whose `key_type` is used for
265 lookup.
266 @param ctx The execution context to search for the service.
267
268 @return A handle owning a freshly constructed implementation.
269
270 @throws std::logic_error if the service is not installed.
271 */
272 template<class Service>
273 20405x static handle create_handle(capy::execution_context& ctx)
274 {
275 20405x auto* svc = ctx.find_service<Service>();
276 20405x if (!svc)
277 4x detail::throw_logic_error(
278 "io_object::create_handle: service not installed");
279 20401x return handle(ctx, *svc);
280 }
281
282 /// Construct an I/O object from a handle.
283 48058x explicit io_object(handle h) noexcept : h_(std::move(h)) {}
284
285 /// Move construct from another I/O object.
286 6106x io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {}
287
288 /// Move assign from another I/O object.
289 4x io_object& operator=(io_object&& other) noexcept
290 {
291 4x if (this != &other)
292 4x h_ = std::move(other.h_);
293 4x return *this;
294 }
295
296 /// Copy construction is disabled; the handle is uniquely owned.
297 io_object(io_object const&) = delete;
298 /// Copy assignment is disabled; the handle is uniquely owned.
299 io_object& operator=(io_object const&) = delete;
300
301 /// The platform I/O handle owned by this object.
302 BOOST_COROSIO_MSVC_WARNING_PUSH
303 BOOST_COROSIO_MSVC_WARNING_DISABLE(4251)
304 handle h_;
305 BOOST_COROSIO_MSVC_WARNING_POP
306 };
307
308 } // namespace boost::corosio
309
310 #endif
311