1 | // |
2 | // windows/basic_random_access_handle.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 | |
11 | #ifndef BOOST_ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP |
12 | #define BOOST_ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP |
13 | |
14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
15 | # pragma once |
16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
17 | |
18 | #include <boost/asio/detail/config.hpp> |
19 | #include <boost/asio/windows/basic_overlapped_handle.hpp> |
20 | |
21 | #if defined(BOOST_ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) \ |
22 | || defined(GENERATING_DOCUMENTATION) |
23 | |
24 | #include <boost/asio/detail/push_options.hpp> |
25 | |
26 | namespace boost { |
27 | namespace asio { |
28 | namespace windows { |
29 | |
30 | /// Provides random-access handle functionality. |
31 | /** |
32 | * The windows::basic_random_access_handle class provides asynchronous and |
33 | * blocking random-access handle functionality. |
34 | * |
35 | * @par Thread Safety |
36 | * @e Distinct @e objects: Safe.@n |
37 | * @e Shared @e objects: Unsafe. |
38 | */ |
39 | template <typename Executor = any_io_executor> |
40 | class basic_random_access_handle |
41 | : public basic_overlapped_handle<Executor> |
42 | { |
43 | private: |
44 | class initiate_async_write_some_at; |
45 | class initiate_async_read_some_at; |
46 | |
47 | public: |
48 | /// The type of the executor associated with the object. |
49 | typedef Executor executor_type; |
50 | |
51 | /// Rebinds the handle type to another executor. |
52 | template <typename Executor1> |
53 | struct rebind_executor |
54 | { |
55 | /// The handle type when rebound to the specified executor. |
56 | typedef basic_random_access_handle<Executor1> other; |
57 | }; |
58 | |
59 | /// The native representation of a handle. |
60 | #if defined(GENERATING_DOCUMENTATION) |
61 | typedef implementation_defined native_handle_type; |
62 | #else |
63 | typedef boost::asio::detail::win_iocp_handle_service::native_handle_type |
64 | native_handle_type; |
65 | #endif |
66 | |
67 | /// Construct a random-access handle without opening it. |
68 | /** |
69 | * This constructor creates a random-access handle without opening it. |
70 | * |
71 | * @param ex The I/O executor that the random-access handle will use, by |
72 | * default, to dispatch handlers for any asynchronous operations performed on |
73 | * the random-access handle. |
74 | */ |
75 | explicit basic_random_access_handle(const executor_type& ex) |
76 | : basic_overlapped_handle<Executor>(ex) |
77 | { |
78 | } |
79 | |
80 | /// Construct a random-access handle without opening it. |
81 | /** |
82 | * This constructor creates a random-access handle without opening it. The |
83 | * handle needs to be opened or assigned before data can be written to or read |
84 | * from it. |
85 | * |
86 | * @param context An execution context which provides the I/O executor that |
87 | * the random-access handle will use, by default, to dispatch handlers for any |
88 | * asynchronous operations performed on the random-access handle. |
89 | */ |
90 | template <typename ExecutionContext> |
91 | explicit basic_random_access_handle(ExecutionContext& context, |
92 | constraint_t< |
93 | is_convertible<ExecutionContext&, execution_context&>::value, |
94 | defaulted_constraint |
95 | > = defaulted_constraint()) |
96 | : basic_overlapped_handle<Executor>(context) |
97 | { |
98 | } |
99 | |
100 | /// Construct a random-access handle on an existing native handle. |
101 | /** |
102 | * This constructor creates a random-access handle object to hold an existing |
103 | * native handle. |
104 | * |
105 | * @param ex The I/O executor that the random-access handle will use, by |
106 | * default, to dispatch handlers for any asynchronous operations performed on |
107 | * the random-access handle. |
108 | * |
109 | * @param handle The new underlying handle implementation. |
110 | * |
111 | * @throws boost::system::system_error Thrown on failure. |
112 | */ |
113 | basic_random_access_handle(const executor_type& ex, |
114 | const native_handle_type& handle) |
115 | : basic_overlapped_handle<Executor>(ex, handle) |
116 | { |
117 | } |
118 | |
119 | /// Construct a random-access handle on an existing native handle. |
120 | /** |
121 | * This constructor creates a random-access handle object to hold an existing |
122 | * native handle. |
123 | * |
124 | * @param context An execution context which provides the I/O executor that |
125 | * the random-access handle will use, by default, to dispatch handlers for any |
126 | * asynchronous operations performed on the random-access handle. |
127 | * |
128 | * @param handle The new underlying handle implementation. |
129 | * |
130 | * @throws boost::system::system_error Thrown on failure. |
131 | */ |
132 | template <typename ExecutionContext> |
133 | basic_random_access_handle(ExecutionContext& context, |
134 | const native_handle_type& handle, |
135 | constraint_t< |
136 | is_convertible<ExecutionContext&, execution_context&>::value |
137 | > = 0) |
138 | : basic_overlapped_handle<Executor>(context, handle) |
139 | { |
140 | } |
141 | |
142 | /// Move-construct a random-access handle from another. |
143 | /** |
144 | * This constructor moves a random-access handle from one object to another. |
145 | * |
146 | * @param other The other random-access handle object from which the |
147 | * move will occur. |
148 | * |
149 | * @note Following the move, the moved-from object is in the same state as if |
150 | * constructed using the @c basic_random_access_handle(const executor_type&) |
151 | * constructor. |
152 | */ |
153 | basic_random_access_handle(basic_random_access_handle&& other) |
154 | : basic_overlapped_handle<Executor>(std::move(other)) |
155 | { |
156 | } |
157 | |
158 | /// Move-assign a random-access handle from another. |
159 | /** |
160 | * This assignment operator moves a random-access handle from one object to |
161 | * another. |
162 | * |
163 | * @param other The other random-access handle object from which the |
164 | * move will occur. |
165 | * |
166 | * @note Following the move, the moved-from object is in the same state as if |
167 | * constructed using the @c basic_random_access_handle(const executor_type&) |
168 | * constructor. |
169 | */ |
170 | basic_random_access_handle& operator=(basic_random_access_handle&& other) |
171 | { |
172 | basic_overlapped_handle<Executor>::operator=(std::move(other)); |
173 | return *this; |
174 | } |
175 | |
176 | /// Move-construct a random-access handle from a handle of another executor |
177 | /// type. |
178 | /** |
179 | * This constructor moves a random-access handle from one object to another. |
180 | * |
181 | * @param other The other random-access handle object from which the |
182 | * move will occur. |
183 | * |
184 | * @note Following the move, the moved-from object is in the same state as if |
185 | * constructed using the @c basic_random_access_handle(const executor_type&) |
186 | * constructor. |
187 | */ |
188 | template<typename Executor1> |
189 | basic_random_access_handle(basic_random_access_handle<Executor1>&& other, |
190 | constraint_t< |
191 | is_convertible<Executor1, Executor>::value, |
192 | defaulted_constraint |
193 | > = defaulted_constraint()) |
194 | : basic_overlapped_handle<Executor>(std::move(other)) |
195 | { |
196 | } |
197 | |
198 | /// Move-assign a random-access handle from a handle of another executor |
199 | /// type. |
200 | /** |
201 | * This assignment operator moves a random-access handle from one object to |
202 | * another. |
203 | * |
204 | * @param other The other random-access handle object from which the |
205 | * move will occur. |
206 | * |
207 | * @note Following the move, the moved-from object is in the same state as if |
208 | * constructed using the @c basic_random_access_handle(const executor_type&) |
209 | * constructor. |
210 | */ |
211 | template<typename Executor1> |
212 | constraint_t< |
213 | is_convertible<Executor1, Executor>::value, |
214 | basic_random_access_handle& |
215 | > operator=(basic_random_access_handle<Executor1>&& other) |
216 | { |
217 | basic_overlapped_handle<Executor>::operator=(std::move(other)); |
218 | return *this; |
219 | } |
220 | |
221 | /// Write some data to the handle at the specified offset. |
222 | /** |
223 | * This function is used to write data to the random-access handle. The |
224 | * function call will block until one or more bytes of the data has been |
225 | * written successfully, or until an error occurs. |
226 | * |
227 | * @param offset The offset at which the data will be written. |
228 | * |
229 | * @param buffers One or more data buffers to be written to the handle. |
230 | * |
231 | * @returns The number of bytes written. |
232 | * |
233 | * @throws boost::system::system_error Thrown on failure. An error code of |
234 | * boost::asio::error::eof indicates that the connection was closed by the |
235 | * peer. |
236 | * |
237 | * @note The write_some_at operation may not write all of the data. Consider |
238 | * using the @ref write_at function if you need to ensure that all data is |
239 | * written before the blocking operation completes. |
240 | * |
241 | * @par Example |
242 | * To write a single data buffer use the @ref buffer function as follows: |
243 | * @code |
244 | * handle.write_some_at(42, boost::asio::buffer(data, size)); |
245 | * @endcode |
246 | * See the @ref buffer documentation for information on writing multiple |
247 | * buffers in one go, and how to use it with arrays, boost::array or |
248 | * std::vector. |
249 | */ |
250 | template <typename ConstBufferSequence> |
251 | std::size_t write_some_at(uint64_t offset, |
252 | const ConstBufferSequence& buffers) |
253 | { |
254 | boost::system::error_code ec; |
255 | std::size_t s = this->impl_.get_service().write_some_at( |
256 | this->impl_.get_implementation(), offset, buffers, ec); |
257 | boost::asio::detail::throw_error(ec, "write_some_at" ); |
258 | return s; |
259 | } |
260 | |
261 | /// Write some data to the handle at the specified offset. |
262 | /** |
263 | * This function is used to write data to the random-access handle. The |
264 | * function call will block until one or more bytes of the data has been |
265 | * written successfully, or until an error occurs. |
266 | * |
267 | * @param offset The offset at which the data will be written. |
268 | * |
269 | * @param buffers One or more data buffers to be written to the handle. |
270 | * |
271 | * @param ec Set to indicate what error occurred, if any. |
272 | * |
273 | * @returns The number of bytes written. Returns 0 if an error occurred. |
274 | * |
275 | * @note The write_some operation may not transmit all of the data to the |
276 | * peer. Consider using the @ref write_at function if you need to ensure that |
277 | * all data is written before the blocking operation completes. |
278 | */ |
279 | template <typename ConstBufferSequence> |
280 | std::size_t write_some_at(uint64_t offset, |
281 | const ConstBufferSequence& buffers, boost::system::error_code& ec) |
282 | { |
283 | return this->impl_.get_service().write_some_at( |
284 | this->impl_.get_implementation(), offset, buffers, ec); |
285 | } |
286 | |
287 | /// Start an asynchronous write at the specified offset. |
288 | /** |
289 | * This function is used to asynchronously write data to the random-access |
290 | * handle. It is an initiating function for an @ref asynchronous_operation, |
291 | * and always returns immediately. |
292 | * |
293 | * @param offset The offset at which the data will be written. |
294 | * |
295 | * @param buffers One or more data buffers to be written to the handle. |
296 | * Although the buffers object may be copied as necessary, ownership of the |
297 | * underlying memory blocks is retained by the caller, which must guarantee |
298 | * that they remain valid until the completion handler is called. |
299 | * |
300 | * @param token The @ref completion_token that will be used to produce a |
301 | * completion handler, which will be called when the write completes. |
302 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
303 | * @ref yield_context, or a function object with the correct completion |
304 | * signature. The function signature of the completion handler must be: |
305 | * @code void handler( |
306 | * const boost::system::error_code& error, // Result of operation. |
307 | * std::size_t bytes_transferred // Number of bytes written. |
308 | * ); @endcode |
309 | * Regardless of whether the asynchronous operation completes immediately or |
310 | * not, the completion handler will not be invoked from within this function. |
311 | * On immediate completion, invocation of the handler will be performed in a |
312 | * manner equivalent to using boost::asio::post(). |
313 | * |
314 | * @par Completion Signature |
315 | * @code void(boost::system::error_code, std::size_t) @endcode |
316 | * |
317 | * @note The write operation may not transmit all of the data to the peer. |
318 | * Consider using the @ref async_write_at function if you need to ensure that |
319 | * all data is written before the asynchronous operation completes. |
320 | * |
321 | * @par Example |
322 | * To write a single data buffer use the @ref buffer function as follows: |
323 | * @code |
324 | * handle.async_write_some_at(42, boost::asio::buffer(data, size), handler); |
325 | * @endcode |
326 | * See the @ref buffer documentation for information on writing multiple |
327 | * buffers in one go, and how to use it with arrays, boost::array or |
328 | * std::vector. |
329 | * |
330 | * @par Per-Operation Cancellation |
331 | * This asynchronous operation supports cancellation for the following |
332 | * boost::asio::cancellation_type values: |
333 | * |
334 | * @li @c cancellation_type::terminal |
335 | * |
336 | * @li @c cancellation_type::partial |
337 | * |
338 | * @li @c cancellation_type::total |
339 | */ |
340 | template <typename ConstBufferSequence, |
341 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, |
342 | std::size_t)) WriteToken = default_completion_token_t<executor_type>> |
343 | auto async_write_some_at(uint64_t offset, const ConstBufferSequence& buffers, |
344 | WriteToken&& token = default_completion_token_t<executor_type>()) |
345 | -> decltype( |
346 | async_initiate<WriteToken, |
347 | void (boost::system::error_code, std::size_t)>( |
348 | declval<initiate_async_write_some_at>(), token, offset, buffers)) |
349 | { |
350 | return async_initiate<WriteToken, |
351 | void (boost::system::error_code, std::size_t)>( |
352 | initiate_async_write_some_at(this), token, offset, buffers); |
353 | } |
354 | |
355 | /// Read some data from the handle at the specified offset. |
356 | /** |
357 | * This function is used to read data from the random-access handle. The |
358 | * function call will block until one or more bytes of data has been read |
359 | * successfully, or until an error occurs. |
360 | * |
361 | * @param offset The offset at which the data will be read. |
362 | * |
363 | * @param buffers One or more buffers into which the data will be read. |
364 | * |
365 | * @returns The number of bytes read. |
366 | * |
367 | * @throws boost::system::system_error Thrown on failure. An error code of |
368 | * boost::asio::error::eof indicates that the connection was closed by the |
369 | * peer. |
370 | * |
371 | * @note The read_some operation may not read all of the requested number of |
372 | * bytes. Consider using the @ref read_at function if you need to ensure that |
373 | * the requested amount of data is read before the blocking operation |
374 | * completes. |
375 | * |
376 | * @par Example |
377 | * To read into a single data buffer use the @ref buffer function as follows: |
378 | * @code |
379 | * handle.read_some_at(42, boost::asio::buffer(data, size)); |
380 | * @endcode |
381 | * See the @ref buffer documentation for information on reading into multiple |
382 | * buffers in one go, and how to use it with arrays, boost::array or |
383 | * std::vector. |
384 | */ |
385 | template <typename MutableBufferSequence> |
386 | std::size_t read_some_at(uint64_t offset, |
387 | const MutableBufferSequence& buffers) |
388 | { |
389 | boost::system::error_code ec; |
390 | std::size_t s = this->impl_.get_service().read_some_at( |
391 | this->impl_.get_implementation(), offset, buffers, ec); |
392 | boost::asio::detail::throw_error(ec, "read_some_at" ); |
393 | return s; |
394 | } |
395 | |
396 | /// Read some data from the handle at the specified offset. |
397 | /** |
398 | * This function is used to read data from the random-access handle. The |
399 | * function call will block until one or more bytes of data has been read |
400 | * successfully, or until an error occurs. |
401 | * |
402 | * @param offset The offset at which the data will be read. |
403 | * |
404 | * @param buffers One or more buffers into which the data will be read. |
405 | * |
406 | * @param ec Set to indicate what error occurred, if any. |
407 | * |
408 | * @returns The number of bytes read. Returns 0 if an error occurred. |
409 | * |
410 | * @note The read_some operation may not read all of the requested number of |
411 | * bytes. Consider using the @ref read_at function if you need to ensure that |
412 | * the requested amount of data is read before the blocking operation |
413 | * completes. |
414 | */ |
415 | template <typename MutableBufferSequence> |
416 | std::size_t read_some_at(uint64_t offset, |
417 | const MutableBufferSequence& buffers, boost::system::error_code& ec) |
418 | { |
419 | return this->impl_.get_service().read_some_at( |
420 | this->impl_.get_implementation(), offset, buffers, ec); |
421 | } |
422 | |
423 | /// Start an asynchronous read at the specified offset. |
424 | /** |
425 | * This function is used to asynchronously read data from the random-access |
426 | * handle. It is an initiating function for an @ref asynchronous_operation, |
427 | * and always returns immediately. |
428 | * |
429 | * @param offset The offset at which the data will be read. |
430 | * |
431 | * @param buffers One or more buffers into which the data will be read. |
432 | * Although the buffers object may be copied as necessary, ownership of the |
433 | * underlying memory blocks is retained by the caller, which must guarantee |
434 | * that they remain valid until the completion handler is called. |
435 | * |
436 | * @param token The @ref completion_token that will be used to produce a |
437 | * completion handler, which will be called when the read completes. |
438 | * Potential completion tokens include @ref use_future, @ref use_awaitable, |
439 | * @ref yield_context, or a function object with the correct completion |
440 | * signature. The function signature of the completion handler must be: |
441 | * @code void handler( |
442 | * const boost::system::error_code& error, // Result of operation. |
443 | * std::size_t bytes_transferred // Number of bytes read. |
444 | * ); @endcode |
445 | * Regardless of whether the asynchronous operation completes immediately or |
446 | * not, the completion handler will not be invoked from within this function. |
447 | * On immediate completion, invocation of the handler will be performed in a |
448 | * manner equivalent to using boost::asio::post(). |
449 | * |
450 | * @par Completion Signature |
451 | * @code void(boost::system::error_code, std::size_t) @endcode |
452 | * |
453 | * @note The read operation may not read all of the requested number of bytes. |
454 | * Consider using the @ref async_read_at function if you need to ensure that |
455 | * the requested amount of data is read before the asynchronous operation |
456 | * completes. |
457 | * |
458 | * @par Example |
459 | * To read into a single data buffer use the @ref buffer function as follows: |
460 | * @code |
461 | * handle.async_read_some_at(42, boost::asio::buffer(data, size), handler); |
462 | * @endcode |
463 | * See the @ref buffer documentation for information on reading into multiple |
464 | * buffers in one go, and how to use it with arrays, boost::array or |
465 | * std::vector. |
466 | * |
467 | * @par Per-Operation Cancellation |
468 | * This asynchronous operation supports cancellation for the following |
469 | * boost::asio::cancellation_type values: |
470 | * |
471 | * @li @c cancellation_type::terminal |
472 | * |
473 | * @li @c cancellation_type::partial |
474 | * |
475 | * @li @c cancellation_type::total |
476 | */ |
477 | template <typename MutableBufferSequence, |
478 | BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, |
479 | std::size_t)) ReadToken = default_completion_token_t<executor_type>> |
480 | auto async_read_some_at(uint64_t offset, const MutableBufferSequence& buffers, |
481 | ReadToken&& token = default_completion_token_t<executor_type>()) |
482 | -> decltype( |
483 | async_initiate<ReadToken, |
484 | void (boost::system::error_code, std::size_t)>( |
485 | declval<initiate_async_read_some_at>(), token, offset, buffers)) |
486 | { |
487 | return async_initiate<ReadToken, |
488 | void (boost::system::error_code, std::size_t)>( |
489 | initiate_async_read_some_at(this), token, offset, buffers); |
490 | } |
491 | |
492 | private: |
493 | class initiate_async_write_some_at |
494 | { |
495 | public: |
496 | typedef Executor executor_type; |
497 | |
498 | explicit initiate_async_write_some_at(basic_random_access_handle* self) |
499 | : self_(self) |
500 | { |
501 | } |
502 | |
503 | const executor_type& get_executor() const noexcept |
504 | { |
505 | return self_->get_executor(); |
506 | } |
507 | |
508 | template <typename WriteHandler, typename ConstBufferSequence> |
509 | void operator()(WriteHandler&& handler, |
510 | uint64_t offset, const ConstBufferSequence& buffers) const |
511 | { |
512 | // If you get an error on the following line it means that your handler |
513 | // does not meet the documented type requirements for a WriteHandler. |
514 | BOOST_ASIO_WRITE_HANDLER_CHECK(WriteHandler, handler) type_check; |
515 | |
516 | detail::non_const_lvalue<WriteHandler> handler2(handler); |
517 | self_->impl_.get_service().async_write_some_at( |
518 | self_->impl_.get_implementation(), offset, buffers, |
519 | handler2.value, self_->impl_.get_executor()); |
520 | } |
521 | |
522 | private: |
523 | basic_random_access_handle* self_; |
524 | }; |
525 | |
526 | class initiate_async_read_some_at |
527 | { |
528 | public: |
529 | typedef Executor executor_type; |
530 | |
531 | explicit initiate_async_read_some_at(basic_random_access_handle* self) |
532 | : self_(self) |
533 | { |
534 | } |
535 | |
536 | const executor_type& get_executor() const noexcept |
537 | { |
538 | return self_->get_executor(); |
539 | } |
540 | |
541 | template <typename ReadHandler, typename MutableBufferSequence> |
542 | void operator()(ReadHandler&& handler, |
543 | uint64_t offset, const MutableBufferSequence& buffers) const |
544 | { |
545 | // If you get an error on the following line it means that your handler |
546 | // does not meet the documented type requirements for a ReadHandler. |
547 | BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check; |
548 | |
549 | detail::non_const_lvalue<ReadHandler> handler2(handler); |
550 | self_->impl_.get_service().async_read_some_at( |
551 | self_->impl_.get_implementation(), offset, buffers, |
552 | handler2.value, self_->impl_.get_executor()); |
553 | } |
554 | |
555 | private: |
556 | basic_random_access_handle* self_; |
557 | }; |
558 | }; |
559 | |
560 | } // namespace windows |
561 | } // namespace asio |
562 | } // namespace boost |
563 | |
564 | #include <boost/asio/detail/pop_options.hpp> |
565 | |
566 | #endif // defined(BOOST_ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE) |
567 | // || defined(GENERATING_DOCUMENTATION) |
568 | |
569 | #endif // BOOST_ASIO_WINDOWS_BASIC_RANDOM_ACCESS_HANDLE_HPP |
570 | |