diff --git a/include/boost/asio/detail/reactive_descriptor_service.hpp b/include/boost/asio/detail/reactive_descriptor_service.hpp index a3a087d6..536b96a5 100644 --- a/include/boost/asio/detail/reactive_descriptor_service.hpp +++ b/include/boost/asio/detail/reactive_descriptor_service.hpp @@ -68,7 +68,7 @@ public: // The descriptor has been set non-blocking. internal_non_blocking = 2, - // Helper "flag" used to determine whether the socket is non-blocking. + // Helper "flag" used to determine whether the descriptor is non-blocking. non_blocking = user_set_non_blocking | internal_non_blocking }; @@ -214,19 +214,31 @@ public: return ec; } - if (command.name() == static_cast(FIONBIO)) + descriptor_ops::ioctl(impl.descriptor_, command.name(), + static_cast(command.data()), ec); + + // When updating the non-blocking mode we always perform the ioctl syscall, + // even if the flags would otherwise indicate that the descriptor is + // already in the correct state. This ensures that the underlying + // descriptor is put into the state that has been requested by the user. If + // the ioctl syscall was successful then we need to update the flags to + // match. + if (!ec && command.name() == static_cast(FIONBIO)) { - if (command.get()) + if (*static_cast(command.data())) + { impl.flags_ |= implementation_type::user_set_non_blocking; + } else - impl.flags_ &= ~implementation_type::user_set_non_blocking; - ec = boost::system::error_code(); - } - else - { - descriptor_ops::ioctl(impl.descriptor_, command.name(), - static_cast(command.data()), ec); + { + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + impl.flags_ &= ~(implementation_type::user_set_non_blocking + | implementation_type::internal_non_blocking); + } } + return ec; } diff --git a/include/boost/asio/detail/reactive_socket_service.hpp b/include/boost/asio/detail/reactive_socket_service.hpp index 2653facf..884ce759 100644 --- a/include/boost/asio/detail/reactive_socket_service.hpp +++ b/include/boost/asio/detail/reactive_socket_service.hpp @@ -445,43 +445,30 @@ public: return ec; } - if (command.name() == static_cast(FIONBIO)) + socket_ops::ioctl(impl.socket_, command.name(), + static_cast(command.data()), ec); + + // When updating the non-blocking mode we always perform the ioctl + // syscall, even if the flags would otherwise indicate that the socket is + // already in the correct state. This ensures that the underlying socket + // is put into the state that has been requested by the user. If the ioctl + // syscall was successful then we need to update the flags to match. + if (!ec && 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 (*static_cast(command.data())) - new_flags |= implementation_type::user_set_non_blocking; - else - 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); + impl.flags_ |= implementation_type::user_set_non_blocking; } else { - ec = boost::system::error_code(); + // Clearing the non-blocking mode always overrides any internally-set + // non-blocking flag. Any subsequent asynchronous operations will need + // to re-enable non-blocking I/O. + impl.flags_ &= ~(implementation_type::user_set_non_blocking + | implementation_type::internal_non_blocking); } + } - // Update socket implementation's flags only if successful. - if (!ec) - impl.flags_ = new_flags; - } - else - { - socket_ops::ioctl(impl.socket_, command.name(), - static_cast(command.data()), ec); - } return ec; }