diff --git a/include/boost/asio/detail/reactive_socket_service.hpp b/include/boost/asio/detail/reactive_socket_service.hpp index d474ae79..16727460 100644 --- a/include/boost/asio/detail/reactive_socket_service.hpp +++ b/include/boost/asio/detail/reactive_socket_service.hpp @@ -74,10 +74,21 @@ public: enum { - user_set_non_blocking = 1, // The user wants a non-blocking socket. - internal_non_blocking = 2, // The socket has been set non-blocking. - enable_connection_aborted = 4, // User wants connection_aborted errors. - user_set_linger = 8 // The user set the linger option. + // The user wants a non-blocking socket. + user_set_non_blocking = 1, + + // The implementation wants a non-blocking socket (in order to be able to + // perform asynchronous read and write operations). + internal_non_blocking = 2, + + // Helper "flag" used to determine whether the socket is non-blocking. + non_blocking = user_set_non_blocking | internal_non_blocking, + + // User wants connection_aborted errors, which are disabled by default. + enable_connection_aborted = 4, + + // The user set the linger option. Needs to be checked when closing. + user_set_linger = 8 }; // Flags indicating the current state of the socket. @@ -120,12 +131,12 @@ public: { reactor_.close_descriptor(impl.socket_, impl.reactor_data_); - if (impl.flags_ & implementation_type::internal_non_blocking) + if (impl.flags_ & implementation_type::non_blocking) { ioctl_arg_type non_blocking = 0; boost::system::error_code ignored_ec; socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); - impl.flags_ &= ~implementation_type::internal_non_blocking; + impl.flags_ &= ~implementation_type::non_blocking; } if (impl.flags_ & implementation_type::user_set_linger) @@ -214,12 +225,12 @@ public: { reactor_.close_descriptor(impl.socket_, impl.reactor_data_); - if (impl.flags_ & implementation_type::internal_non_blocking) + if (impl.flags_ & implementation_type::non_blocking) { ioctl_arg_type non_blocking = 0; boost::system::error_code ignored_ec; socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ignored_ec); - impl.flags_ &= ~implementation_type::internal_non_blocking; + impl.flags_ &= ~implementation_type::non_blocking; } if (socket_ops::close(impl.socket_, ec) == socket_error_retval) @@ -433,11 +444,35 @@ public: if (command.name() == static_cast(FIONBIO)) { + // Flags are manipulated in a temporary variable so that the socket + // implementation is not updated unless the ioctl operation succeeds. + unsigned char new_flags = impl.flags_; if (command.get()) - impl.flags_ |= implementation_type::user_set_non_blocking; + new_flags |= implementation_type::user_set_non_blocking; else - impl.flags_ &= ~implementation_type::user_set_non_blocking; - ec = boost::system::error_code(); + new_flags &= ~implementation_type::user_set_non_blocking; + + // Perform ioctl on socket if the non-blocking state has changed. + if (!(impl.flags_ & implementation_type::non_blocking) + && (new_flags & implementation_type::non_blocking)) + { + ioctl_arg_type non_blocking = 1; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec); + } + else if ((impl.flags_ & implementation_type::non_blocking) + && !(new_flags & implementation_type::non_blocking)) + { + ioctl_arg_type non_blocking = 0; + socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec); + } + else + { + ec = boost::system::error_code(); + } + + // Update socket implementation's flags only if successful. + if (!ec) + impl.flags_ = new_flags; } else { @@ -530,18 +565,6 @@ public: return 0; } - // Make socket non-blocking if user wants non-blocking. - if (impl.flags_ & implementation_type::user_set_non_blocking) - { - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - ioctl_arg_type non_blocking = 1; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - return 0; - impl.flags_ |= implementation_type::internal_non_blocking; - } - } - // Send the data. for (;;) { @@ -684,12 +707,15 @@ public: // Make socket non-blocking. if (!(impl.flags_ & implementation_type::internal_non_blocking)) { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + if (!(impl.flags_ & implementation_type::non_blocking)) { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; + ioctl_arg_type non_blocking = 1; + boost::system::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->get_io_service().post(bind_handler(handler, ec, 0)); + return; + } } impl.flags_ |= implementation_type::internal_non_blocking; } @@ -773,18 +799,6 @@ public: boost::asio::buffer_size(buffer)); } - // Make socket non-blocking if user wants non-blocking. - if (impl.flags_ & implementation_type::user_set_non_blocking) - { - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - ioctl_arg_type non_blocking = 1; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - return 0; - impl.flags_ |= implementation_type::internal_non_blocking; - } - } - // Send the data. for (;;) { @@ -912,12 +926,15 @@ public: // Make socket non-blocking. if (!(impl.flags_ & implementation_type::internal_non_blocking)) { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + if (!(impl.flags_ & implementation_type::non_blocking)) { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; + ioctl_arg_type non_blocking = 1; + boost::system::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->get_io_service().post(bind_handler(handler, ec, 0)); + return; + } } impl.flags_ |= implementation_type::internal_non_blocking; } @@ -981,18 +998,6 @@ public: return 0; } - // Make socket non-blocking if user wants non-blocking. - if (impl.flags_ & implementation_type::user_set_non_blocking) - { - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - ioctl_arg_type non_blocking = 1; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - return 0; - impl.flags_ |= implementation_type::internal_non_blocking; - } - } - // Receive some data. for (;;) { @@ -1148,12 +1153,15 @@ public: // Make socket non-blocking. if (!(impl.flags_ & implementation_type::internal_non_blocking)) { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + if (!(impl.flags_ & implementation_type::non_blocking)) { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; + ioctl_arg_type non_blocking = 1; + boost::system::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->get_io_service().post(bind_handler(handler, ec, 0)); + return; + } } impl.flags_ |= implementation_type::internal_non_blocking; } @@ -1225,18 +1233,6 @@ public: boost::asio::buffer_size(buffer)); } - // Make socket non-blocking if user wants non-blocking. - if (impl.flags_ & implementation_type::user_set_non_blocking) - { - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - ioctl_arg_type non_blocking = 1; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - return 0; - impl.flags_ |= implementation_type::internal_non_blocking; - } - } - // Receive some data. for (;;) { @@ -1385,12 +1381,15 @@ public: // Make socket non-blocking. if (!(impl.flags_ & implementation_type::internal_non_blocking)) { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + if (!(impl.flags_ & implementation_type::non_blocking)) { - this->get_io_service().post(bind_handler(handler, ec, 0)); - return; + ioctl_arg_type non_blocking = 1; + boost::system::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->get_io_service().post(bind_handler(handler, ec, 0)); + return; + } } impl.flags_ |= implementation_type::internal_non_blocking; } @@ -1450,18 +1449,6 @@ public: return ec; } - // Make socket non-blocking if user wants non-blocking. - if (impl.flags_ & implementation_type::user_set_non_blocking) - { - if (!(impl.flags_ & implementation_type::internal_non_blocking)) - { - ioctl_arg_type non_blocking = 1; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - return ec; - impl.flags_ |= implementation_type::internal_non_blocking; - } - } - // Accept a socket. for (;;) { @@ -1623,12 +1610,15 @@ public: // Make socket non-blocking. if (!(impl.flags_ & implementation_type::internal_non_blocking)) { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + if (!(impl.flags_ & implementation_type::non_blocking)) { - this->get_io_service().post(bind_handler(handler, ec)); - return; + ioctl_arg_type non_blocking = 1; + boost::system::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->get_io_service().post(bind_handler(handler, ec)); + return; + } } impl.flags_ |= implementation_type::internal_non_blocking; } @@ -1652,18 +1642,30 @@ public: return ec; } - if (impl.flags_ & implementation_type::internal_non_blocking) - { - // Mark the socket as blocking while we perform the connect. - ioctl_arg_type non_blocking = 0; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) - return ec; - impl.flags_ &= ~implementation_type::internal_non_blocking; - } - // Perform the connect operation. socket_ops::connect(impl.socket_, peer_endpoint.data(), peer_endpoint.size(), ec); + if (ec != boost::asio::error::in_progress + && ec != boost::asio::error::would_block) + { + // The connect operation finished immediately. + return ec; + } + + // Wait for socket to become ready. + if (socket_ops::poll_connect(impl.socket_, ec) < 0) + return ec; + + // Get the error code from the connect operation. + int connect_error = 0; + size_t connect_error_len = sizeof(connect_error); + if (socket_ops::getsockopt(impl.socket_, SOL_SOCKET, SO_ERROR, + &connect_error, &connect_error_len, ec) == socket_error_retval) + return ec; + + // Return the result of the connect operation. + ec = boost::system::error_code(connect_error, + boost::asio::error::get_system_category()); return ec; } @@ -1731,12 +1733,15 @@ public: // Make socket non-blocking. if (!(impl.flags_ & implementation_type::internal_non_blocking)) { - ioctl_arg_type non_blocking = 1; - boost::system::error_code ec; - if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + if (!(impl.flags_ & implementation_type::non_blocking)) { - this->get_io_service().post(bind_handler(handler, ec)); - return; + ioctl_arg_type non_blocking = 1; + boost::system::error_code ec; + if (socket_ops::ioctl(impl.socket_, FIONBIO, &non_blocking, ec)) + { + this->get_io_service().post(bind_handler(handler, ec)); + return; + } } impl.flags_ |= implementation_type::internal_non_blocking; } diff --git a/include/boost/asio/detail/socket_ops.hpp b/include/boost/asio/detail/socket_ops.hpp index f912e536..e61cf86c 100644 --- a/include/boost/asio/detail/socket_ops.hpp +++ b/include/boost/asio/detail/socket_ops.hpp @@ -702,6 +702,32 @@ inline int poll_write(socket_type s, boost::system::error_code& ec) #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) } +inline int poll_connect(socket_type s, boost::system::error_code& ec) +{ +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + FD_SET write_fds; + FD_ZERO(&write_fds); + FD_SET(s, &write_fds); + FD_SET except_fds; + FD_ZERO(&except_fds); + FD_SET(s, &except_fds); + clear_error(ec); + int result = error_wrapper(::select(s, 0, &write_fds, &except_fds, 0), ec); +# if defined(UNDER_CE) + if (result >= 0) + clear_error(ec); +# endif + return result; +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + fds.revents = 0; + clear_error(ec); + return error_wrapper(::poll(&fds, 1, -1), ec); +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +} + inline const char* inet_ntop(int af, const void* src, char* dest, size_t length, unsigned long scope_id, boost::system::error_code& ec) {