basic_writable_pipe.hpp 21 KB

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