TLA Line data 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 <utility>
19 :
20 : namespace boost::corosio {
21 :
22 : /** Base class for platform I/O objects.
23 :
24 : Provides common infrastructure for I/O objects that wrap kernel
25 : resources (sockets, timers, signal handlers, acceptors). Derived
26 : classes dispatch operations through a platform-specific vtable
27 : (IOCP, epoll, kqueue, io_uring).
28 :
29 : @par Semantics
30 : Only concrete platform I/O types should inherit from `io_object`.
31 : Test mocks, decorators, and stream adapters must not inherit from
32 : this class. Use concepts or templates for generic I/O algorithms.
33 :
34 : @par Thread Safety
35 : Distinct objects: Safe.
36 : Shared objects: Unsafe. All operations on a single I/O object
37 : must be serialized.
38 :
39 : @note Intended as a protected base class. The handle member
40 : `h_` is accessible to derived classes.
41 :
42 : @see io_stream, tcp_socket, tcp_acceptor
43 : */
44 : class BOOST_COROSIO_DECL io_object
45 : {
46 : public:
47 : class handle;
48 :
49 : /** Base interface for platform I/O implementations.
50 :
51 : Derived classes provide platform-specific operation dispatch.
52 : */
53 : struct implementation
54 : {
55 HIT 26378 : virtual ~implementation() = default;
56 : };
57 :
58 : /** Service interface for I/O object lifecycle management.
59 :
60 : Platform backends implement this interface to manage the
61 : creation, closing, and destruction of I/O object
62 : implementations.
63 : */
64 : struct io_service
65 : {
66 6457 : virtual ~io_service() = default;
67 :
68 : /// Construct a new implementation instance.
69 : virtual implementation* construct() = 0;
70 :
71 : /// Destroy the implementation, closing kernel resources and freeing memory.
72 : virtual void destroy(implementation*) = 0;
73 :
74 : /// Close the I/O object, releasing kernel resources without deallocating.
75 9335 : virtual void close(handle&) {}
76 : };
77 :
78 : /** RAII wrapper for I/O object implementation lifetime.
79 :
80 : Manages ownership of the platform-specific implementation,
81 : automatically destroying it when the handle goes out of scope.
82 : */
83 : class handle
84 : {
85 : capy::execution_context* ctx_ = nullptr;
86 : io_service* svc_ = nullptr;
87 : implementation* impl_ = nullptr;
88 :
89 : public:
90 : /// Destroy the handle and its implementation.
91 63191 : ~handle()
92 : {
93 63191 : if (impl_)
94 : {
95 26802 : svc_->close(*this);
96 26802 : svc_->destroy(impl_);
97 : }
98 63191 : }
99 :
100 : /// Construct an empty handle.
101 MIS 0 : handle() = default;
102 :
103 : /// Construct a handle bound to a context and service.
104 HIT 26829 : handle(capy::execution_context& ctx, io_service& svc)
105 26829 : : ctx_(&ctx)
106 26829 : , svc_(&svc)
107 26829 : , impl_(svc_->construct())
108 : {
109 26829 : }
110 :
111 : /// Move construct from another handle.
112 36364 : handle(handle&& other) noexcept
113 36364 : : ctx_(std::exchange(other.ctx_, nullptr))
114 36364 : , svc_(std::exchange(other.svc_, nullptr))
115 36364 : , impl_(std::exchange(other.impl_, nullptr))
116 : {
117 36364 : }
118 :
119 : /// Move assign from another handle.
120 25 : handle& operator=(handle&& other) noexcept
121 : {
122 25 : if (this != &other)
123 : {
124 25 : if (impl_)
125 : {
126 25 : svc_->close(*this);
127 25 : svc_->destroy(impl_);
128 : }
129 25 : ctx_ = std::exchange(other.ctx_, nullptr);
130 25 : svc_ = std::exchange(other.svc_, nullptr);
131 25 : impl_ = std::exchange(other.impl_, nullptr);
132 : }
133 25 : return *this;
134 : }
135 :
136 : handle(handle const&) = delete;
137 : handle& operator=(handle const&) = delete;
138 :
139 : /// Return true if the handle owns an implementation.
140 62555 : explicit operator bool() const noexcept
141 : {
142 62555 : return impl_ != nullptr;
143 : }
144 :
145 : /// Return the associated I/O service.
146 26665 : io_service& service() const noexcept
147 : {
148 26665 : return *svc_;
149 : }
150 :
151 : /// Return the platform implementation.
152 557961 : implementation* get() const noexcept
153 : {
154 557961 : return impl_;
155 : }
156 :
157 : /** Replace the implementation, destroying the old one.
158 :
159 : @param p The new implementation to own. May be nullptr.
160 : */
161 8518 : void reset(implementation* p) noexcept
162 : {
163 8518 : if (impl_)
164 : {
165 8518 : svc_->close(*this);
166 8518 : svc_->destroy(impl_);
167 : }
168 8518 : impl_ = p;
169 8518 : }
170 :
171 : /// Return the execution context.
172 10 : capy::execution_context& context() const noexcept
173 : {
174 10 : return *ctx_;
175 : }
176 : };
177 :
178 : /// Return the execution context.
179 10 : capy::execution_context& context() const noexcept
180 : {
181 10 : return h_.context();
182 : }
183 :
184 : protected:
185 27050 : virtual ~io_object() = default;
186 :
187 : /// Default construct for virtual base initialization.
188 MIS 0 : io_object() noexcept = default;
189 :
190 : /** Create a handle bound to a service found in the context.
191 :
192 : @tparam Service The service type whose key_type is used for lookup.
193 : @param ctx The execution context to search for the service.
194 :
195 : @return A handle owning a freshly constructed implementation.
196 :
197 : @throws std::logic_error if the service is not installed.
198 : */
199 : template<class Service>
200 HIT 17611 : static handle create_handle(capy::execution_context& ctx)
201 : {
202 17611 : Service* svc = nullptr;
203 :
204 : // Prefer key_type lookup — stable across TUs even for template
205 : // instantiations where type_id<T>() may differ per object file
206 : // on platforms that don't merge inline-function statics (FreeBSD).
207 : if constexpr (requires { typename Service::key_type; })
208 : svc = static_cast<Service*>(
209 17611 : ctx.find_service<typename Service::key_type>());
210 :
211 17611 : if (!svc)
212 MIS 0 : svc = ctx.find_service<Service>();
213 :
214 HIT 17611 : if (!svc)
215 MIS 0 : detail::throw_logic_error(
216 : "io_object::create_handle: service not installed");
217 HIT 17611 : return handle(ctx, *svc);
218 : }
219 :
220 : /// Construct an I/O object from a handle.
221 26829 : explicit io_object(handle h) noexcept : h_(std::move(h)) {}
222 :
223 : /// Move construct from another I/O object.
224 223 : io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {}
225 :
226 : /// Move assign from another I/O object.
227 : io_object& operator=(io_object&& other) noexcept
228 : {
229 : if (this != &other)
230 : h_ = std::move(other.h_);
231 : return *this;
232 : }
233 :
234 : io_object(io_object const&) = delete;
235 : io_object& operator=(io_object const&) = delete;
236 :
237 : /// The platform I/O handle owned by this object.
238 : handle h_;
239 : };
240 :
241 : } // namespace boost::corosio
242 :
243 : #endif
|