reactive_descriptor_service.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. //
  2. // reactive_descriptor_service.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2008 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_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP
  11. #define BOOST_ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_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/push_options.hpp>
  16. #include <boost/asio/buffer.hpp>
  17. #include <boost/asio/error.hpp>
  18. #include <boost/asio/io_service.hpp>
  19. #include <boost/asio/detail/bind_handler.hpp>
  20. #include <boost/asio/detail/handler_base_from_member.hpp>
  21. #include <boost/asio/detail/noncopyable.hpp>
  22. #include <boost/asio/detail/service_base.hpp>
  23. #include <boost/asio/detail/descriptor_ops.hpp>
  24. #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
  25. namespace boost {
  26. namespace asio {
  27. namespace detail {
  28. template <typename Reactor>
  29. class reactive_descriptor_service
  30. : public boost::asio::detail::service_base<
  31. reactive_descriptor_service<Reactor> >
  32. {
  33. public:
  34. // The native type of a descriptor.
  35. typedef int native_type;
  36. // The implementation type of the descriptor.
  37. class implementation_type
  38. : private boost::asio::detail::noncopyable
  39. {
  40. public:
  41. // Default constructor.
  42. implementation_type()
  43. : descriptor_(-1),
  44. flags_(0)
  45. {
  46. }
  47. private:
  48. // Only this service will have access to the internal values.
  49. friend class reactive_descriptor_service<Reactor>;
  50. // The native descriptor representation.
  51. int descriptor_;
  52. enum
  53. {
  54. user_set_non_blocking = 1, // The user wants a non-blocking descriptor.
  55. internal_non_blocking = 2 // The descriptor has been set non-blocking.
  56. };
  57. // Flags indicating the current state of the descriptor.
  58. unsigned char flags_;
  59. // Per-descriptor data used by the reactor.
  60. typename Reactor::per_descriptor_data reactor_data_;
  61. };
  62. // The maximum number of buffers to support in a single operation.
  63. enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len };
  64. // Constructor.
  65. reactive_descriptor_service(boost::asio::io_service& io_service)
  66. : boost::asio::detail::service_base<
  67. reactive_descriptor_service<Reactor> >(io_service),
  68. reactor_(boost::asio::use_service<Reactor>(io_service))
  69. {
  70. reactor_.init_task();
  71. }
  72. // Destroy all user-defined handler objects owned by the service.
  73. void shutdown_service()
  74. {
  75. }
  76. // Construct a new descriptor implementation.
  77. void construct(implementation_type& impl)
  78. {
  79. impl.descriptor_ = -1;
  80. impl.flags_ = 0;
  81. }
  82. // Destroy a descriptor implementation.
  83. void destroy(implementation_type& impl)
  84. {
  85. if (impl.descriptor_ != -1)
  86. {
  87. reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_);
  88. if (impl.flags_ & implementation_type::internal_non_blocking)
  89. {
  90. ioctl_arg_type non_blocking = 0;
  91. boost::system::error_code ignored_ec;
  92. descriptor_ops::ioctl(impl.descriptor_,
  93. FIONBIO, &non_blocking, ignored_ec);
  94. impl.flags_ &= ~implementation_type::internal_non_blocking;
  95. }
  96. boost::system::error_code ignored_ec;
  97. descriptor_ops::close(impl.descriptor_, ignored_ec);
  98. impl.descriptor_ = -1;
  99. }
  100. }
  101. // Assign a native descriptor to a descriptor implementation.
  102. boost::system::error_code assign(implementation_type& impl,
  103. const native_type& native_descriptor, boost::system::error_code& ec)
  104. {
  105. if (is_open(impl))
  106. {
  107. ec = boost::asio::error::already_open;
  108. return ec;
  109. }
  110. if (int err = reactor_.register_descriptor(
  111. native_descriptor, impl.reactor_data_))
  112. {
  113. ec = boost::system::error_code(err,
  114. boost::asio::error::get_system_category());
  115. return ec;
  116. }
  117. impl.descriptor_ = native_descriptor;
  118. impl.flags_ = 0;
  119. ec = boost::system::error_code();
  120. return ec;
  121. }
  122. // Determine whether the descriptor is open.
  123. bool is_open(const implementation_type& impl) const
  124. {
  125. return impl.descriptor_ != -1;
  126. }
  127. // Destroy a descriptor implementation.
  128. boost::system::error_code close(implementation_type& impl,
  129. boost::system::error_code& ec)
  130. {
  131. if (is_open(impl))
  132. {
  133. reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_);
  134. if (impl.flags_ & implementation_type::internal_non_blocking)
  135. {
  136. ioctl_arg_type non_blocking = 0;
  137. boost::system::error_code ignored_ec;
  138. descriptor_ops::ioctl(impl.descriptor_,
  139. FIONBIO, &non_blocking, ignored_ec);
  140. impl.flags_ &= ~implementation_type::internal_non_blocking;
  141. }
  142. if (descriptor_ops::close(impl.descriptor_, ec) == -1)
  143. return ec;
  144. impl.descriptor_ = -1;
  145. }
  146. ec = boost::system::error_code();
  147. return ec;
  148. }
  149. // Get the native descriptor representation.
  150. native_type native(const implementation_type& impl) const
  151. {
  152. return impl.descriptor_;
  153. }
  154. // Cancel all operations associated with the descriptor.
  155. boost::system::error_code cancel(implementation_type& impl,
  156. boost::system::error_code& ec)
  157. {
  158. if (!is_open(impl))
  159. {
  160. ec = boost::asio::error::bad_descriptor;
  161. return ec;
  162. }
  163. reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_);
  164. ec = boost::system::error_code();
  165. return ec;
  166. }
  167. // Perform an IO control command on the descriptor.
  168. template <typename IO_Control_Command>
  169. boost::system::error_code io_control(implementation_type& impl,
  170. IO_Control_Command& command, boost::system::error_code& ec)
  171. {
  172. if (!is_open(impl))
  173. {
  174. ec = boost::asio::error::bad_descriptor;
  175. return ec;
  176. }
  177. if (command.name() == static_cast<int>(FIONBIO))
  178. {
  179. if (command.get())
  180. impl.flags_ |= implementation_type::user_set_non_blocking;
  181. else
  182. impl.flags_ &= ~implementation_type::user_set_non_blocking;
  183. ec = boost::system::error_code();
  184. }
  185. else
  186. {
  187. descriptor_ops::ioctl(impl.descriptor_, command.name(),
  188. static_cast<ioctl_arg_type*>(command.data()), ec);
  189. }
  190. return ec;
  191. }
  192. // Write some data to the descriptor.
  193. template <typename ConstBufferSequence>
  194. size_t write_some(implementation_type& impl,
  195. const ConstBufferSequence& buffers, boost::system::error_code& ec)
  196. {
  197. if (!is_open(impl))
  198. {
  199. ec = boost::asio::error::bad_descriptor;
  200. return 0;
  201. }
  202. // Copy buffers into array.
  203. descriptor_ops::buf bufs[max_buffers];
  204. typename ConstBufferSequence::const_iterator iter = buffers.begin();
  205. typename ConstBufferSequence::const_iterator end = buffers.end();
  206. size_t i = 0;
  207. size_t total_buffer_size = 0;
  208. for (; iter != end && i < max_buffers; ++iter, ++i)
  209. {
  210. boost::asio::const_buffer buffer(*iter);
  211. descriptor_ops::init_buf(bufs[i],
  212. boost::asio::buffer_cast<const void*>(buffer),
  213. boost::asio::buffer_size(buffer));
  214. total_buffer_size += boost::asio::buffer_size(buffer);
  215. }
  216. // A request to read_some 0 bytes on a stream is a no-op.
  217. if (total_buffer_size == 0)
  218. {
  219. ec = boost::system::error_code();
  220. return 0;
  221. }
  222. // Make descriptor non-blocking if user wants non-blocking.
  223. if (impl.flags_ & implementation_type::user_set_non_blocking)
  224. {
  225. if (!(impl.flags_ & implementation_type::internal_non_blocking))
  226. {
  227. ioctl_arg_type non_blocking = 1;
  228. if (descriptor_ops::ioctl(impl.descriptor_,
  229. FIONBIO, &non_blocking, ec))
  230. return 0;
  231. impl.flags_ |= implementation_type::internal_non_blocking;
  232. }
  233. }
  234. // Send the data.
  235. for (;;)
  236. {
  237. // Try to complete the operation without blocking.
  238. int bytes_sent = descriptor_ops::gather_write(
  239. impl.descriptor_, bufs, i, ec);
  240. // Check if operation succeeded.
  241. if (bytes_sent >= 0)
  242. return bytes_sent;
  243. // Operation failed.
  244. if ((impl.flags_ & implementation_type::user_set_non_blocking)
  245. || (ec != boost::asio::error::would_block
  246. && ec != boost::asio::error::try_again))
  247. return 0;
  248. // Wait for descriptor to become ready.
  249. if (descriptor_ops::poll_write(impl.descriptor_, ec) < 0)
  250. return 0;
  251. }
  252. }
  253. // Wait until data can be written without blocking.
  254. size_t write_some(implementation_type& impl,
  255. const null_buffers&, boost::system::error_code& ec)
  256. {
  257. if (!is_open(impl))
  258. {
  259. ec = boost::asio::error::bad_descriptor;
  260. return 0;
  261. }
  262. // Wait for descriptor to become ready.
  263. descriptor_ops::poll_write(impl.descriptor_, ec);
  264. return 0;
  265. }
  266. template <typename ConstBufferSequence, typename Handler>
  267. class write_operation :
  268. public handler_base_from_member<Handler>
  269. {
  270. public:
  271. write_operation(int descriptor, boost::asio::io_service& io_service,
  272. const ConstBufferSequence& buffers, Handler handler)
  273. : handler_base_from_member<Handler>(handler),
  274. descriptor_(descriptor),
  275. io_service_(io_service),
  276. work_(io_service),
  277. buffers_(buffers)
  278. {
  279. }
  280. bool perform(boost::system::error_code& ec,
  281. std::size_t& bytes_transferred)
  282. {
  283. // Check whether the operation was successful.
  284. if (ec)
  285. {
  286. bytes_transferred = 0;
  287. return true;
  288. }
  289. // Copy buffers into array.
  290. descriptor_ops::buf bufs[max_buffers];
  291. typename ConstBufferSequence::const_iterator iter = buffers_.begin();
  292. typename ConstBufferSequence::const_iterator end = buffers_.end();
  293. size_t i = 0;
  294. for (; iter != end && i < max_buffers; ++iter, ++i)
  295. {
  296. boost::asio::const_buffer buffer(*iter);
  297. descriptor_ops::init_buf(bufs[i],
  298. boost::asio::buffer_cast<const void*>(buffer),
  299. boost::asio::buffer_size(buffer));
  300. }
  301. // Write the data.
  302. int bytes = descriptor_ops::gather_write(descriptor_, bufs, i, ec);
  303. // Check if we need to run the operation again.
  304. if (ec == boost::asio::error::would_block
  305. || ec == boost::asio::error::try_again)
  306. return false;
  307. bytes_transferred = (bytes < 0 ? 0 : bytes);
  308. return true;
  309. }
  310. void complete(const boost::system::error_code& ec,
  311. std::size_t bytes_transferred)
  312. {
  313. io_service_.post(bind_handler(this->handler_, ec, bytes_transferred));
  314. }
  315. private:
  316. int descriptor_;
  317. boost::asio::io_service& io_service_;
  318. boost::asio::io_service::work work_;
  319. ConstBufferSequence buffers_;
  320. };
  321. // Start an asynchronous write. The data being sent must be valid for the
  322. // lifetime of the asynchronous operation.
  323. template <typename ConstBufferSequence, typename Handler>
  324. void async_write_some(implementation_type& impl,
  325. const ConstBufferSequence& buffers, Handler handler)
  326. {
  327. if (!is_open(impl))
  328. {
  329. this->get_io_service().post(bind_handler(handler,
  330. boost::asio::error::bad_descriptor, 0));
  331. }
  332. else
  333. {
  334. // Determine total size of buffers.
  335. typename ConstBufferSequence::const_iterator iter = buffers.begin();
  336. typename ConstBufferSequence::const_iterator end = buffers.end();
  337. size_t i = 0;
  338. size_t total_buffer_size = 0;
  339. for (; iter != end && i < max_buffers; ++iter, ++i)
  340. {
  341. boost::asio::const_buffer buffer(*iter);
  342. total_buffer_size += boost::asio::buffer_size(buffer);
  343. }
  344. // A request to read_some 0 bytes on a stream is a no-op.
  345. if (total_buffer_size == 0)
  346. {
  347. this->get_io_service().post(bind_handler(handler,
  348. boost::system::error_code(), 0));
  349. return;
  350. }
  351. // Make descriptor non-blocking.
  352. if (!(impl.flags_ & implementation_type::internal_non_blocking))
  353. {
  354. ioctl_arg_type non_blocking = 1;
  355. boost::system::error_code ec;
  356. if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec))
  357. {
  358. this->get_io_service().post(bind_handler(handler, ec, 0));
  359. return;
  360. }
  361. impl.flags_ |= implementation_type::internal_non_blocking;
  362. }
  363. reactor_.start_write_op(impl.descriptor_, impl.reactor_data_,
  364. write_operation<ConstBufferSequence, Handler>(
  365. impl.descriptor_, this->get_io_service(), buffers, handler));
  366. }
  367. }
  368. template <typename Handler>
  369. class null_buffers_operation :
  370. public handler_base_from_member<Handler>
  371. {
  372. public:
  373. null_buffers_operation(boost::asio::io_service& io_service, Handler handler)
  374. : handler_base_from_member<Handler>(handler),
  375. work_(io_service)
  376. {
  377. }
  378. bool perform(boost::system::error_code&,
  379. std::size_t& bytes_transferred)
  380. {
  381. bytes_transferred = 0;
  382. return true;
  383. }
  384. void complete(const boost::system::error_code& ec,
  385. std::size_t bytes_transferred)
  386. {
  387. work_.get_io_service().post(bind_handler(
  388. this->handler_, ec, bytes_transferred));
  389. }
  390. private:
  391. boost::asio::io_service::work work_;
  392. };
  393. // Start an asynchronous wait until data can be written without blocking.
  394. template <typename Handler>
  395. void async_write_some(implementation_type& impl,
  396. const null_buffers&, Handler handler)
  397. {
  398. if (!is_open(impl))
  399. {
  400. this->get_io_service().post(bind_handler(handler,
  401. boost::asio::error::bad_descriptor, 0));
  402. }
  403. else
  404. {
  405. reactor_.start_write_op(impl.descriptor_, impl.reactor_data_,
  406. null_buffers_operation<Handler>(this->get_io_service(), handler),
  407. false);
  408. }
  409. }
  410. // Read some data from the stream. Returns the number of bytes read.
  411. template <typename MutableBufferSequence>
  412. size_t read_some(implementation_type& impl,
  413. const MutableBufferSequence& buffers, boost::system::error_code& ec)
  414. {
  415. if (!is_open(impl))
  416. {
  417. ec = boost::asio::error::bad_descriptor;
  418. return 0;
  419. }
  420. // Copy buffers into array.
  421. descriptor_ops::buf bufs[max_buffers];
  422. typename MutableBufferSequence::const_iterator iter = buffers.begin();
  423. typename MutableBufferSequence::const_iterator end = buffers.end();
  424. size_t i = 0;
  425. size_t total_buffer_size = 0;
  426. for (; iter != end && i < max_buffers; ++iter, ++i)
  427. {
  428. boost::asio::mutable_buffer buffer(*iter);
  429. descriptor_ops::init_buf(bufs[i],
  430. boost::asio::buffer_cast<void*>(buffer),
  431. boost::asio::buffer_size(buffer));
  432. total_buffer_size += boost::asio::buffer_size(buffer);
  433. }
  434. // A request to read_some 0 bytes on a stream is a no-op.
  435. if (total_buffer_size == 0)
  436. {
  437. ec = boost::system::error_code();
  438. return 0;
  439. }
  440. // Make descriptor non-blocking if user wants non-blocking.
  441. if (impl.flags_ & implementation_type::user_set_non_blocking)
  442. {
  443. if (!(impl.flags_ & implementation_type::internal_non_blocking))
  444. {
  445. ioctl_arg_type non_blocking = 1;
  446. if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec))
  447. return 0;
  448. impl.flags_ |= implementation_type::internal_non_blocking;
  449. }
  450. }
  451. // Read some data.
  452. for (;;)
  453. {
  454. // Try to complete the operation without blocking.
  455. int bytes_read = descriptor_ops::scatter_read(
  456. impl.descriptor_, bufs, i, ec);
  457. // Check if operation succeeded.
  458. if (bytes_read > 0)
  459. return bytes_read;
  460. // Check for EOF.
  461. if (bytes_read == 0)
  462. {
  463. ec = boost::asio::error::eof;
  464. return 0;
  465. }
  466. // Operation failed.
  467. if ((impl.flags_ & implementation_type::user_set_non_blocking)
  468. || (ec != boost::asio::error::would_block
  469. && ec != boost::asio::error::try_again))
  470. return 0;
  471. // Wait for descriptor to become ready.
  472. if (descriptor_ops::poll_read(impl.descriptor_, ec) < 0)
  473. return 0;
  474. }
  475. }
  476. // Wait until data can be read without blocking.
  477. size_t read_some(implementation_type& impl,
  478. const null_buffers&, boost::system::error_code& ec)
  479. {
  480. if (!is_open(impl))
  481. {
  482. ec = boost::asio::error::bad_descriptor;
  483. return 0;
  484. }
  485. // Wait for descriptor to become ready.
  486. descriptor_ops::poll_read(impl.descriptor_, ec);
  487. return 0;
  488. }
  489. template <typename MutableBufferSequence, typename Handler>
  490. class read_operation :
  491. public handler_base_from_member<Handler>
  492. {
  493. public:
  494. read_operation(int descriptor, boost::asio::io_service& io_service,
  495. const MutableBufferSequence& buffers, Handler handler)
  496. : handler_base_from_member<Handler>(handler),
  497. descriptor_(descriptor),
  498. io_service_(io_service),
  499. work_(io_service),
  500. buffers_(buffers)
  501. {
  502. }
  503. bool perform(boost::system::error_code& ec,
  504. std::size_t& bytes_transferred)
  505. {
  506. // Check whether the operation was successful.
  507. if (ec)
  508. {
  509. bytes_transferred = 0;
  510. return true;
  511. }
  512. // Copy buffers into array.
  513. descriptor_ops::buf bufs[max_buffers];
  514. typename MutableBufferSequence::const_iterator iter = buffers_.begin();
  515. typename MutableBufferSequence::const_iterator end = buffers_.end();
  516. size_t i = 0;
  517. for (; iter != end && i < max_buffers; ++iter, ++i)
  518. {
  519. boost::asio::mutable_buffer buffer(*iter);
  520. descriptor_ops::init_buf(bufs[i],
  521. boost::asio::buffer_cast<void*>(buffer),
  522. boost::asio::buffer_size(buffer));
  523. }
  524. // Read some data.
  525. int bytes = descriptor_ops::scatter_read(descriptor_, bufs, i, ec);
  526. if (bytes == 0)
  527. ec = boost::asio::error::eof;
  528. // Check if we need to run the operation again.
  529. if (ec == boost::asio::error::would_block
  530. || ec == boost::asio::error::try_again)
  531. return false;
  532. bytes_transferred = (bytes < 0 ? 0 : bytes);
  533. return true;
  534. }
  535. void complete(const boost::system::error_code& ec,
  536. std::size_t bytes_transferred)
  537. {
  538. io_service_.post(bind_handler(this->handler_, ec, bytes_transferred));
  539. }
  540. private:
  541. int descriptor_;
  542. boost::asio::io_service& io_service_;
  543. boost::asio::io_service::work work_;
  544. MutableBufferSequence buffers_;
  545. };
  546. // Start an asynchronous read. The buffer for the data being read must be
  547. // valid for the lifetime of the asynchronous operation.
  548. template <typename MutableBufferSequence, typename Handler>
  549. void async_read_some(implementation_type& impl,
  550. const MutableBufferSequence& buffers, Handler handler)
  551. {
  552. if (!is_open(impl))
  553. {
  554. this->get_io_service().post(bind_handler(handler,
  555. boost::asio::error::bad_descriptor, 0));
  556. }
  557. else
  558. {
  559. // Determine total size of buffers.
  560. typename MutableBufferSequence::const_iterator iter = buffers.begin();
  561. typename MutableBufferSequence::const_iterator end = buffers.end();
  562. size_t i = 0;
  563. size_t total_buffer_size = 0;
  564. for (; iter != end && i < max_buffers; ++iter, ++i)
  565. {
  566. boost::asio::mutable_buffer buffer(*iter);
  567. total_buffer_size += boost::asio::buffer_size(buffer);
  568. }
  569. // A request to read_some 0 bytes on a stream is a no-op.
  570. if (total_buffer_size == 0)
  571. {
  572. this->get_io_service().post(bind_handler(handler,
  573. boost::system::error_code(), 0));
  574. return;
  575. }
  576. // Make descriptor non-blocking.
  577. if (!(impl.flags_ & implementation_type::internal_non_blocking))
  578. {
  579. ioctl_arg_type non_blocking = 1;
  580. boost::system::error_code ec;
  581. if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec))
  582. {
  583. this->get_io_service().post(bind_handler(handler, ec, 0));
  584. return;
  585. }
  586. impl.flags_ |= implementation_type::internal_non_blocking;
  587. }
  588. reactor_.start_read_op(impl.descriptor_, impl.reactor_data_,
  589. read_operation<MutableBufferSequence, Handler>(
  590. impl.descriptor_, this->get_io_service(), buffers, handler));
  591. }
  592. }
  593. // Wait until data can be read without blocking.
  594. template <typename Handler>
  595. void async_read_some(implementation_type& impl,
  596. const null_buffers&, Handler handler)
  597. {
  598. if (!is_open(impl))
  599. {
  600. this->get_io_service().post(bind_handler(handler,
  601. boost::asio::error::bad_descriptor, 0));
  602. }
  603. else
  604. {
  605. reactor_.start_read_op(impl.descriptor_, impl.reactor_data_,
  606. null_buffers_operation<Handler>(this->get_io_service(), handler),
  607. false);
  608. }
  609. }
  610. private:
  611. // The selector that performs event demultiplexing for the service.
  612. Reactor& reactor_;
  613. };
  614. } // namespace detail
  615. } // namespace asio
  616. } // namespace boost
  617. #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
  618. #include <boost/asio/detail/pop_options.hpp>
  619. #endif // BOOST_ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP