2
// reactive_descriptor_service.hpp
3
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
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)
11
#ifndef ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP
12
#define ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP
14
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
16
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18
#include "asio/detail/push_options.hpp"
20
#include "asio/buffer.hpp"
21
#include "asio/error.hpp"
22
#include "asio/io_service.hpp"
23
#include "asio/detail/bind_handler.hpp"
24
#include "asio/detail/handler_base_from_member.hpp"
25
#include "asio/detail/noncopyable.hpp"
26
#include "asio/detail/service_base.hpp"
27
#include "asio/detail/descriptor_ops.hpp"
29
#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
34
template <typename Reactor>
35
class reactive_descriptor_service
36
: public asio::detail::service_base<
37
reactive_descriptor_service<Reactor> >
40
// The native type of a descriptor.
41
typedef int native_type;
43
// The implementation type of the descriptor.
44
class implementation_type
45
: private asio::detail::noncopyable
48
// Default constructor.
56
// Only this service will have access to the internal values.
57
friend class reactive_descriptor_service<Reactor>;
59
// The native descriptor representation.
64
user_set_non_blocking = 1, // The user wants a non-blocking descriptor.
65
internal_non_blocking = 2 // The descriptor has been set non-blocking.
68
// Flags indicating the current state of the descriptor.
71
// Per-descriptor data used by the reactor.
72
typename Reactor::per_descriptor_data reactor_data_;
75
// The maximum number of buffers to support in a single operation.
76
enum { max_buffers = 64 < max_iov_len ? 64 : max_iov_len };
79
reactive_descriptor_service(asio::io_service& io_service)
80
: asio::detail::service_base<
81
reactive_descriptor_service<Reactor> >(io_service),
82
reactor_(asio::use_service<Reactor>(io_service))
86
// Destroy all user-defined handler objects owned by the service.
87
void shutdown_service()
91
// Construct a new descriptor implementation.
92
void construct(implementation_type& impl)
94
impl.descriptor_ = -1;
98
// Destroy a descriptor implementation.
99
void destroy(implementation_type& impl)
101
if (impl.descriptor_ != -1)
103
reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_);
105
if (impl.flags_ & implementation_type::internal_non_blocking)
107
ioctl_arg_type non_blocking = 0;
108
asio::error_code ignored_ec;
109
descriptor_ops::ioctl(impl.descriptor_,
110
FIONBIO, &non_blocking, ignored_ec);
111
impl.flags_ &= ~implementation_type::internal_non_blocking;
114
asio::error_code ignored_ec;
115
descriptor_ops::close(impl.descriptor_, ignored_ec);
117
impl.descriptor_ = -1;
121
// Assign a native descriptor to a descriptor implementation.
122
asio::error_code assign(implementation_type& impl,
123
const native_type& native_descriptor, asio::error_code& ec)
127
ec = asio::error::already_open;
131
if (int err = reactor_.register_descriptor(
132
native_descriptor, impl.reactor_data_))
134
ec = asio::error_code(err,
135
asio::error::get_system_category());
139
impl.descriptor_ = native_descriptor;
141
ec = asio::error_code();
145
// Determine whether the descriptor is open.
146
bool is_open(const implementation_type& impl) const
148
return impl.descriptor_ != -1;
151
// Destroy a descriptor implementation.
152
asio::error_code close(implementation_type& impl,
153
asio::error_code& ec)
157
reactor_.close_descriptor(impl.descriptor_, impl.reactor_data_);
159
if (impl.flags_ & implementation_type::internal_non_blocking)
161
ioctl_arg_type non_blocking = 0;
162
asio::error_code ignored_ec;
163
descriptor_ops::ioctl(impl.descriptor_,
164
FIONBIO, &non_blocking, ignored_ec);
165
impl.flags_ &= ~implementation_type::internal_non_blocking;
168
if (descriptor_ops::close(impl.descriptor_, ec) == -1)
171
impl.descriptor_ = -1;
174
ec = asio::error_code();
178
// Get the native descriptor representation.
179
native_type native(const implementation_type& impl) const
181
return impl.descriptor_;
184
// Cancel all operations associated with the descriptor.
185
asio::error_code cancel(implementation_type& impl,
186
asio::error_code& ec)
190
ec = asio::error::bad_descriptor;
194
reactor_.cancel_ops(impl.descriptor_, impl.reactor_data_);
195
ec = asio::error_code();
199
// Perform an IO control command on the descriptor.
200
template <typename IO_Control_Command>
201
asio::error_code io_control(implementation_type& impl,
202
IO_Control_Command& command, asio::error_code& ec)
206
ec = asio::error::bad_descriptor;
210
if (command.name() == static_cast<int>(FIONBIO))
213
impl.flags_ |= implementation_type::user_set_non_blocking;
215
impl.flags_ &= ~implementation_type::user_set_non_blocking;
216
ec = asio::error_code();
220
descriptor_ops::ioctl(impl.descriptor_, command.name(),
221
static_cast<ioctl_arg_type*>(command.data()), ec);
226
// Write some data to the descriptor.
227
template <typename ConstBufferSequence>
228
size_t write_some(implementation_type& impl,
229
const ConstBufferSequence& buffers, asio::error_code& ec)
233
ec = asio::error::bad_descriptor;
237
// Copy buffers into array.
238
descriptor_ops::buf bufs[max_buffers];
239
typename ConstBufferSequence::const_iterator iter = buffers.begin();
240
typename ConstBufferSequence::const_iterator end = buffers.end();
242
size_t total_buffer_size = 0;
243
for (; iter != end && i < max_buffers; ++iter, ++i)
245
asio::const_buffer buffer(*iter);
246
descriptor_ops::init_buf(bufs[i],
247
asio::buffer_cast<const void*>(buffer),
248
asio::buffer_size(buffer));
249
total_buffer_size += asio::buffer_size(buffer);
252
// A request to read_some 0 bytes on a stream is a no-op.
253
if (total_buffer_size == 0)
255
ec = asio::error_code();
259
// Make descriptor non-blocking if user wants non-blocking.
260
if (impl.flags_ & implementation_type::user_set_non_blocking)
262
if (!(impl.flags_ & implementation_type::internal_non_blocking))
264
ioctl_arg_type non_blocking = 1;
265
if (descriptor_ops::ioctl(impl.descriptor_,
266
FIONBIO, &non_blocking, ec))
268
impl.flags_ |= implementation_type::internal_non_blocking;
275
// Try to complete the operation without blocking.
276
int bytes_sent = descriptor_ops::gather_write(
277
impl.descriptor_, bufs, i, ec);
279
// Check if operation succeeded.
284
if ((impl.flags_ & implementation_type::user_set_non_blocking)
285
|| (ec != asio::error::would_block
286
&& ec != asio::error::try_again))
289
// Wait for descriptor to become ready.
290
if (descriptor_ops::poll_write(impl.descriptor_, ec) < 0)
295
// Wait until data can be written without blocking.
296
size_t write_some(implementation_type& impl,
297
const null_buffers&, asio::error_code& ec)
301
ec = asio::error::bad_descriptor;
305
// Wait for descriptor to become ready.
306
descriptor_ops::poll_write(impl.descriptor_, ec);
311
template <typename ConstBufferSequence, typename Handler>
312
class write_operation :
313
public handler_base_from_member<Handler>
316
write_operation(int descriptor, asio::io_service& io_service,
317
const ConstBufferSequence& buffers, Handler handler)
318
: handler_base_from_member<Handler>(handler),
319
descriptor_(descriptor),
320
io_service_(io_service),
326
bool perform(asio::error_code& ec,
327
std::size_t& bytes_transferred)
329
// Check whether the operation was successful.
332
bytes_transferred = 0;
336
// Copy buffers into array.
337
descriptor_ops::buf bufs[max_buffers];
338
typename ConstBufferSequence::const_iterator iter = buffers_.begin();
339
typename ConstBufferSequence::const_iterator end = buffers_.end();
341
for (; iter != end && i < max_buffers; ++iter, ++i)
343
asio::const_buffer buffer(*iter);
344
descriptor_ops::init_buf(bufs[i],
345
asio::buffer_cast<const void*>(buffer),
346
asio::buffer_size(buffer));
350
int bytes = descriptor_ops::gather_write(descriptor_, bufs, i, ec);
352
// Check if we need to run the operation again.
353
if (ec == asio::error::would_block
354
|| ec == asio::error::try_again)
357
bytes_transferred = (bytes < 0 ? 0 : bytes);
361
void complete(const asio::error_code& ec,
362
std::size_t bytes_transferred)
364
io_service_.post(bind_handler(this->handler_, ec, bytes_transferred));
369
asio::io_service& io_service_;
370
asio::io_service::work work_;
371
ConstBufferSequence buffers_;
374
// Start an asynchronous write. The data being sent must be valid for the
375
// lifetime of the asynchronous operation.
376
template <typename ConstBufferSequence, typename Handler>
377
void async_write_some(implementation_type& impl,
378
const ConstBufferSequence& buffers, Handler handler)
382
this->get_io_service().post(bind_handler(handler,
383
asio::error::bad_descriptor, 0));
387
// Determine total size of buffers.
388
typename ConstBufferSequence::const_iterator iter = buffers.begin();
389
typename ConstBufferSequence::const_iterator end = buffers.end();
391
size_t total_buffer_size = 0;
392
for (; iter != end && i < max_buffers; ++iter, ++i)
394
asio::const_buffer buffer(*iter);
395
total_buffer_size += asio::buffer_size(buffer);
398
// A request to read_some 0 bytes on a stream is a no-op.
399
if (total_buffer_size == 0)
401
this->get_io_service().post(bind_handler(handler,
402
asio::error_code(), 0));
406
// Make descriptor non-blocking.
407
if (!(impl.flags_ & implementation_type::internal_non_blocking))
409
ioctl_arg_type non_blocking = 1;
411
if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec))
413
this->get_io_service().post(bind_handler(handler, ec, 0));
416
impl.flags_ |= implementation_type::internal_non_blocking;
419
reactor_.start_write_op(impl.descriptor_, impl.reactor_data_,
420
write_operation<ConstBufferSequence, Handler>(
421
impl.descriptor_, this->get_io_service(), buffers, handler));
425
template <typename Handler>
426
class null_buffers_operation :
427
public handler_base_from_member<Handler>
430
null_buffers_operation(asio::io_service& io_service, Handler handler)
431
: handler_base_from_member<Handler>(handler),
436
bool perform(asio::error_code&,
437
std::size_t& bytes_transferred)
439
bytes_transferred = 0;
443
void complete(const asio::error_code& ec,
444
std::size_t bytes_transferred)
446
work_.get_io_service().post(bind_handler(
447
this->handler_, ec, bytes_transferred));
451
asio::io_service::work work_;
454
// Start an asynchronous wait until data can be written without blocking.
455
template <typename Handler>
456
void async_write_some(implementation_type& impl,
457
const null_buffers&, Handler handler)
461
this->get_io_service().post(bind_handler(handler,
462
asio::error::bad_descriptor, 0));
466
reactor_.start_write_op(impl.descriptor_, impl.reactor_data_,
467
null_buffers_operation<Handler>(this->get_io_service(), handler),
472
// Read some data from the stream. Returns the number of bytes read.
473
template <typename MutableBufferSequence>
474
size_t read_some(implementation_type& impl,
475
const MutableBufferSequence& buffers, asio::error_code& ec)
479
ec = asio::error::bad_descriptor;
483
// Copy buffers into array.
484
descriptor_ops::buf bufs[max_buffers];
485
typename MutableBufferSequence::const_iterator iter = buffers.begin();
486
typename MutableBufferSequence::const_iterator end = buffers.end();
488
size_t total_buffer_size = 0;
489
for (; iter != end && i < max_buffers; ++iter, ++i)
491
asio::mutable_buffer buffer(*iter);
492
descriptor_ops::init_buf(bufs[i],
493
asio::buffer_cast<void*>(buffer),
494
asio::buffer_size(buffer));
495
total_buffer_size += asio::buffer_size(buffer);
498
// A request to read_some 0 bytes on a stream is a no-op.
499
if (total_buffer_size == 0)
501
ec = asio::error_code();
505
// Make descriptor non-blocking if user wants non-blocking.
506
if (impl.flags_ & implementation_type::user_set_non_blocking)
508
if (!(impl.flags_ & implementation_type::internal_non_blocking))
510
ioctl_arg_type non_blocking = 1;
511
if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec))
513
impl.flags_ |= implementation_type::internal_non_blocking;
520
// Try to complete the operation without blocking.
521
int bytes_read = descriptor_ops::scatter_read(
522
impl.descriptor_, bufs, i, ec);
524
// Check if operation succeeded.
531
ec = asio::error::eof;
536
if ((impl.flags_ & implementation_type::user_set_non_blocking)
537
|| (ec != asio::error::would_block
538
&& ec != asio::error::try_again))
541
// Wait for descriptor to become ready.
542
if (descriptor_ops::poll_read(impl.descriptor_, ec) < 0)
547
// Wait until data can be read without blocking.
548
size_t read_some(implementation_type& impl,
549
const null_buffers&, asio::error_code& ec)
553
ec = asio::error::bad_descriptor;
557
// Wait for descriptor to become ready.
558
descriptor_ops::poll_read(impl.descriptor_, ec);
563
template <typename MutableBufferSequence, typename Handler>
564
class read_operation :
565
public handler_base_from_member<Handler>
568
read_operation(int descriptor, asio::io_service& io_service,
569
const MutableBufferSequence& buffers, Handler handler)
570
: handler_base_from_member<Handler>(handler),
571
descriptor_(descriptor),
572
io_service_(io_service),
578
bool perform(asio::error_code& ec,
579
std::size_t& bytes_transferred)
581
// Check whether the operation was successful.
584
bytes_transferred = 0;
588
// Copy buffers into array.
589
descriptor_ops::buf bufs[max_buffers];
590
typename MutableBufferSequence::const_iterator iter = buffers_.begin();
591
typename MutableBufferSequence::const_iterator end = buffers_.end();
593
for (; iter != end && i < max_buffers; ++iter, ++i)
595
asio::mutable_buffer buffer(*iter);
596
descriptor_ops::init_buf(bufs[i],
597
asio::buffer_cast<void*>(buffer),
598
asio::buffer_size(buffer));
602
int bytes = descriptor_ops::scatter_read(descriptor_, bufs, i, ec);
604
ec = asio::error::eof;
606
// Check if we need to run the operation again.
607
if (ec == asio::error::would_block
608
|| ec == asio::error::try_again)
611
bytes_transferred = (bytes < 0 ? 0 : bytes);
615
void complete(const asio::error_code& ec,
616
std::size_t bytes_transferred)
618
io_service_.post(bind_handler(this->handler_, ec, bytes_transferred));
623
asio::io_service& io_service_;
624
asio::io_service::work work_;
625
MutableBufferSequence buffers_;
628
// Start an asynchronous read. The buffer for the data being read must be
629
// valid for the lifetime of the asynchronous operation.
630
template <typename MutableBufferSequence, typename Handler>
631
void async_read_some(implementation_type& impl,
632
const MutableBufferSequence& buffers, Handler handler)
636
this->get_io_service().post(bind_handler(handler,
637
asio::error::bad_descriptor, 0));
641
// Determine total size of buffers.
642
typename MutableBufferSequence::const_iterator iter = buffers.begin();
643
typename MutableBufferSequence::const_iterator end = buffers.end();
645
size_t total_buffer_size = 0;
646
for (; iter != end && i < max_buffers; ++iter, ++i)
648
asio::mutable_buffer buffer(*iter);
649
total_buffer_size += asio::buffer_size(buffer);
652
// A request to read_some 0 bytes on a stream is a no-op.
653
if (total_buffer_size == 0)
655
this->get_io_service().post(bind_handler(handler,
656
asio::error_code(), 0));
660
// Make descriptor non-blocking.
661
if (!(impl.flags_ & implementation_type::internal_non_blocking))
663
ioctl_arg_type non_blocking = 1;
665
if (descriptor_ops::ioctl(impl.descriptor_, FIONBIO, &non_blocking, ec))
667
this->get_io_service().post(bind_handler(handler, ec, 0));
670
impl.flags_ |= implementation_type::internal_non_blocking;
673
reactor_.start_read_op(impl.descriptor_, impl.reactor_data_,
674
read_operation<MutableBufferSequence, Handler>(
675
impl.descriptor_, this->get_io_service(), buffers, handler));
679
// Wait until data can be read without blocking.
680
template <typename Handler>
681
void async_read_some(implementation_type& impl,
682
const null_buffers&, Handler handler)
686
this->get_io_service().post(bind_handler(handler,
687
asio::error::bad_descriptor, 0));
691
reactor_.start_read_op(impl.descriptor_, impl.reactor_data_,
692
null_buffers_operation<Handler>(this->get_io_service(), handler),
698
// The selector that performs event demultiplexing for the service.
702
} // namespace detail
705
#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__)
707
#include "asio/detail/pop_options.hpp"
709
#endif // ASIO_DETAIL_REACTIVE_DESCRIPTOR_SERVICE_HPP