1 // 2 // detail/reactive_descriptor_service.hpp 3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2021 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_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP 12 #define BOOST_ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_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_WINDOWS) \ 21 && !defined(BOOST_ASIO_WINDOWS_RUNTIME) \ 22 && !defined(__CYGWIN__) 23 24 #include <boost/asio/buffer.hpp> 25 #include <boost/asio/execution_context.hpp> 26 #include <boost/asio/detail/bind_handler.hpp> 27 #include <boost/asio/detail/buffer_sequence_adapter.hpp> 28 #include <boost/asio/detail/descriptor_ops.hpp> 29 #include <boost/asio/detail/descriptor_read_op.hpp> 30 #include <boost/asio/detail/descriptor_write_op.hpp> 31 #include <boost/asio/detail/fenced_block.hpp> 32 #include <boost/asio/detail/memory.hpp> 33 #include <boost/asio/detail/noncopyable.hpp> 34 #include <boost/asio/detail/reactive_null_buffers_op.hpp> 35 #include <boost/asio/detail/reactive_wait_op.hpp> 36 #include <boost/asio/detail/reactor.hpp> 37 #include <boost/asio/posix/descriptor_base.hpp> 38 39 #include <boost/asio/detail/push_options.hpp> 40 41 namespace boost { 42 namespace asio { 43 namespace detail { 44 45 class reactive_descriptor_service : 46 public execution_context_service_base<reactive_descriptor_service> 47 { 48 public: 49 // The native type of a descriptor. 50 typedef int native_handle_type; 51 52 // The implementation type of the descriptor. 53 class implementation_type 54 : private boost::asio::detail::noncopyable 55 { 56 public: 57 // Default constructor. implementation_type()58 implementation_type() 59 : descriptor_(-1), 60 state_(0) 61 { 62 } 63 64 private: 65 // Only this service will have access to the internal values. 66 friend class reactive_descriptor_service; 67 68 // The native descriptor representation. 69 int descriptor_; 70 71 // The current state of the descriptor. 72 descriptor_ops::state_type state_; 73 74 // Per-descriptor data used by the reactor. 75 reactor::per_descriptor_data reactor_data_; 76 }; 77 78 // Constructor. 79 BOOST_ASIO_DECL reactive_descriptor_service(execution_context& context); 80 81 // Destroy all user-defined handler objects owned by the service. 82 BOOST_ASIO_DECL void shutdown(); 83 84 // Construct a new descriptor implementation. 85 BOOST_ASIO_DECL void construct(implementation_type& impl); 86 87 // Move-construct a new descriptor implementation. 88 BOOST_ASIO_DECL void move_construct(implementation_type& impl, 89 implementation_type& other_impl) BOOST_ASIO_NOEXCEPT; 90 91 // Move-assign from another descriptor implementation. 92 BOOST_ASIO_DECL void move_assign(implementation_type& impl, 93 reactive_descriptor_service& other_service, 94 implementation_type& other_impl); 95 96 // Destroy a descriptor implementation. 97 BOOST_ASIO_DECL void destroy(implementation_type& impl); 98 99 // Assign a native descriptor to a descriptor implementation. 100 BOOST_ASIO_DECL boost::system::error_code assign(implementation_type& impl, 101 const native_handle_type& native_descriptor, 102 boost::system::error_code& ec); 103 104 // Determine whether the descriptor is open. is_open(const implementation_type & impl) const105 bool is_open(const implementation_type& impl) const 106 { 107 return impl.descriptor_ != -1; 108 } 109 110 // Destroy a descriptor implementation. 111 BOOST_ASIO_DECL boost::system::error_code close(implementation_type& impl, 112 boost::system::error_code& ec); 113 114 // Get the native descriptor representation. native_handle(const implementation_type & impl) const115 native_handle_type native_handle(const implementation_type& impl) const 116 { 117 return impl.descriptor_; 118 } 119 120 // Release ownership of the native descriptor representation. 121 BOOST_ASIO_DECL native_handle_type release(implementation_type& impl); 122 123 // Cancel all operations associated with the descriptor. 124 BOOST_ASIO_DECL boost::system::error_code cancel(implementation_type& impl, 125 boost::system::error_code& ec); 126 127 // Perform an IO control command on the descriptor. 128 template <typename IO_Control_Command> io_control(implementation_type & impl,IO_Control_Command & command,boost::system::error_code & ec)129 boost::system::error_code io_control(implementation_type& impl, 130 IO_Control_Command& command, boost::system::error_code& ec) 131 { 132 descriptor_ops::ioctl(impl.descriptor_, impl.state_, 133 command.name(), static_cast<ioctl_arg_type*>(command.data()), ec); 134 return ec; 135 } 136 137 // Gets the non-blocking mode of the descriptor. non_blocking(const implementation_type & impl) const138 bool non_blocking(const implementation_type& impl) const 139 { 140 return (impl.state_ & descriptor_ops::user_set_non_blocking) != 0; 141 } 142 143 // Sets the non-blocking mode of the descriptor. non_blocking(implementation_type & impl,bool mode,boost::system::error_code & ec)144 boost::system::error_code non_blocking(implementation_type& impl, 145 bool mode, boost::system::error_code& ec) 146 { 147 descriptor_ops::set_user_non_blocking( 148 impl.descriptor_, impl.state_, mode, ec); 149 return ec; 150 } 151 152 // Gets the non-blocking mode of the native descriptor implementation. native_non_blocking(const implementation_type & impl) const153 bool native_non_blocking(const implementation_type& impl) const 154 { 155 return (impl.state_ & descriptor_ops::internal_non_blocking) != 0; 156 } 157 158 // Sets the non-blocking mode of the native descriptor implementation. native_non_blocking(implementation_type & impl,bool mode,boost::system::error_code & ec)159 boost::system::error_code native_non_blocking(implementation_type& impl, 160 bool mode, boost::system::error_code& ec) 161 { 162 descriptor_ops::set_internal_non_blocking( 163 impl.descriptor_, impl.state_, mode, ec); 164 return ec; 165 } 166 167 // Wait for the descriptor to become ready to read, ready to write, or to have 168 // pending error conditions. wait(implementation_type & impl,posix::descriptor_base::wait_type w,boost::system::error_code & ec)169 boost::system::error_code wait(implementation_type& impl, 170 posix::descriptor_base::wait_type w, boost::system::error_code& ec) 171 { 172 switch (w) 173 { 174 case posix::descriptor_base::wait_read: 175 descriptor_ops::poll_read(impl.descriptor_, impl.state_, ec); 176 break; 177 case posix::descriptor_base::wait_write: 178 descriptor_ops::poll_write(impl.descriptor_, impl.state_, ec); 179 break; 180 case posix::descriptor_base::wait_error: 181 descriptor_ops::poll_error(impl.descriptor_, impl.state_, ec); 182 break; 183 default: 184 ec = boost::asio::error::invalid_argument; 185 break; 186 } 187 188 return ec; 189 } 190 191 // Asynchronously wait for the descriptor to become ready to read, ready to 192 // write, or to have pending error conditions. 193 template <typename Handler, typename IoExecutor> async_wait(implementation_type & impl,posix::descriptor_base::wait_type w,Handler & handler,const IoExecutor & io_ex)194 void async_wait(implementation_type& impl, 195 posix::descriptor_base::wait_type w, 196 Handler& handler, const IoExecutor& io_ex) 197 { 198 bool is_continuation = 199 boost_asio_handler_cont_helpers::is_continuation(handler); 200 201 // Allocate and construct an operation to wrap the handler. 202 typedef reactive_wait_op<Handler, IoExecutor> op; 203 typename op::ptr p = { boost::asio::detail::addressof(handler), 204 op::ptr::allocate(handler), 0 }; 205 p.p = new (p.v) op(success_ec_, handler, io_ex); 206 207 BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", 208 &impl, impl.descriptor_, "async_wait")); 209 210 int op_type; 211 switch (w) 212 { 213 case posix::descriptor_base::wait_read: 214 op_type = reactor::read_op; 215 break; 216 case posix::descriptor_base::wait_write: 217 op_type = reactor::write_op; 218 break; 219 case posix::descriptor_base::wait_error: 220 op_type = reactor::except_op; 221 break; 222 default: 223 p.p->ec_ = boost::asio::error::invalid_argument; 224 reactor_.post_immediate_completion(p.p, is_continuation); 225 p.v = p.p = 0; 226 return; 227 } 228 229 start_op(impl, op_type, p.p, is_continuation, false, false); 230 p.v = p.p = 0; 231 } 232 233 // Write some data to the descriptor. 234 template <typename ConstBufferSequence> write_some(implementation_type & impl,const ConstBufferSequence & buffers,boost::system::error_code & ec)235 size_t write_some(implementation_type& impl, 236 const ConstBufferSequence& buffers, boost::system::error_code& ec) 237 { 238 typedef buffer_sequence_adapter<boost::asio::const_buffer, 239 ConstBufferSequence> bufs_type; 240 241 if (bufs_type::is_single_buffer) 242 { 243 return descriptor_ops::sync_write1(impl.descriptor_, 244 impl.state_, bufs_type::first(buffers).data(), 245 bufs_type::first(buffers).size(), ec); 246 } 247 else 248 { 249 bufs_type bufs(buffers); 250 251 return descriptor_ops::sync_write(impl.descriptor_, impl.state_, 252 bufs.buffers(), bufs.count(), bufs.all_empty(), ec); 253 } 254 } 255 256 // Wait until data can be written without blocking. write_some(implementation_type & impl,const null_buffers &,boost::system::error_code & ec)257 size_t write_some(implementation_type& impl, 258 const null_buffers&, boost::system::error_code& ec) 259 { 260 // Wait for descriptor to become ready. 261 descriptor_ops::poll_write(impl.descriptor_, impl.state_, ec); 262 263 return 0; 264 } 265 266 // Start an asynchronous write. The data being sent must be valid for the 267 // lifetime of the asynchronous operation. 268 template <typename ConstBufferSequence, typename Handler, typename IoExecutor> async_write_some(implementation_type & impl,const ConstBufferSequence & buffers,Handler & handler,const IoExecutor & io_ex)269 void async_write_some(implementation_type& impl, 270 const ConstBufferSequence& buffers, Handler& handler, 271 const IoExecutor& io_ex) 272 { 273 bool is_continuation = 274 boost_asio_handler_cont_helpers::is_continuation(handler); 275 276 // Allocate and construct an operation to wrap the handler. 277 typedef descriptor_write_op<ConstBufferSequence, Handler, IoExecutor> op; 278 typename op::ptr p = { boost::asio::detail::addressof(handler), 279 op::ptr::allocate(handler), 0 }; 280 p.p = new (p.v) op(success_ec_, impl.descriptor_, buffers, handler, io_ex); 281 282 BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", 283 &impl, impl.descriptor_, "async_write_some")); 284 285 start_op(impl, reactor::write_op, p.p, is_continuation, true, 286 buffer_sequence_adapter<boost::asio::const_buffer, 287 ConstBufferSequence>::all_empty(buffers)); 288 p.v = p.p = 0; 289 } 290 291 // Start an asynchronous wait until data can be written without blocking. 292 template <typename Handler, typename IoExecutor> async_write_some(implementation_type & impl,const null_buffers &,Handler & handler,const IoExecutor & io_ex)293 void async_write_some(implementation_type& impl, 294 const null_buffers&, Handler& handler, const IoExecutor& io_ex) 295 { 296 bool is_continuation = 297 boost_asio_handler_cont_helpers::is_continuation(handler); 298 299 // Allocate and construct an operation to wrap the handler. 300 typedef reactive_null_buffers_op<Handler, IoExecutor> op; 301 typename op::ptr p = { boost::asio::detail::addressof(handler), 302 op::ptr::allocate(handler), 0 }; 303 p.p = new (p.v) op(success_ec_, handler, io_ex); 304 305 BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", 306 &impl, impl.descriptor_, "async_write_some(null_buffers)")); 307 308 start_op(impl, reactor::write_op, p.p, is_continuation, false, false); 309 p.v = p.p = 0; 310 } 311 312 // Read some data from the stream. Returns the number of bytes read. 313 template <typename MutableBufferSequence> read_some(implementation_type & impl,const MutableBufferSequence & buffers,boost::system::error_code & ec)314 size_t read_some(implementation_type& impl, 315 const MutableBufferSequence& buffers, boost::system::error_code& ec) 316 { 317 typedef buffer_sequence_adapter<boost::asio::mutable_buffer, 318 MutableBufferSequence> bufs_type; 319 320 if (bufs_type::is_single_buffer) 321 { 322 return descriptor_ops::sync_read1(impl.descriptor_, 323 impl.state_, bufs_type::first(buffers).data(), 324 bufs_type::first(buffers).size(), ec); 325 } 326 else 327 { 328 bufs_type bufs(buffers); 329 330 return descriptor_ops::sync_read(impl.descriptor_, impl.state_, 331 bufs.buffers(), bufs.count(), bufs.all_empty(), ec); 332 } 333 } 334 335 // Wait until data can be read without blocking. read_some(implementation_type & impl,const null_buffers &,boost::system::error_code & ec)336 size_t read_some(implementation_type& impl, 337 const null_buffers&, boost::system::error_code& ec) 338 { 339 // Wait for descriptor to become ready. 340 descriptor_ops::poll_read(impl.descriptor_, impl.state_, ec); 341 342 return 0; 343 } 344 345 // Start an asynchronous read. The buffer for the data being read must be 346 // valid for the lifetime of the asynchronous operation. 347 template <typename MutableBufferSequence, 348 typename Handler, typename IoExecutor> async_read_some(implementation_type & impl,const MutableBufferSequence & buffers,Handler & handler,const IoExecutor & io_ex)349 void async_read_some(implementation_type& impl, 350 const MutableBufferSequence& buffers, 351 Handler& handler, const IoExecutor& io_ex) 352 { 353 bool is_continuation = 354 boost_asio_handler_cont_helpers::is_continuation(handler); 355 356 // Allocate and construct an operation to wrap the handler. 357 typedef descriptor_read_op<MutableBufferSequence, Handler, IoExecutor> op; 358 typename op::ptr p = { boost::asio::detail::addressof(handler), 359 op::ptr::allocate(handler), 0 }; 360 p.p = new (p.v) op(success_ec_, impl.descriptor_, buffers, handler, io_ex); 361 362 BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", 363 &impl, impl.descriptor_, "async_read_some")); 364 365 start_op(impl, reactor::read_op, p.p, is_continuation, true, 366 buffer_sequence_adapter<boost::asio::mutable_buffer, 367 MutableBufferSequence>::all_empty(buffers)); 368 p.v = p.p = 0; 369 } 370 371 // Wait until data can be read without blocking. 372 template <typename Handler, typename IoExecutor> async_read_some(implementation_type & impl,const null_buffers &,Handler & handler,const IoExecutor & io_ex)373 void async_read_some(implementation_type& impl, 374 const null_buffers&, Handler& handler, const IoExecutor& io_ex) 375 { 376 bool is_continuation = 377 boost_asio_handler_cont_helpers::is_continuation(handler); 378 379 // Allocate and construct an operation to wrap the handler. 380 typedef reactive_null_buffers_op<Handler, IoExecutor> op; 381 typename op::ptr p = { boost::asio::detail::addressof(handler), 382 op::ptr::allocate(handler), 0 }; 383 p.p = new (p.v) op(success_ec_, handler, io_ex); 384 385 BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "descriptor", 386 &impl, impl.descriptor_, "async_read_some(null_buffers)")); 387 388 start_op(impl, reactor::read_op, p.p, is_continuation, false, false); 389 p.v = p.p = 0; 390 } 391 392 private: 393 // Start the asynchronous operation. 394 BOOST_ASIO_DECL void start_op(implementation_type& impl, int op_type, 395 reactor_op* op, bool is_continuation, bool is_non_blocking, bool noop); 396 397 // The selector that performs event demultiplexing for the service. 398 reactor& reactor_; 399 400 // Cached success value to avoid accessing category singleton. 401 const boost::system::error_code success_ec_; 402 }; 403 404 } // namespace detail 405 } // namespace asio 406 } // namespace boost 407 408 #include <boost/asio/detail/pop_options.hpp> 409 410 #if defined(BOOST_ASIO_HEADER_ONLY) 411 # include <boost/asio/detail/impl/reactive_descriptor_service.ipp> 412 #endif // defined(BOOST_ASIO_HEADER_ONLY) 413 414 #endif // !defined(BOOST_ASIO_WINDOWS) 415 // && !defined(BOOST_ASIO_WINDOWS_RUNTIME) 416 // && !defined(__CYGWIN__) 417 418 #endif // BOOST_ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP 419