buffer_registration.hpp 9.4 KB

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