1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_IO_OBJECT_HPP
10  
#ifndef BOOST_COROSIO_IO_OBJECT_HPP
11  
#define BOOST_COROSIO_IO_OBJECT_HPP
11  
#define BOOST_COROSIO_IO_OBJECT_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
 
14 +
#include <boost/corosio/detail/except.hpp>
14  
#include <boost/capy/ex/execution_context.hpp>
15  
#include <boost/capy/ex/execution_context.hpp>
15  

16  

 
17 +
#include <utility>
 
18 +

16  
namespace boost::corosio {
19  
namespace boost::corosio {
17  

20  

18  
/** Base class for platform I/O objects.
21  
/** Base class for platform I/O objects.
19  

22  

20  
    Provides common infrastructure for I/O objects that wrap kernel
23  
    Provides common infrastructure for I/O objects that wrap kernel
21  
    resources (sockets, timers, signal handlers, acceptors). Derived
24  
    resources (sockets, timers, signal handlers, acceptors). Derived
22  
    classes dispatch operations through a platform-specific vtable
25  
    classes dispatch operations through a platform-specific vtable
23  
    (IOCP, epoll, kqueue, io_uring).
26  
    (IOCP, epoll, kqueue, io_uring).
24  

27  

25  
    @par Semantics
28  
    @par Semantics
26  
    Only concrete platform I/O types should inherit from `io_object`.
29  
    Only concrete platform I/O types should inherit from `io_object`.
27  
    Test mocks, decorators, and stream adapters must not inherit from
30  
    Test mocks, decorators, and stream adapters must not inherit from
28  
    this class. Use concepts or templates for generic I/O algorithms.
31  
    this class. Use concepts or templates for generic I/O algorithms.
29  

32  

30  
    @par Thread Safety
33  
    @par Thread Safety
31  
    Distinct objects: Safe.
34  
    Distinct objects: Safe.
32  
    Shared objects: Unsafe. All operations on a single I/O object
35  
    Shared objects: Unsafe. All operations on a single I/O object
33  
    must be serialized.
36  
    must be serialized.
34  

37  

35 -
    @note Intended as a protected base class. The implementation
38 +
    @note Intended as a protected base class. The handle member
36 -
        pointer `impl_` is accessible to derived classes.
39 +
        `h_` is accessible to derived classes.
37  

40  

38  
    @see io_stream, tcp_socket, tcp_acceptor
41  
    @see io_stream, tcp_socket, tcp_acceptor
39  
*/
42  
*/
40  
class BOOST_COROSIO_DECL io_object
43  
class BOOST_COROSIO_DECL io_object
41  
{
44  
{
42 -
    /// Forward declaration for platform-specific implementation.
 
43 -
    struct implementation;
 
44 -

 
45  
public:
45  
public:
46  
    class handle;
46  
    class handle;
47  

47  

 
48 +
    /** Base interface for platform I/O implementations.
 
49 +

 
50 +
        Derived classes provide platform-specific operation dispatch.
 
51 +
    */
 
52 +
    struct implementation
 
53 +
    {
 
54 +
        virtual ~implementation() = default;
 
55 +
    };
 
56 +

48  
    /** Service interface for I/O object lifecycle management.
57  
    /** Service interface for I/O object lifecycle management.
49  

58  

50  
        Platform backends implement this interface to manage the
59  
        Platform backends implement this interface to manage the
51 -
        creation, opening, closing, and destruction of I/O object
60 +
        creation, closing, and destruction of I/O object
52  
        implementations.
61  
        implementations.
53  
    */
62  
    */
54  
    struct io_service
63  
    struct io_service
55  
    {
64  
    {
56 -
        /// Open the I/O object for use.
65 +
        virtual ~io_service() = default;
57 -
        virtual void open(handle&) = 0;
 
58  

66  

59 -
        /// Close the I/O object, releasing kernel resources.
67 +
        /// Construct a new implementation instance.
60 -
        virtual void close(handle&) = 0;
68 +
        virtual implementation* construct() = 0;
61  

69  

62 -
        /// Destroy the implementation, freeing memory.
70 +
        /// Destroy the implementation, closing kernel resources and freeing memory.
63  
        virtual void destroy(implementation*) = 0;
71  
        virtual void destroy(implementation*) = 0;
64  

72  

65 -
        /// Construct a new implementation instance.
73 +
        /// Close the I/O object, releasing kernel resources without deallocating.
66 -
        virtual implementation* construct() = 0;
74 +
        virtual void close(handle&) {}
67  
    };
75  
    };
68  

76  

69  
    /** RAII wrapper for I/O object implementation lifetime.
77  
    /** RAII wrapper for I/O object implementation lifetime.
70  

78  

71  
        Manages ownership of the platform-specific implementation,
79  
        Manages ownership of the platform-specific implementation,
72  
        automatically destroying it when the handle goes out of scope.
80  
        automatically destroying it when the handle goes out of scope.
73  
    */
81  
    */
74  
    class handle
82  
    class handle
75  
    {
83  
    {
76  
        capy::execution_context* ctx_ = nullptr;
84  
        capy::execution_context* ctx_ = nullptr;
77  
        io_service* svc_ = nullptr;
85  
        io_service* svc_ = nullptr;
78  
        implementation* impl_ = nullptr;
86  
        implementation* impl_ = nullptr;
79  

87  

80  
    public:
88  
    public:
81  
        /// Destroy the handle and its implementation.
89  
        /// Destroy the handle and its implementation.
82  
        ~handle()
90  
        ~handle()
83  
        {
91  
        {
84  
            if(impl_)
92  
            if(impl_)
 
93 +
            {
 
94 +
                svc_->close(*this);
85  
                svc_->destroy(impl_);
95  
                svc_->destroy(impl_);
 
96 +
            }
86  
        }
97  
        }
87  

98  

88  
        /// Construct an empty handle.
99  
        /// Construct an empty handle.
89  
        handle() = default;
100  
        handle() = default;
90  

101  

91  
        /// Construct a handle bound to a context and service.
102  
        /// Construct a handle bound to a context and service.
92  
        handle(
103  
        handle(
93  
            capy::execution_context& ctx,
104  
            capy::execution_context& ctx,
94  
            io_service& svc)
105  
            io_service& svc)
95  
            : ctx_(&ctx)
106  
            : ctx_(&ctx)
96  
            , svc_(&svc)
107  
            , svc_(&svc)
97  
            , impl_(svc_->construct())
108  
            , impl_(svc_->construct())
98  
        {
109  
        {
99  
        }
110  
        }
100  

111  

101  
        /// Move construct from another handle.
112  
        /// Move construct from another handle.
102 -
        handle(handle&& other)
113 +
        handle(handle&& other) noexcept
103  
            : ctx_(std::exchange(other.ctx_, nullptr))
114  
            : ctx_(std::exchange(other.ctx_, nullptr))
104  
            , svc_(std::exchange(other.svc_, nullptr))
115  
            , svc_(std::exchange(other.svc_, nullptr))
105  
            , impl_(std::exchange(other.impl_, nullptr))
116  
            , impl_(std::exchange(other.impl_, nullptr))
106  
        {
117  
        {
107  
        }
118  
        }
108  

119  

109  
        /// Move assign from another handle.
120  
        /// Move assign from another handle.
110  
        handle& operator=(handle&& other) noexcept
121  
        handle& operator=(handle&& other) noexcept
111  
        {
122  
        {
112 -
            ctx_ = std::exchange(other.ctx_, nullptr);
123 +
            if (this != &other)
113 -
            svc_ = std::exchange(other.svc_, nullptr);
124 +
            {
114 -
            impl_ = std::exchange(other.impl_, nullptr);
125 +
                if (impl_)
 
126 +
                {
 
127 +
                    svc_->close(*this);
 
128 +
                    svc_->destroy(impl_);
 
129 +
                }
 
130 +
                ctx_ = std::exchange(other.ctx_, nullptr);
 
131 +
                svc_ = std::exchange(other.svc_, nullptr);
 
132 +
                impl_ = std::exchange(other.impl_, nullptr);
 
133 +
            }
115  
            return *this;
134  
            return *this;
116  
        }
135  
        }
117  

136  

118 -
        /// Return the execution context.
137 +
        handle(handle const&) = delete;
119 -
        capy::execution_context& context() const noexcept
138 +
        handle& operator=(handle const&) = delete;
 
139 +

 
140 +
        /// Return true if the handle owns an implementation.
 
141 +
        explicit operator bool() const noexcept
120  
        {
142  
        {
121 -
            return *ctx_;
143 +
            return impl_ != nullptr;
122  
        }
144  
        }
123  

145  

124  
        /// Return the associated I/O service.
146  
        /// Return the associated I/O service.
125  
        io_service& service() const noexcept
147  
        io_service& service() const noexcept
126  
        {
148  
        {
127  
            return *svc_;
149  
            return *svc_;
128  
        }
150  
        }
129  

151  

130  
        /// Return the platform implementation.
152  
        /// Return the platform implementation.
131 -
        implementation& get() const noexcept
153 +
        implementation* get() const noexcept
132  
        {
154  
        {
133 -
            return *impl_;
155 +
            return impl_;
134 -
    };
 
135  
        }
156  
        }
136  

157  

137 -
    /** Base interface for platform I/O implementations.
158 +
        /** Replace the implementation, destroying the old one.
138  

159  

139 -
        Derived classes provide platform-specific operation dispatch.
160 +
            @param p The new implementation to own. May be nullptr.
140 -
    */
161 +
        */
141 -
    struct io_object_impl
162 +
        void reset(implementation* p) noexcept
142 -
    {
163 +
        {
143 -
        virtual ~io_object_impl() = default;
164 +
            if (impl_)
 
165 +
            {
 
166 +
                svc_->close(*this);
 
167 +
                svc_->destroy(impl_);
 
168 +
            }
 
169 +
            impl_ = p;
 
170 +
        }
144  

171  

145 -
        /// Release associated resources without closing.
172 +
        /// Return the execution context.
146 -
        virtual void release() = 0;
173 +
        capy::execution_context& context() const noexcept
 
174 +
        {
 
175 +
            return *ctx_;
 
176 +
        }
147  
    };
177  
    };
148  

178  

149  
    /// Return the execution context.
179  
    /// Return the execution context.
150  
    capy::execution_context&
180  
    capy::execution_context&
151  
    context() const noexcept
181  
    context() const noexcept
152  
    {
182  
    {
153 -
        return *ctx_;
183 +
        return h_.context();
154  
    }
184  
    }
155  

185  

156  
protected:
186  
protected:
157  
    virtual ~io_object() = default;
187  
    virtual ~io_object() = default;
158  

188  

159 -
    /// Construct an I/O object bound to the given context.
189 +
    /** Create a handle bound to a service found in the context.
 
190 +

 
191 +
        @tparam Service The service type whose key_type is used for lookup.
 
192 +
        @param ctx The execution context to search for the service.
 
193 +

 
194 +
        @return A handle owning a freshly constructed implementation.
 
195 +

 
196 +
        @throws std::logic_error if the service is not installed.
 
197 +
    */
 
198 +
    template<class Service>
 
199 +
    static handle create_handle(capy::execution_context& ctx)
 
200 +
    {
 
201 +
        auto* svc = ctx.find_service<Service>();
 
202 +
        if (!svc)
 
203 +
            detail::throw_logic_error(
 
204 +
                "io_object::create_handle: service not installed");
 
205 +
        return handle(ctx, *svc);
 
206 +
    }
 
207 +

 
208 +
    /// Construct an I/O object from a handle.
160  
    explicit
209  
    explicit
161 -
    io_object(
210 +
    io_object(handle h) noexcept
162 -
        capy::execution_context& ctx) noexcept
211 +
        : h_(std::move(h))
163 -
        : ctx_(&ctx)
 
164  
    {
212  
    {
165  
    }
213  
    }
166  

214  

167 -
    capy::execution_context* ctx_ = nullptr;
215 +
    /// Move construct from another I/O object.
168 -
    io_object_impl* impl_ = nullptr;
216 +
    io_object(io_object&& other) noexcept
 
217 +
        : h_(std::move(other.h_))
 
218 +
    {
 
219 +
    }
 
220 +

 
221 +
    /// Move assign from another I/O object.
 
222 +
    io_object& operator=(io_object&& other) noexcept
 
223 +
    {
 
224 +
        if (this != &other)
 
225 +
            h_ = std::move(other.h_);
 
226 +
        return *this;
 
227 +
    }
 
228 +

 
229 +
    io_object(io_object const&) = delete;
 
230 +
    io_object& operator=(io_object const&) = delete;
 
231 +

 
232 +
    handle h_;
169  
};
233  
};
170  

234  

171  
} // namespace boost::corosio
235  
} // namespace boost::corosio
172  

236  

173  
#endif
237  
#endif