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 : /** Owns the platform-specific handle and execution context that a derived
23 : socket, timer, signal handler, or acceptor type uses to dispatch
24 : operations.
25 :
26 : Provides common infrastructure for I/O objects that wrap kernel
27 : resources (sockets, timers, signal handlers, acceptors). Derived
28 : classes dispatch operations through a platform-specific vtable
29 : (IOCP, epoll, kqueue, io_uring).
30 :
31 : @par Semantics
32 : Only concrete platform I/O types should inherit from `io_object`.
33 : Test mocks, decorators, and stream adapters must not inherit from
34 : this class. Use concepts or templates for generic I/O algorithms.
35 :
36 : @par Thread Safety
37 : Distinct objects: Safe.
38 : Shared objects: Unsafe. All operations on a single I/O object
39 : must be serialized.
40 :
41 : @note Intended as a protected base class. The handle member
42 : `h_` is accessible to derived classes.
43 :
44 : @see io_stream, tcp_socket, tcp_acceptor
45 : */
46 : class BOOST_COROSIO_DECL io_object
47 : {
48 : public:
49 : class handle;
50 :
51 : /** Derived types dispatch platform-specific I/O operations through it.
52 : */
53 : struct implementation
54 : {
55 : /// Destroy the implementation; called only through @ref io_service.
56 HIT 18115 : virtual ~implementation() = default;
57 : };
58 :
59 : /** Constructs, closes, and destroys platform implementations on
60 : behalf of an I/O object. Platform backends implement this
61 : interface.
62 : */
63 : struct BOOST_COROSIO_DECL io_service
64 : {
65 : /// Destroy the service; the execution context outlives it.
66 5026 : 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* impl) = 0;
73 :
74 : /// Close the I/O object, releasing kernel resources without deallocating.
75 15337 : virtual void close([[maybe_unused]] handle& h) {}
76 : };
77 :
78 : /** Owns a platform-specific I/O implementation and destroys it
79 : when the handle goes out of scope.
80 : */
81 : class handle
82 : {
83 : capy::execution_context* ctx_ = nullptr;
84 : io_service* svc_ = nullptr;
85 : implementation* impl_ = nullptr;
86 :
87 : public:
88 : /// Destroy the handle and its implementation.
89 54848 : ~handle()
90 : {
91 54848 : if (impl_)
92 : {
93 26835 : svc_->close(*this);
94 26835 : svc_->destroy(impl_);
95 : }
96 54848 : }
97 :
98 : /// Construct an empty handle.
99 10 : handle() = default;
100 :
101 : /// Construct a handle bound to a context and service.
102 26897 : handle(capy::execution_context& ctx, io_service& svc)
103 26897 : : ctx_(&ctx)
104 26897 : , svc_(&svc)
105 26897 : , impl_(svc_->construct())
106 : {
107 26897 : }
108 :
109 : /// Move construct from another handle.
110 27962 : handle(handle&& other) noexcept
111 27962 : : ctx_(std::exchange(other.ctx_, nullptr))
112 27962 : , svc_(std::exchange(other.svc_, nullptr))
113 27962 : , impl_(std::exchange(other.impl_, nullptr))
114 : {
115 27962 : }
116 :
117 : /// Move assign from another handle.
118 42 : handle& operator=(handle&& other) noexcept
119 : {
120 42 : if (this != &other)
121 : {
122 42 : if (impl_)
123 : {
124 41 : svc_->close(*this);
125 41 : svc_->destroy(impl_);
126 : }
127 42 : ctx_ = std::exchange(other.ctx_, nullptr);
128 42 : svc_ = std::exchange(other.svc_, nullptr);
129 42 : impl_ = std::exchange(other.impl_, nullptr);
130 : }
131 42 : return *this;
132 : }
133 :
134 : /// Copy construction is disabled; the implementation is uniquely owned.
135 : handle(handle const&) = delete;
136 : /// Copy assignment is disabled; the implementation is uniquely owned.
137 : handle& operator=(handle const&) = delete;
138 :
139 : /// Return true if the handle owns an implementation.
140 43868 : explicit operator bool() const noexcept
141 : {
142 43868 : return impl_ != nullptr;
143 : }
144 :
145 : /// Return the associated I/O service.
146 19273 : io_service& service() const noexcept
147 : {
148 19273 : return *svc_;
149 : }
150 :
151 : /// Return the platform implementation.
152 544029 : implementation* get() const noexcept
153 : {
154 544029 : 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 4496 : void reset(implementation* p) noexcept
162 : {
163 4496 : if (impl_)
164 : {
165 4496 : svc_->close(*this);
166 4496 : svc_->destroy(impl_);
167 : }
168 4496 : impl_ = p;
169 4496 : }
170 :
171 : /// Return the execution context.
172 39 : capy::execution_context& context() const noexcept
173 : {
174 39 : return *ctx_;
175 : }
176 : };
177 :
178 : /// Return the execution context.
179 39 : capy::execution_context& context() const noexcept
180 : {
181 39 : return h_.context();
182 : }
183 :
184 : protected:
185 : /// Destroy the object; protected, so only a derived type destroys one.
186 27618 : virtual ~io_object() = default;
187 :
188 : /// Default construct for virtual base initialization.
189 10 : io_object() noexcept = default;
190 :
191 : /** Create a handle bound to a service found in the context.
192 :
193 : @tparam Service The service type whose key_type is used for lookup.
194 : @param ctx The execution context to search for the service.
195 :
196 : @return A handle owning a freshly constructed implementation.
197 :
198 : @throws std::logic_error if the service is not installed.
199 : */
200 : template<class Service>
201 15090 : static handle create_handle(capy::execution_context& ctx)
202 : {
203 15090 : auto* svc = ctx.find_service<Service>();
204 15090 : if (!svc)
205 4 : detail::throw_logic_error(
206 : "io_object::create_handle: service not installed");
207 15086 : return handle(ctx, *svc);
208 : }
209 :
210 : /// Construct an I/O object from a handle.
211 26897 : explicit io_object(handle h) noexcept : h_(std::move(h)) {}
212 :
213 : /// Move construct from another I/O object.
214 732 : io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {}
215 :
216 : /// Move assign from another I/O object.
217 4 : io_object& operator=(io_object&& other) noexcept
218 : {
219 4 : if (this != &other)
220 4 : h_ = std::move(other.h_);
221 4 : return *this;
222 : }
223 :
224 : /// Copy construction is disabled; the handle is uniquely owned.
225 : io_object(io_object const&) = delete;
226 : /// Copy assignment is disabled; the handle is uniquely owned.
227 : io_object& operator=(io_object const&) = delete;
228 :
229 : /// The platform I/O handle owned by this object.
230 : BOOST_COROSIO_MSVC_WARNING_PUSH
231 : BOOST_COROSIO_MSVC_WARNING_DISABLE(4251)
232 : handle h_;
233 : BOOST_COROSIO_MSVC_WARNING_POP
234 : };
235 :
236 : } // namespace boost::corosio
237 :
238 : #endif
|