Chris@16: // Chris@16: // detail/impl/select_reactor.ipp Chris@16: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Chris@16: // Chris@101: // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: Chris@16: #ifndef BOOST_ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP Chris@16: #define BOOST_ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP Chris@16: Chris@16: #if defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: # pragma once Chris@16: #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: Chris@16: #include Chris@16: Chris@16: #if defined(BOOST_ASIO_HAS_IOCP) \ Chris@16: || (!defined(BOOST_ASIO_HAS_DEV_POLL) \ Chris@16: && !defined(BOOST_ASIO_HAS_EPOLL) \ Chris@16: && !defined(BOOST_ASIO_HAS_KQUEUE) \ Chris@16: && !defined(BOOST_ASIO_WINDOWS_RUNTIME)) Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost { Chris@16: namespace asio { Chris@16: namespace detail { Chris@16: Chris@16: select_reactor::select_reactor(boost::asio::io_service& io_service) Chris@16: : boost::asio::detail::service_base(io_service), Chris@16: io_service_(use_service(io_service)), Chris@16: mutex_(), Chris@16: interrupter_(), Chris@16: #if defined(BOOST_ASIO_HAS_IOCP) Chris@16: stop_thread_(false), Chris@16: thread_(0), Chris@16: #endif // defined(BOOST_ASIO_HAS_IOCP) Chris@16: shutdown_(false) Chris@16: { Chris@16: #if defined(BOOST_ASIO_HAS_IOCP) Chris@16: boost::asio::detail::signal_blocker sb; Chris@16: thread_ = new boost::asio::detail::thread( Chris@16: bind_handler(&select_reactor::call_run_thread, this)); Chris@16: #endif // defined(BOOST_ASIO_HAS_IOCP) Chris@16: } Chris@16: Chris@16: select_reactor::~select_reactor() Chris@16: { Chris@16: shutdown_service(); Chris@16: } Chris@16: Chris@16: void select_reactor::shutdown_service() Chris@16: { Chris@16: boost::asio::detail::mutex::scoped_lock lock(mutex_); Chris@16: shutdown_ = true; Chris@16: #if defined(BOOST_ASIO_HAS_IOCP) Chris@16: stop_thread_ = true; Chris@16: #endif // defined(BOOST_ASIO_HAS_IOCP) Chris@16: lock.unlock(); Chris@16: Chris@16: #if defined(BOOST_ASIO_HAS_IOCP) Chris@16: if (thread_) Chris@16: { Chris@16: interrupter_.interrupt(); Chris@16: thread_->join(); Chris@16: delete thread_; Chris@16: thread_ = 0; Chris@16: } Chris@16: #endif // defined(BOOST_ASIO_HAS_IOCP) Chris@16: Chris@16: op_queue ops; Chris@16: Chris@16: for (int i = 0; i < max_ops; ++i) Chris@16: op_queue_[i].get_all_operations(ops); Chris@16: Chris@16: timer_queues_.get_all_timers(ops); Chris@16: Chris@16: io_service_.abandon_operations(ops); Chris@16: } Chris@16: Chris@16: void select_reactor::fork_service(boost::asio::io_service::fork_event fork_ev) Chris@16: { Chris@16: if (fork_ev == boost::asio::io_service::fork_child) Chris@16: interrupter_.recreate(); Chris@16: } Chris@16: Chris@16: void select_reactor::init_task() Chris@16: { Chris@16: io_service_.init_task(); Chris@16: } Chris@16: Chris@16: int select_reactor::register_descriptor(socket_type, Chris@16: select_reactor::per_descriptor_data&) Chris@16: { Chris@16: return 0; Chris@16: } Chris@16: Chris@16: int select_reactor::register_internal_descriptor( Chris@16: int op_type, socket_type descriptor, Chris@16: select_reactor::per_descriptor_data&, reactor_op* op) Chris@16: { Chris@16: boost::asio::detail::mutex::scoped_lock lock(mutex_); Chris@16: Chris@16: op_queue_[op_type].enqueue_operation(descriptor, op); Chris@16: interrupter_.interrupt(); Chris@16: Chris@16: return 0; Chris@16: } Chris@16: Chris@16: void select_reactor::move_descriptor(socket_type, Chris@16: select_reactor::per_descriptor_data&, Chris@16: select_reactor::per_descriptor_data&) Chris@16: { Chris@16: } Chris@16: Chris@16: void select_reactor::start_op(int op_type, socket_type descriptor, Chris@16: select_reactor::per_descriptor_data&, reactor_op* op, Chris@16: bool is_continuation, bool) Chris@16: { Chris@16: boost::asio::detail::mutex::scoped_lock lock(mutex_); Chris@16: Chris@16: if (shutdown_) Chris@16: { Chris@16: post_immediate_completion(op, is_continuation); Chris@16: return; Chris@16: } Chris@16: Chris@16: bool first = op_queue_[op_type].enqueue_operation(descriptor, op); Chris@16: io_service_.work_started(); Chris@16: if (first) Chris@16: interrupter_.interrupt(); Chris@16: } Chris@16: Chris@16: void select_reactor::cancel_ops(socket_type descriptor, Chris@16: select_reactor::per_descriptor_data&) Chris@16: { Chris@16: boost::asio::detail::mutex::scoped_lock lock(mutex_); Chris@16: cancel_ops_unlocked(descriptor, boost::asio::error::operation_aborted); Chris@16: } Chris@16: Chris@16: void select_reactor::deregister_descriptor(socket_type descriptor, Chris@16: select_reactor::per_descriptor_data&, bool) Chris@16: { Chris@16: boost::asio::detail::mutex::scoped_lock lock(mutex_); Chris@16: cancel_ops_unlocked(descriptor, boost::asio::error::operation_aborted); Chris@16: } Chris@16: Chris@16: void select_reactor::deregister_internal_descriptor( Chris@16: socket_type descriptor, select_reactor::per_descriptor_data&) Chris@16: { Chris@16: boost::asio::detail::mutex::scoped_lock lock(mutex_); Chris@16: op_queue ops; Chris@16: for (int i = 0; i < max_ops; ++i) Chris@16: op_queue_[i].cancel_operations(descriptor, ops); Chris@16: } Chris@16: Chris@16: void select_reactor::run(bool block, op_queue& ops) Chris@16: { Chris@16: boost::asio::detail::mutex::scoped_lock lock(mutex_); Chris@16: Chris@16: #if defined(BOOST_ASIO_HAS_IOCP) Chris@16: // Check if the thread is supposed to stop. Chris@16: if (stop_thread_) Chris@16: return; Chris@16: #endif // defined(BOOST_ASIO_HAS_IOCP) Chris@16: Chris@16: // Set up the descriptor sets. Chris@16: for (int i = 0; i < max_select_ops; ++i) Chris@16: fd_sets_[i].reset(); Chris@16: fd_sets_[read_op].set(interrupter_.read_descriptor()); Chris@16: socket_type max_fd = 0; Chris@16: bool have_work_to_do = !timer_queues_.all_empty(); Chris@16: for (int i = 0; i < max_select_ops; ++i) Chris@16: { Chris@16: have_work_to_do = have_work_to_do || !op_queue_[i].empty(); Chris@101: fd_sets_[i].set(op_queue_[i], ops); Chris@16: if (fd_sets_[i].max_descriptor() > max_fd) Chris@16: max_fd = fd_sets_[i].max_descriptor(); Chris@16: } Chris@16: Chris@16: #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) Chris@16: // Connection operations on Windows use both except and write fd_sets. Chris@16: have_work_to_do = have_work_to_do || !op_queue_[connect_op].empty(); Chris@101: fd_sets_[write_op].set(op_queue_[connect_op], ops); Chris@16: if (fd_sets_[write_op].max_descriptor() > max_fd) Chris@16: max_fd = fd_sets_[write_op].max_descriptor(); Chris@101: fd_sets_[except_op].set(op_queue_[connect_op], ops); Chris@16: if (fd_sets_[except_op].max_descriptor() > max_fd) Chris@16: max_fd = fd_sets_[except_op].max_descriptor(); Chris@16: #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) Chris@16: Chris@16: // We can return immediately if there's no work to do and the reactor is Chris@16: // not supposed to block. Chris@16: if (!block && !have_work_to_do) Chris@16: return; Chris@16: Chris@16: // Determine how long to block while waiting for events. Chris@16: timeval tv_buf = { 0, 0 }; Chris@16: timeval* tv = block ? get_timeout(tv_buf) : &tv_buf; Chris@16: Chris@16: lock.unlock(); Chris@16: Chris@16: // Block on the select call until descriptors become ready. Chris@16: boost::system::error_code ec; Chris@16: int retval = socket_ops::select(static_cast(max_fd + 1), Chris@16: fd_sets_[read_op], fd_sets_[write_op], fd_sets_[except_op], tv, ec); Chris@16: Chris@16: // Reset the interrupter. Chris@16: if (retval > 0 && fd_sets_[read_op].is_set(interrupter_.read_descriptor())) Chris@16: { Chris@16: interrupter_.reset(); Chris@16: --retval; Chris@16: } Chris@16: Chris@16: lock.lock(); Chris@16: Chris@16: // Dispatch all ready operations. Chris@16: if (retval > 0) Chris@16: { Chris@16: #if defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) Chris@16: // Connection operations on Windows use both except and write fd_sets. Chris@101: fd_sets_[except_op].perform(op_queue_[connect_op], ops); Chris@101: fd_sets_[write_op].perform(op_queue_[connect_op], ops); Chris@16: #endif // defined(BOOST_ASIO_WINDOWS) || defined(__CYGWIN__) Chris@16: Chris@16: // Exception operations must be processed first to ensure that any Chris@16: // out-of-band data is read before normal data. Chris@16: for (int i = max_select_ops - 1; i >= 0; --i) Chris@101: fd_sets_[i].perform(op_queue_[i], ops); Chris@16: } Chris@16: timer_queues_.get_ready_timers(ops); Chris@16: } Chris@16: Chris@16: void select_reactor::interrupt() Chris@16: { Chris@16: interrupter_.interrupt(); Chris@16: } Chris@16: Chris@16: #if defined(BOOST_ASIO_HAS_IOCP) Chris@16: void select_reactor::run_thread() Chris@16: { Chris@16: boost::asio::detail::mutex::scoped_lock lock(mutex_); Chris@16: while (!stop_thread_) Chris@16: { Chris@16: lock.unlock(); Chris@16: op_queue ops; Chris@16: run(true, ops); Chris@16: io_service_.post_deferred_completions(ops); Chris@16: lock.lock(); Chris@16: } Chris@16: } Chris@16: Chris@16: void select_reactor::call_run_thread(select_reactor* reactor) Chris@16: { Chris@16: reactor->run_thread(); Chris@16: } Chris@16: #endif // defined(BOOST_ASIO_HAS_IOCP) Chris@16: Chris@16: void select_reactor::do_add_timer_queue(timer_queue_base& queue) Chris@16: { Chris@16: mutex::scoped_lock lock(mutex_); Chris@16: timer_queues_.insert(&queue); Chris@16: } Chris@16: Chris@16: void select_reactor::do_remove_timer_queue(timer_queue_base& queue) Chris@16: { Chris@16: mutex::scoped_lock lock(mutex_); Chris@16: timer_queues_.erase(&queue); Chris@16: } Chris@16: Chris@16: timeval* select_reactor::get_timeout(timeval& tv) Chris@16: { Chris@16: // By default we will wait no longer than 5 minutes. This will ensure that Chris@16: // any changes to the system clock are detected after no longer than this. Chris@16: long usec = timer_queues_.wait_duration_usec(5 * 60 * 1000 * 1000); Chris@16: tv.tv_sec = usec / 1000000; Chris@16: tv.tv_usec = usec % 1000000; Chris@16: return &tv; Chris@16: } Chris@16: Chris@16: void select_reactor::cancel_ops_unlocked(socket_type descriptor, Chris@16: const boost::system::error_code& ec) Chris@16: { Chris@16: bool need_interrupt = false; Chris@16: op_queue ops; Chris@16: for (int i = 0; i < max_ops; ++i) Chris@16: need_interrupt = op_queue_[i].cancel_operations( Chris@16: descriptor, ops, ec) || need_interrupt; Chris@16: io_service_.post_deferred_completions(ops); Chris@16: if (need_interrupt) Chris@16: interrupter_.interrupt(); Chris@16: } Chris@16: Chris@16: } // namespace detail Chris@16: } // namespace asio Chris@16: } // namespace boost Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // defined(BOOST_ASIO_HAS_IOCP) Chris@16: // || (!defined(BOOST_ASIO_HAS_DEV_POLL) Chris@16: // && !defined(BOOST_ASIO_HAS_EPOLL) Chris@16: // && !defined(BOOST_ASIO_HAS_KQUEUE)) Chris@16: // && !defined(BOOST_ASIO_WINDOWS_RUNTIME)) Chris@16: Chris@16: #endif // BOOST_ASIO_DETAIL_IMPL_SELECT_REACTOR_IPP