// // buffer_registration.hpp // ~~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef ASIO_BUFFER_REGISTRATION_HPP #define ASIO_BUFFER_REGISTRATION_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include #include #include "asio/detail/memory.hpp" #include "asio/execution/context.hpp" #include "asio/execution/executor.hpp" #include "asio/execution_context.hpp" #include "asio/is_executor.hpp" #include "asio/query.hpp" #include "asio/registered_buffer.hpp" #if defined(ASIO_HAS_IO_URING) # include "asio/detail/scheduler.hpp" # include "asio/detail/io_uring_service.hpp" #endif // defined(ASIO_HAS_IO_URING) #include "asio/detail/push_options.hpp" namespace asio { namespace detail { class buffer_registration_base { protected: static mutable_registered_buffer make_buffer(const mutable_buffer& b, const void* scope, int index) noexcept { return mutable_registered_buffer(b, registered_buffer_id(scope, index)); } }; } // namespace detail /// Automatically registers and unregistered buffers with an execution context. /** * For portability, applications should assume that only one registration is * permitted per execution context. */ template > class buffer_registration : detail::buffer_registration_base { public: /// The allocator type used for allocating storage for the buffers container. typedef Allocator allocator_type; #if defined(GENERATING_DOCUMENTATION) /// The type of an iterator over the registered buffers. typedef unspecified iterator; /// The type of a const iterator over the registered buffers. typedef unspecified const_iterator; #else // defined(GENERATING_DOCUMENTATION) typedef std::vector::const_iterator iterator; typedef std::vector::const_iterator const_iterator; #endif // defined(GENERATING_DOCUMENTATION) /// Register buffers with an executor's execution context. template buffer_registration(const Executor& ex, const MutableBufferSequence& buffer_sequence, const allocator_type& alloc = allocator_type(), constraint_t< is_executor::value || execution::is_executor::value > = 0) : buffer_sequence_(buffer_sequence), buffers_( ASIO_REBIND_ALLOC(allocator_type, mutable_registered_buffer)(alloc)) { init_buffers(buffer_registration::get_context(ex), asio::buffer_sequence_begin(buffer_sequence_), asio::buffer_sequence_end(buffer_sequence_)); } /// Register buffers with an execution context. template buffer_registration(ExecutionContext& ctx, const MutableBufferSequence& buffer_sequence, const allocator_type& alloc = allocator_type(), constraint_t< is_convertible::value > = 0) : buffer_sequence_(buffer_sequence), buffers_( ASIO_REBIND_ALLOC(allocator_type, mutable_registered_buffer)(alloc)) { init_buffers(ctx, asio::buffer_sequence_begin(buffer_sequence_), asio::buffer_sequence_end(buffer_sequence_)); } /// Move constructor. buffer_registration(buffer_registration&& other) noexcept : buffer_sequence_(std::move(other.buffer_sequence_)), buffers_(std::move(other.buffers_)) { #if defined(ASIO_HAS_IO_URING) service_ = other.service_; other.service_ = 0; #endif // defined(ASIO_HAS_IO_URING) } /// Unregisters the buffers. ~buffer_registration() { #if defined(ASIO_HAS_IO_URING) if (service_) service_->unregister_buffers(); #endif // defined(ASIO_HAS_IO_URING) } /// Move assignment. buffer_registration& operator=(buffer_registration&& other) noexcept { if (this != &other) { buffer_sequence_ = std::move(other.buffer_sequence_); buffers_ = std::move(other.buffers_); #if defined(ASIO_HAS_IO_URING) if (service_) service_->unregister_buffers(); service_ = other.service_; other.service_ = 0; #endif // defined(ASIO_HAS_IO_URING) } return *this; } /// Get the number of registered buffers. std::size_t size() const noexcept { return buffers_.size(); } /// Get the begin iterator for the sequence of registered buffers. const_iterator begin() const noexcept { return buffers_.begin(); } /// Get the begin iterator for the sequence of registered buffers. const_iterator cbegin() const noexcept { return buffers_.cbegin(); } /// Get the end iterator for the sequence of registered buffers. const_iterator end() const noexcept { return buffers_.end(); } /// Get the end iterator for the sequence of registered buffers. const_iterator cend() const noexcept { return buffers_.cend(); } /// Get the buffer at the specified index. const mutable_registered_buffer& operator[](std::size_t i) noexcept { return buffers_[i]; } /// Get the buffer at the specified index. const mutable_registered_buffer& at(std::size_t i) noexcept { return buffers_.at(i); } private: // Disallow copying and assignment. buffer_registration(const buffer_registration&) = delete; buffer_registration& operator=(const buffer_registration&) = delete; // Helper function to get an executor's context. template static execution_context& get_context(const T& t, enable_if_t::value>* = 0) { return asio::query(t, execution::context); } // Helper function to get an executor's context. template static execution_context& get_context(const T& t, enable_if_t::value>* = 0) { return t.context(); } // Helper function to initialise the container of buffers. template void init_buffers(execution_context& ctx, Iterator begin, Iterator end) { std::size_t n = std::distance(begin, end); buffers_.resize(n); #if defined(ASIO_HAS_IO_URING) service_ = &use_service(ctx); std::vector iovecs(n, ASIO_REBIND_ALLOC(allocator_type, iovec)( buffers_.get_allocator())); #endif // defined(ASIO_HAS_IO_URING) Iterator iter = begin; for (int index = 0; iter != end; ++index, ++iter) { mutable_buffer b(*iter); std::size_t i = static_cast(index); buffers_[i] = this->make_buffer(b, &ctx, index); #if defined(ASIO_HAS_IO_URING) iovecs[i].iov_base = buffers_[i].data(); iovecs[i].iov_len = buffers_[i].size(); #endif // defined(ASIO_HAS_IO_URING) } #if defined(ASIO_HAS_IO_URING) if (n > 0) { service_->register_buffers(&iovecs[0], static_cast(iovecs.size())); } #endif // defined(ASIO_HAS_IO_URING) } MutableBufferSequence buffer_sequence_; std::vector buffers_; #if defined(ASIO_HAS_IO_URING) detail::io_uring_service* service_; #endif // defined(ASIO_HAS_IO_URING) }; /// Register buffers with an execution context. template ASIO_NODISCARD inline buffer_registration register_buffers(const Executor& ex, const MutableBufferSequence& buffer_sequence, constraint_t< is_executor::value || execution::is_executor::value > = 0) { return buffer_registration(ex, buffer_sequence); } /// Register buffers with an execution context. template ASIO_NODISCARD inline buffer_registration register_buffers(const Executor& ex, const MutableBufferSequence& buffer_sequence, const Allocator& alloc, constraint_t< is_executor::value || execution::is_executor::value > = 0) { return buffer_registration( ex, buffer_sequence, alloc); } /// Register buffers with an execution context. template ASIO_NODISCARD inline buffer_registration register_buffers(ExecutionContext& ctx, const MutableBufferSequence& buffer_sequence, constraint_t< is_convertible::value > = 0) { return buffer_registration(ctx, buffer_sequence); } /// Register buffers with an execution context. template ASIO_NODISCARD inline buffer_registration register_buffers(ExecutionContext& ctx, const MutableBufferSequence& buffer_sequence, const Allocator& alloc, constraint_t< is_convertible::value > = 0) { return buffer_registration( ctx, buffer_sequence, alloc); } } // namespace asio #include "asio/detail/pop_options.hpp" #endif // ASIO_BUFFER_REGISTRATION_HPP