buffer_registration.hpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. //
  2. // buffer_registration.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef BOOST_ASIO_BUFFER_REGISTRATION_HPP
  11. #define BOOST_ASIO_BUFFER_REGISTRATION_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <boost/asio/detail/config.hpp>
  16. #include <iterator>
  17. #include <utility>
  18. #include <vector>
  19. #include <boost/asio/detail/memory.hpp>
  20. #include <boost/asio/execution/context.hpp>
  21. #include <boost/asio/execution/executor.hpp>
  22. #include <boost/asio/execution_context.hpp>
  23. #include <boost/asio/is_executor.hpp>
  24. #include <boost/asio/query.hpp>
  25. #include <boost/asio/registered_buffer.hpp>
  26. #if defined(BOOST_ASIO_HAS_IO_URING)
  27. # include <boost/asio/detail/scheduler.hpp>
  28. # include <boost/asio/detail/io_uring_service.hpp>
  29. #endif // defined(BOOST_ASIO_HAS_IO_URING)
  30. #include <boost/asio/detail/push_options.hpp>
  31. namespace boost {
  32. namespace asio {
  33. namespace detail {
  34. class buffer_registration_base
  35. {
  36. protected:
  37. static mutable_registered_buffer make_buffer(const mutable_buffer& b,
  38. const void* scope, int index) noexcept
  39. {
  40. return mutable_registered_buffer(b, registered_buffer_id(scope, index));
  41. }
  42. };
  43. } // namespace detail
  44. /// Automatically registers and unregistered buffers with an execution context.
  45. /**
  46. * For portability, applications should assume that only one registration is
  47. * permitted per execution context.
  48. */
  49. template <typename MutableBufferSequence,
  50. typename Allocator = std::allocator<void>>
  51. class buffer_registration
  52. : detail::buffer_registration_base
  53. {
  54. public:
  55. /// The allocator type used for allocating storage for the buffers container.
  56. typedef Allocator allocator_type;
  57. #if defined(GENERATING_DOCUMENTATION)
  58. /// The type of an iterator over the registered buffers.
  59. typedef unspecified iterator;
  60. /// The type of a const iterator over the registered buffers.
  61. typedef unspecified const_iterator;
  62. #else // defined(GENERATING_DOCUMENTATION)
  63. typedef std::vector<mutable_registered_buffer>::const_iterator iterator;
  64. typedef std::vector<mutable_registered_buffer>::const_iterator const_iterator;
  65. #endif // defined(GENERATING_DOCUMENTATION)
  66. /// Register buffers with an executor's execution context.
  67. template <typename Executor>
  68. buffer_registration(const Executor& ex,
  69. const MutableBufferSequence& buffer_sequence,
  70. const allocator_type& alloc = allocator_type(),
  71. constraint_t<
  72. is_executor<Executor>::value || execution::is_executor<Executor>::value
  73. > = 0)
  74. : buffer_sequence_(buffer_sequence),
  75. buffers_(
  76. BOOST_ASIO_REBIND_ALLOC(allocator_type,
  77. mutable_registered_buffer)(alloc))
  78. {
  79. init_buffers(buffer_registration::get_context(ex),
  80. boost::asio::buffer_sequence_begin(buffer_sequence_),
  81. boost::asio::buffer_sequence_end(buffer_sequence_));
  82. }
  83. /// Register buffers with an execution context.
  84. template <typename ExecutionContext>
  85. buffer_registration(ExecutionContext& ctx,
  86. const MutableBufferSequence& buffer_sequence,
  87. const allocator_type& alloc = allocator_type(),
  88. constraint_t<
  89. is_convertible<ExecutionContext&, execution_context&>::value
  90. > = 0)
  91. : buffer_sequence_(buffer_sequence),
  92. buffers_(
  93. BOOST_ASIO_REBIND_ALLOC(allocator_type,
  94. mutable_registered_buffer)(alloc))
  95. {
  96. init_buffers(ctx,
  97. boost::asio::buffer_sequence_begin(buffer_sequence_),
  98. boost::asio::buffer_sequence_end(buffer_sequence_));
  99. }
  100. /// Move constructor.
  101. buffer_registration(buffer_registration&& other) noexcept
  102. : buffer_sequence_(std::move(other.buffer_sequence_)),
  103. buffers_(std::move(other.buffers_))
  104. {
  105. #if defined(BOOST_ASIO_HAS_IO_URING)
  106. service_ = other.service_;
  107. other.service_ = 0;
  108. #endif // defined(BOOST_ASIO_HAS_IO_URING)
  109. }
  110. /// Unregisters the buffers.
  111. ~buffer_registration()
  112. {
  113. #if defined(BOOST_ASIO_HAS_IO_URING)
  114. if (service_)
  115. service_->unregister_buffers();
  116. #endif // defined(BOOST_ASIO_HAS_IO_URING)
  117. }
  118. /// Move assignment.
  119. buffer_registration& operator=(buffer_registration&& other) noexcept
  120. {
  121. if (this != &other)
  122. {
  123. buffer_sequence_ = std::move(other.buffer_sequence_);
  124. buffers_ = std::move(other.buffers_);
  125. #if defined(BOOST_ASIO_HAS_IO_URING)
  126. if (service_)
  127. service_->unregister_buffers();
  128. service_ = other.service_;
  129. other.service_ = 0;
  130. #endif // defined(BOOST_ASIO_HAS_IO_URING)
  131. }
  132. return *this;
  133. }
  134. /// Get the number of registered buffers.
  135. std::size_t size() const noexcept
  136. {
  137. return buffers_.size();
  138. }
  139. /// Get the begin iterator for the sequence of registered buffers.
  140. const_iterator begin() const noexcept
  141. {
  142. return buffers_.begin();
  143. }
  144. /// Get the begin iterator for the sequence of registered buffers.
  145. const_iterator cbegin() const noexcept
  146. {
  147. return buffers_.cbegin();
  148. }
  149. /// Get the end iterator for the sequence of registered buffers.
  150. const_iterator end() const noexcept
  151. {
  152. return buffers_.end();
  153. }
  154. /// Get the end iterator for the sequence of registered buffers.
  155. const_iterator cend() const noexcept
  156. {
  157. return buffers_.cend();
  158. }
  159. /// Get the buffer at the specified index.
  160. const mutable_registered_buffer& operator[](std::size_t i) noexcept
  161. {
  162. return buffers_[i];
  163. }
  164. /// Get the buffer at the specified index.
  165. const mutable_registered_buffer& at(std::size_t i) noexcept
  166. {
  167. return buffers_.at(i);
  168. }
  169. private:
  170. // Disallow copying and assignment.
  171. buffer_registration(const buffer_registration&) = delete;
  172. buffer_registration& operator=(const buffer_registration&) = delete;
  173. // Helper function to get an executor's context.
  174. template <typename T>
  175. static execution_context& get_context(const T& t,
  176. enable_if_t<execution::is_executor<T>::value>* = 0)
  177. {
  178. return boost::asio::query(t, execution::context);
  179. }
  180. // Helper function to get an executor's context.
  181. template <typename T>
  182. static execution_context& get_context(const T& t,
  183. enable_if_t<!execution::is_executor<T>::value>* = 0)
  184. {
  185. return t.context();
  186. }
  187. // Helper function to initialise the container of buffers.
  188. template <typename Iterator>
  189. void init_buffers(execution_context& ctx, Iterator begin, Iterator end)
  190. {
  191. std::size_t n = std::distance(begin, end);
  192. buffers_.resize(n);
  193. #if defined(BOOST_ASIO_HAS_IO_URING)
  194. service_ = &use_service<detail::io_uring_service>(ctx);
  195. std::vector<iovec,
  196. BOOST_ASIO_REBIND_ALLOC(allocator_type, iovec)> iovecs(n,
  197. BOOST_ASIO_REBIND_ALLOC(allocator_type, iovec)(
  198. buffers_.get_allocator()));
  199. #endif // defined(BOOST_ASIO_HAS_IO_URING)
  200. Iterator iter = begin;
  201. for (int index = 0; iter != end; ++index, ++iter)
  202. {
  203. mutable_buffer b(*iter);
  204. std::size_t i = static_cast<std::size_t>(index);
  205. buffers_[i] = this->make_buffer(b, &ctx, index);
  206. #if defined(BOOST_ASIO_HAS_IO_URING)
  207. iovecs[i].iov_base = buffers_[i].data();
  208. iovecs[i].iov_len = buffers_[i].size();
  209. #endif // defined(BOOST_ASIO_HAS_IO_URING)
  210. }
  211. #if defined(BOOST_ASIO_HAS_IO_URING)
  212. if (n > 0)
  213. {
  214. service_->register_buffers(&iovecs[0],
  215. static_cast<unsigned>(iovecs.size()));
  216. }
  217. #endif // defined(BOOST_ASIO_HAS_IO_URING)
  218. }
  219. MutableBufferSequence buffer_sequence_;
  220. std::vector<mutable_registered_buffer,
  221. BOOST_ASIO_REBIND_ALLOC(allocator_type,
  222. mutable_registered_buffer)> buffers_;
  223. #if defined(BOOST_ASIO_HAS_IO_URING)
  224. detail::io_uring_service* service_;
  225. #endif // defined(BOOST_ASIO_HAS_IO_URING)
  226. };
  227. /// Register buffers with an execution context.
  228. template <typename Executor, typename MutableBufferSequence>
  229. BOOST_ASIO_NODISCARD inline
  230. buffer_registration<MutableBufferSequence>
  231. register_buffers(const Executor& ex,
  232. const MutableBufferSequence& buffer_sequence,
  233. constraint_t<
  234. is_executor<Executor>::value || execution::is_executor<Executor>::value
  235. > = 0)
  236. {
  237. return buffer_registration<MutableBufferSequence>(ex, buffer_sequence);
  238. }
  239. /// Register buffers with an execution context.
  240. template <typename Executor, typename MutableBufferSequence, typename Allocator>
  241. BOOST_ASIO_NODISCARD inline
  242. buffer_registration<MutableBufferSequence, Allocator>
  243. register_buffers(const Executor& ex,
  244. const MutableBufferSequence& buffer_sequence, const Allocator& alloc,
  245. constraint_t<
  246. is_executor<Executor>::value || execution::is_executor<Executor>::value
  247. > = 0)
  248. {
  249. return buffer_registration<MutableBufferSequence, Allocator>(
  250. ex, buffer_sequence, alloc);
  251. }
  252. /// Register buffers with an execution context.
  253. template <typename ExecutionContext, typename MutableBufferSequence>
  254. BOOST_ASIO_NODISCARD inline
  255. buffer_registration<MutableBufferSequence>
  256. register_buffers(ExecutionContext& ctx,
  257. const MutableBufferSequence& buffer_sequence,
  258. constraint_t<
  259. is_convertible<ExecutionContext&, execution_context&>::value
  260. > = 0)
  261. {
  262. return buffer_registration<MutableBufferSequence>(ctx, buffer_sequence);
  263. }
  264. /// Register buffers with an execution context.
  265. template <typename ExecutionContext,
  266. typename MutableBufferSequence, typename Allocator>
  267. BOOST_ASIO_NODISCARD inline
  268. buffer_registration<MutableBufferSequence, Allocator>
  269. register_buffers(ExecutionContext& ctx,
  270. const MutableBufferSequence& buffer_sequence, const Allocator& alloc,
  271. constraint_t<
  272. is_convertible<ExecutionContext&, execution_context&>::value
  273. > = 0)
  274. {
  275. return buffer_registration<MutableBufferSequence, Allocator>(
  276. ctx, buffer_sequence, alloc);
  277. }
  278. } // namespace asio
  279. } // namespace boost
  280. #include <boost/asio/detail/pop_options.hpp>
  281. #endif // BOOST_ASIO_BUFFER_REGISTRATION_HPP