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