1//
2// basic_readable_pipe.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_BASIC_READABLE_PIPE_HPP
12#define BOOST_ASIO_BASIC_READABLE_PIPE_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
20#if defined(BOOST_ASIO_HAS_PIPE) \
21 || defined(GENERATING_DOCUMENTATION)
22
23#include <string>
24#include <utility>
25#include <boost/asio/any_io_executor.hpp>
26#include <boost/asio/async_result.hpp>
27#include <boost/asio/detail/handler_type_requirements.hpp>
28#include <boost/asio/detail/io_object_impl.hpp>
29#include <boost/asio/detail/non_const_lvalue.hpp>
30#include <boost/asio/detail/throw_error.hpp>
31#include <boost/asio/detail/type_traits.hpp>
32#include <boost/asio/error.hpp>
33#include <boost/asio/execution_context.hpp>
34#if defined(BOOST_ASIO_HAS_IOCP)
35# include <boost/asio/detail/win_iocp_handle_service.hpp>
36#elif defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT)
37# include <boost/asio/detail/io_uring_descriptor_service.hpp>
38#else
39# include <boost/asio/detail/reactive_descriptor_service.hpp>
40#endif
41
42#include <boost/asio/detail/push_options.hpp>
43
44namespace boost {
45namespace asio {
46
47/// Provides pipe functionality.
48/**
49 * The basic_readable_pipe class provides a wrapper over pipe
50 * functionality.
51 *
52 * @par Thread Safety
53 * @e Distinct @e objects: Safe.@n
54 * @e Shared @e objects: Unsafe.
55 */
56template <typename Executor = any_io_executor>
57class basic_readable_pipe
58{
59private:
60 class initiate_async_read_some;
61
62public:
63 /// The type of the executor associated with the object.
64 typedef Executor executor_type;
65
66 /// Rebinds the pipe type to another executor.
67 template <typename Executor1>
68 struct rebind_executor
69 {
70 /// The pipe type when rebound to the specified executor.
71 typedef basic_readable_pipe<Executor1> other;
72 };
73
74 /// The native representation of a pipe.
75#if defined(GENERATING_DOCUMENTATION)
76 typedef implementation_defined native_handle_type;
77#elif defined(BOOST_ASIO_HAS_IOCP)
78 typedef detail::win_iocp_handle_service::native_handle_type
79 native_handle_type;
80#elif defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT)
81 typedef detail::io_uring_descriptor_service::native_handle_type
82 native_handle_type;
83#else
84 typedef detail::reactive_descriptor_service::native_handle_type
85 native_handle_type;
86#endif
87
88 /// A basic_readable_pipe is always the lowest layer.
89 typedef basic_readable_pipe lowest_layer_type;
90
91 /// Construct a basic_readable_pipe without opening it.
92 /**
93 * This constructor creates a pipe without opening it.
94 *
95 * @param ex The I/O executor that the pipe will use, by default, to dispatch
96 * handlers for any asynchronous operations performed on the pipe.
97 */
98 explicit basic_readable_pipe(const executor_type& ex)
99 : impl_(0, ex)
100 {
101 }
102
103 /// Construct a basic_readable_pipe without opening it.
104 /**
105 * This constructor creates a pipe without opening it.
106 *
107 * @param context An execution context which provides the I/O executor that
108 * the pipe will use, by default, to dispatch handlers for any asynchronous
109 * operations performed on the pipe.
110 */
111 template <typename ExecutionContext>
112 explicit basic_readable_pipe(ExecutionContext& context,
113 constraint_t<
114 is_convertible<ExecutionContext&, execution_context&>::value,
115 defaulted_constraint
116 > = defaulted_constraint())
117 : impl_(0, 0, context)
118 {
119 }
120
121 /// Construct a basic_readable_pipe on an existing native pipe.
122 /**
123 * This constructor creates a pipe object to hold an existing native
124 * pipe.
125 *
126 * @param ex The I/O executor that the pipe will use, by default, to
127 * dispatch handlers for any asynchronous operations performed on the
128 * pipe.
129 *
130 * @param native_pipe A native pipe.
131 *
132 * @throws boost::system::system_error Thrown on failure.
133 */
134 basic_readable_pipe(const executor_type& ex,
135 const native_handle_type& native_pipe)
136 : impl_(0, ex)
137 {
138 boost::system::error_code ec;
139 impl_.get_service().assign(impl_.get_implementation(),
140 native_pipe, ec);
141 boost::asio::detail::throw_error(err: ec, location: "assign");
142 }
143
144 /// Construct a basic_readable_pipe on an existing native pipe.
145 /**
146 * This constructor creates a pipe object to hold an existing native
147 * pipe.
148 *
149 * @param context An execution context which provides the I/O executor that
150 * the pipe will use, by default, to dispatch handlers for any
151 * asynchronous operations performed on the pipe.
152 *
153 * @param native_pipe A native pipe.
154 *
155 * @throws boost::system::system_error Thrown on failure.
156 */
157 template <typename ExecutionContext>
158 basic_readable_pipe(ExecutionContext& context,
159 const native_handle_type& native_pipe,
160 constraint_t<
161 is_convertible<ExecutionContext&, execution_context&>::value
162 > = 0)
163 : impl_(0, 0, context)
164 {
165 boost::system::error_code ec;
166 impl_.get_service().assign(impl_.get_implementation(),
167 native_pipe, ec);
168 boost::asio::detail::throw_error(err: ec, location: "assign");
169 }
170
171 /// Move-construct a basic_readable_pipe from another.
172 /**
173 * This constructor moves a pipe from one object to another.
174 *
175 * @param other The other basic_readable_pipe object from which the move will
176 * occur.
177 *
178 * @note Following the move, the moved-from object is in the same state as if
179 * constructed using the @c basic_readable_pipe(const executor_type&)
180 * constructor.
181 */
182 basic_readable_pipe(basic_readable_pipe&& other)
183 : impl_(std::move(other.impl_))
184 {
185 }
186
187 /// Move-assign a basic_readable_pipe from another.
188 /**
189 * This assignment operator moves a pipe from one object to another.
190 *
191 * @param other The other basic_readable_pipe object from which the move will
192 * occur.
193 *
194 * @note Following the move, the moved-from object is in the same state as if
195 * constructed using the @c basic_readable_pipe(const executor_type&)
196 * constructor.
197 */
198 basic_readable_pipe& operator=(basic_readable_pipe&& other)
199 {
200 impl_ = std::move(other.impl_);
201 return *this;
202 }
203
204 // All pipes have access to each other's implementations.
205 template <typename Executor1>
206 friend class basic_readable_pipe;
207
208 /// Move-construct a basic_readable_pipe from a pipe of another executor type.
209 /**
210 * This constructor moves a pipe from one object to another.
211 *
212 * @param other The other basic_readable_pipe object from which the move will
213 * occur.
214 *
215 * @note Following the move, the moved-from object is in the same state as if
216 * constructed using the @c basic_readable_pipe(const executor_type&)
217 * constructor.
218 */
219 template <typename Executor1>
220 basic_readable_pipe(basic_readable_pipe<Executor1>&& other,
221 constraint_t<
222 is_convertible<Executor1, Executor>::value,
223 defaulted_constraint
224 > = defaulted_constraint())
225 : impl_(std::move(other.impl_))
226 {
227 }
228
229 /// Move-assign a basic_readable_pipe from a pipe of another executor type.
230 /**
231 * This assignment operator moves a pipe from one object to another.
232 *
233 * @param other The other basic_readable_pipe object from which the move will
234 * occur.
235 *
236 * @note Following the move, the moved-from object is in the same state as if
237 * constructed using the @c basic_readable_pipe(const executor_type&)
238 * constructor.
239 */
240 template <typename Executor1>
241 constraint_t<
242 is_convertible<Executor1, Executor>::value,
243 basic_readable_pipe&
244 > operator=(basic_readable_pipe<Executor1>&& other)
245 {
246 basic_readable_pipe tmp(std::move(other));
247 impl_ = std::move(tmp.impl_);
248 return *this;
249 }
250
251 /// Destroys the pipe.
252 /**
253 * This function destroys the pipe, cancelling any outstanding
254 * asynchronous wait operations associated with the pipe as if by
255 * calling @c cancel.
256 */
257 ~basic_readable_pipe()
258 {
259 }
260
261 /// Get the executor associated with the object.
262 const executor_type& get_executor() noexcept
263 {
264 return impl_.get_executor();
265 }
266
267 /// Get a reference to the lowest layer.
268 /**
269 * This function returns a reference to the lowest layer in a stack of
270 * layers. Since a basic_readable_pipe cannot contain any further layers, it
271 * simply returns a reference to itself.
272 *
273 * @return A reference to the lowest layer in the stack of layers. Ownership
274 * is not transferred to the caller.
275 */
276 lowest_layer_type& lowest_layer()
277 {
278 return *this;
279 }
280
281 /// Get a const reference to the lowest layer.
282 /**
283 * This function returns a const reference to the lowest layer in a stack of
284 * layers. Since a basic_readable_pipe cannot contain any further layers, it
285 * simply returns a reference to itself.
286 *
287 * @return A const reference to the lowest layer in the stack of layers.
288 * Ownership is not transferred to the caller.
289 */
290 const lowest_layer_type& lowest_layer() const
291 {
292 return *this;
293 }
294
295 /// Assign an existing native pipe to the pipe.
296 /*
297 * This function opens the pipe to hold an existing native pipe.
298 *
299 * @param native_pipe A native pipe.
300 *
301 * @throws boost::system::system_error Thrown on failure.
302 */
303 void assign(const native_handle_type& native_pipe)
304 {
305 boost::system::error_code ec;
306 impl_.get_service().assign(impl_.get_implementation(), native_pipe, ec);
307 boost::asio::detail::throw_error(err: ec, location: "assign");
308 }
309
310 /// Assign an existing native pipe to the pipe.
311 /*
312 * This function opens the pipe to hold an existing native pipe.
313 *
314 * @param native_pipe A native pipe.
315 *
316 * @param ec Set to indicate what error occurred, if any.
317 */
318 BOOST_ASIO_SYNC_OP_VOID assign(const native_handle_type& native_pipe,
319 boost::system::error_code& ec)
320 {
321 impl_.get_service().assign(impl_.get_implementation(), native_pipe, ec);
322 BOOST_ASIO_SYNC_OP_VOID_RETURN(ec);
323 }
324
325 /// Determine whether the pipe is open.
326 bool is_open() const
327 {
328 return impl_.get_service().is_open(impl_.get_implementation());
329 }
330
331 /// Close the pipe.
332 /**
333 * This function is used to close the pipe. Any asynchronous read operations
334 * will be cancelled immediately, and will complete with the
335 * boost::asio::error::operation_aborted error.
336 *
337 * @throws boost::system::system_error Thrown on failure.
338 */
339 void close()
340 {
341 boost::system::error_code ec;
342 impl_.get_service().close(impl_.get_implementation(), ec);
343 boost::asio::detail::throw_error(err: ec, location: "close");
344 }
345
346 /// Close the pipe.
347 /**
348 * This function is used to close the pipe. Any asynchronous read operations
349 * will be cancelled immediately, and will complete with the
350 * boost::asio::error::operation_aborted error.
351 *
352 * @param ec Set to indicate what error occurred, if any.
353 */
354 BOOST_ASIO_SYNC_OP_VOID close(boost::system::error_code& ec)
355 {
356 impl_.get_service().close(impl_.get_implementation(), ec);
357 BOOST_ASIO_SYNC_OP_VOID_RETURN(ec);
358 }
359
360 /// Release ownership of the underlying native pipe.
361 /**
362 * This function causes all outstanding asynchronous read operations to
363 * finish immediately, and the handlers for cancelled operations will be
364 * passed the boost::asio::error::operation_aborted error. Ownership of the
365 * native pipe is then transferred to the caller.
366 *
367 * @throws boost::system::system_error Thrown on failure.
368 *
369 * @note This function is unsupported on Windows versions prior to Windows
370 * 8.1, and will fail with boost::asio::error::operation_not_supported on
371 * these platforms.
372 */
373#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \
374 && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603)
375 __declspec(deprecated("This function always fails with "
376 "operation_not_supported when used on Windows versions "
377 "prior to Windows 8.1."))
378#endif
379 native_handle_type release()
380 {
381 boost::system::error_code ec;
382 native_handle_type s = impl_.get_service().release(
383 impl_.get_implementation(), ec);
384 boost::asio::detail::throw_error(err: ec, location: "release");
385 return s;
386 }
387
388 /// Release ownership of the underlying native pipe.
389 /**
390 * This function causes all outstanding asynchronous read operations to
391 * finish immediately, and the handlers for cancelled operations will be
392 * passed the boost::asio::error::operation_aborted error. Ownership of the
393 * native pipe is then transferred to the caller.
394 *
395 * @param ec Set to indicate what error occurred, if any.
396 *
397 * @note This function is unsupported on Windows versions prior to Windows
398 * 8.1, and will fail with boost::asio::error::operation_not_supported on
399 * these platforms.
400 */
401#if defined(BOOST_ASIO_MSVC) && (BOOST_ASIO_MSVC >= 1400) \
402 && (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0603)
403 __declspec(deprecated("This function always fails with "
404 "operation_not_supported when used on Windows versions "
405 "prior to Windows 8.1."))
406#endif
407 native_handle_type release(boost::system::error_code& ec)
408 {
409 return impl_.get_service().release(impl_.get_implementation(), ec);
410 }
411
412 /// Get the native pipe representation.
413 /**
414 * This function may be used to obtain the underlying representation of the
415 * pipe. This is intended to allow access to native pipe
416 * functionality that is not otherwise provided.
417 */
418 native_handle_type native_handle()
419 {
420 return impl_.get_service().native_handle(impl_.get_implementation());
421 }
422
423 /// Cancel all asynchronous operations associated with the pipe.
424 /**
425 * This function causes all outstanding asynchronous read operations to finish
426 * immediately, and the handlers for cancelled operations will be passed the
427 * boost::asio::error::operation_aborted error.
428 *
429 * @throws boost::system::system_error Thrown on failure.
430 */
431 void cancel()
432 {
433 boost::system::error_code ec;
434 impl_.get_service().cancel(impl_.get_implementation(), ec);
435 boost::asio::detail::throw_error(err: ec, location: "cancel");
436 }
437
438 /// Cancel all asynchronous operations associated with the pipe.
439 /**
440 * This function causes all outstanding asynchronous read operations to finish
441 * immediately, and the handlers for cancelled operations will be passed the
442 * boost::asio::error::operation_aborted error.
443 *
444 * @param ec Set to indicate what error occurred, if any.
445 */
446 BOOST_ASIO_SYNC_OP_VOID cancel(boost::system::error_code& ec)
447 {
448 impl_.get_service().cancel(impl_.get_implementation(), ec);
449 BOOST_ASIO_SYNC_OP_VOID_RETURN(ec);
450 }
451
452 /// Read some data from the pipe.
453 /**
454 * This function is used to read data from the pipe. The function call will
455 * block until one or more bytes of data has been read successfully, or until
456 * an error occurs.
457 *
458 * @param buffers One or more buffers into which the data will be read.
459 *
460 * @returns The number of bytes read.
461 *
462 * @throws boost::system::system_error Thrown on failure. An error code of
463 * boost::asio::error::eof indicates that the connection was closed by the
464 * peer.
465 *
466 * @note The read_some operation may not read all of the requested number of
467 * bytes. Consider using the @ref read function if you need to ensure that
468 * the requested amount of data is read before the blocking operation
469 * completes.
470 *
471 * @par Example
472 * To read into a single data buffer use the @ref buffer function as follows:
473 * @code
474 * basic_readable_pipe.read_some(boost::asio::buffer(data, size));
475 * @endcode
476 * See the @ref buffer documentation for information on reading into multiple
477 * buffers in one go, and how to use it with arrays, boost::array or
478 * std::vector.
479 */
480 template <typename MutableBufferSequence>
481 std::size_t read_some(const MutableBufferSequence& buffers)
482 {
483 boost::system::error_code ec;
484 std::size_t s = impl_.get_service().read_some(
485 impl_.get_implementation(), buffers, ec);
486 boost::asio::detail::throw_error(err: ec, location: "read_some");
487 return s;
488 }
489
490 /// Read some data from the pipe.
491 /**
492 * This function is used to read data from the pipe. The function call will
493 * block until one or more bytes of data has been read successfully, or until
494 * an error occurs.
495 *
496 * @param buffers One or more buffers into which the data will be read.
497 *
498 * @param ec Set to indicate what error occurred, if any.
499 *
500 * @returns The number of bytes read. Returns 0 if an error occurred.
501 *
502 * @note The read_some operation may not read all of the requested number of
503 * bytes. Consider using the @ref read function if you need to ensure that
504 * the requested amount of data is read before the blocking operation
505 * completes.
506 */
507 template <typename MutableBufferSequence>
508 std::size_t read_some(const MutableBufferSequence& buffers,
509 boost::system::error_code& ec)
510 {
511 return impl_.get_service().read_some(
512 impl_.get_implementation(), buffers, ec);
513 }
514
515 /// Start an asynchronous read.
516 /**
517 * This function is used to asynchronously read data from the pipe. It is an
518 * initiating function for an @ref asynchronous_operation, and always returns
519 * immediately.
520 *
521 * @param buffers One or more buffers into which the data will be read.
522 * Although the buffers object may be copied as necessary, ownership of the
523 * underlying memory blocks is retained by the caller, which must guarantee
524 * that they remain valid until the completion handler is called.
525 *
526 * @param token The @ref completion_token that will be used to produce a
527 * completion handler, which will be called when the read completes.
528 * Potential completion tokens include @ref use_future, @ref use_awaitable,
529 * @ref yield_context, or a function object with the correct completion
530 * signature. The function signature of the completion handler must be:
531 * @code void handler(
532 * const boost::system::error_code& error, // Result of operation.
533 * std::size_t bytes_transferred // Number of bytes read.
534 * ); @endcode
535 * Regardless of whether the asynchronous operation completes immediately or
536 * not, the completion handler will not be invoked from within this function.
537 * On immediate completion, invocation of the handler will be performed in a
538 * manner equivalent to using boost::asio::post().
539 *
540 * @par Completion Signature
541 * @code void(boost::system::error_code, std::size_t) @endcode
542 *
543 * @note The read operation may not read all of the requested number of bytes.
544 * Consider using the @ref async_read function if you need to ensure that the
545 * requested amount of data is read before the asynchronous operation
546 * completes.
547 *
548 * @par Example
549 * To read into a single data buffer use the @ref buffer function as follows:
550 * @code
551 * basic_readable_pipe.async_read_some(
552 * boost::asio::buffer(data, size), handler);
553 * @endcode
554 * See the @ref buffer documentation for information on reading into multiple
555 * buffers in one go, and how to use it with arrays, boost::array or
556 * std::vector.
557 */
558 template <typename MutableBufferSequence,
559 BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code,
560 std::size_t)) ReadToken = default_completion_token_t<executor_type>>
561 auto async_read_some(const MutableBufferSequence& buffers,
562 ReadToken&& token = default_completion_token_t<executor_type>())
563 -> decltype(
564 async_initiate<ReadToken,
565 void (boost::system::error_code, std::size_t)>(
566 declval<initiate_async_read_some>(), token, buffers))
567 {
568 return async_initiate<ReadToken,
569 void (boost::system::error_code, std::size_t)>(
570 initiate_async_read_some(this), token, buffers);
571 }
572
573private:
574 // Disallow copying and assignment.
575 basic_readable_pipe(const basic_readable_pipe&) = delete;
576 basic_readable_pipe& operator=(const basic_readable_pipe&) = delete;
577
578 class initiate_async_read_some
579 {
580 public:
581 typedef Executor executor_type;
582
583 explicit initiate_async_read_some(basic_readable_pipe* self)
584 : self_(self)
585 {
586 }
587
588 const executor_type& get_executor() const noexcept
589 {
590 return self_->get_executor();
591 }
592
593 template <typename ReadHandler, typename MutableBufferSequence>
594 void operator()(ReadHandler&& handler,
595 const MutableBufferSequence& buffers) const
596 {
597 // If you get an error on the following line it means that your handler
598 // does not meet the documented type requirements for a ReadHandler.
599 BOOST_ASIO_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
600
601 detail::non_const_lvalue<ReadHandler> handler2(handler);
602 self_->impl_.get_service().async_read_some(
603 self_->impl_.get_implementation(), buffers,
604 handler2.value, self_->impl_.get_executor());
605 }
606
607 private:
608 basic_readable_pipe* self_;
609 };
610
611#if defined(BOOST_ASIO_HAS_IOCP)
612 detail::io_object_impl<detail::win_iocp_handle_service, Executor> impl_;
613#elif defined(BOOST_ASIO_HAS_IO_URING_AS_DEFAULT)
614 detail::io_object_impl<detail::io_uring_descriptor_service, Executor> impl_;
615#else
616 detail::io_object_impl<detail::reactive_descriptor_service, Executor> impl_;
617#endif
618};
619
620} // namespace asio
621} // namespace boost
622
623#include <boost/asio/detail/pop_options.hpp>
624
625#endif // defined(BOOST_ASIO_HAS_PIPE)
626 // || defined(GENERATING_DOCUMENTATION)
627
628#endif // BOOST_ASIO_BASIC_READABLE_PIPE_HPP
629

source code of boost/libs/asio/include/boost/asio/basic_readable_pipe.hpp