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 +
        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 +
        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 +
        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 +
        ~handle()
 
92 +
        {
 
93 +
            if (impl_)
 
94 +
            {
 
95 +
                svc_->close(*this);
 
96 +
                svc_->destroy(impl_);
 
97 +
            }
 
98 +
        }
 
99 +

 
100 +
        /// Construct an empty handle.
 
101 +
        handle() = default;
 
102 +

 
103 +
        /// Construct a handle bound to a context and service.
 
104 +
        handle(capy::execution_context& ctx, io_service& svc)
 
105 +
            : ctx_(&ctx)
 
106 +
            , svc_(&svc)
 
107 +
            , impl_(svc_->construct())
 
108 +
        {
 
109 +
        }
 
110 +

 
111 +
        /// Move construct from another handle.
 
112 +
        handle(handle&& other) noexcept
 
113 +
            : ctx_(std::exchange(other.ctx_, nullptr))
 
114 +
            , svc_(std::exchange(other.svc_, nullptr))
 
115 +
            , impl_(std::exchange(other.impl_, nullptr))
 
116 +
        {
 
117 +
        }
 
118 +

 
119 +
        /// Move assign from another handle.
 
120 +
        handle& operator=(handle&& other) noexcept
 
121 +
        {
 
122 +
            if (this != &other)
 
123 +
            {
 
124 +
                if (impl_)
 
125 +
                {
 
126 +
                    svc_->close(*this);
 
127 +
                    svc_->destroy(impl_);
 
128 +
                }
 
129 +
                ctx_  = std::exchange(other.ctx_, nullptr);
 
130 +
                svc_  = std::exchange(other.svc_, nullptr);
 
131 +
                impl_ = std::exchange(other.impl_, nullptr);
 
132 +
            }
 
133 +
            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 +
        explicit operator bool() const noexcept
 
141 +
        {
 
142 +
            return impl_ != nullptr;
 
143 +
        }
 
144 +

 
145 +
        /// Return the associated I/O service.
 
146 +
        io_service& service() const noexcept
 
147 +
        {
 
148 +
            return *svc_;
 
149 +
        }
 
150 +

 
151 +
        /// Return the platform implementation.
 
152 +
        implementation* get() const noexcept
 
153 +
        {
 
154 +
            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 +
        void reset(implementation* p) noexcept
 
162 +
        {
 
163 +
            if (impl_)
 
164 +
            {
 
165 +
                svc_->close(*this);
 
166 +
                svc_->destroy(impl_);
 
167 +
            }
 
168 +
            impl_ = p;
 
169 +
        }
 
170 +

 
171 +
        /// Return the execution context.
 
172 +
        capy::execution_context& context() const noexcept
 
173 +
        {
 
174 +
            return *ctx_;
 
175 +
        }
 
176 +
    };
 
177 +

 
178 +
    /// Return the execution context.
 
179 +
    capy::execution_context& context() const noexcept
 
180 +
    {
 
181 +
        return h_.context();
 
182 +
    }
 
183 +

 
184 +
protected:
 
185 +
    virtual ~io_object() = default;
 
186 +

 
187 +
    /// Default construct for virtual base initialization.
 
188 +
    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 +
    static handle create_handle(capy::execution_context& ctx)
 
201 +
    {
 
202 +
        auto* svc = ctx.find_service<Service>();
 
203 +
        if (!svc)
 
204 +
            detail::throw_logic_error(
 
205 +
                "io_object::create_handle: service not installed");
 
206 +
        return handle(ctx, *svc);
 
207 +
    }
 
208 +

 
209 +
    /// Construct an I/O object from a handle.
 
210 +
    explicit io_object(handle h) noexcept : h_(std::move(h)) {}
 
211 +

 
212 +
    /// Move construct from another I/O object.
 
213 +
    io_object(io_object&& other) noexcept : h_(std::move(other.h_)) {}
 
214 +

 
215 +
    /// Move assign from another I/O object.
 
216 +
    io_object& operator=(io_object&& other) noexcept
 
217 +
    {
 
218 +
        if (this != &other)
 
219 +
            h_ = std::move(other.h_);
 
220 +
        return *this;
 
221 +
    }
 
222 +

 
223 +
    io_object(io_object const&)            = delete;
 
224 +
    io_object& operator=(io_object const&) = delete;
 
225 +

 
226 +
    handle h_;
 
227 +
};
 
228 +

 
229 +
} // namespace boost::corosio
 
230 +

 
231 +
#endif