[asio] send blocks with MSG_DONTWAIT (linux)

If i pass the flag MSG_DONTWAIT to basic_stream_socket::send(buffers, flags) the call blocks in the following situation. -> Connection is established (boost::asio::socket_base::send_buffer_size option(2048) is set) -> sending data form local endpoint to remote endpoint -> remote endpoint does not read the data due to software bug/crash etc. -> sendqueue (see netstat) on local endpoint runs full -> send on local endpoint blocks at this point I think the flag MSG_DONTWAIT should avoid this blocking call. If i do the same using C-function send(fd, buf*, len, flag) the call does not block. I have analyzed the code and i think the problem is in send function in asio/detail/reactive_socket_service.hpp socket_ops::send returns but socket_ops::poll_write blocks. I have added the following changes (see comments __SP__) and all works ok for me. Is the blocking desired or where is my mistake? modified code form asio/detail/reactive_socket_service.hpp ---------------------------------------------------------- // Send the given data to the peer. template <typename ConstBufferSequence> size_t send(implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, boost::system::error_code& ec) { if (!is_open(impl)) { ec = boost::asio::error::bad_descriptor; return 0; } // Copy buffers into array. socket_ops::buf bufs[max_buffers]; typename ConstBufferSequence::const_iterator iter = buffers.begin(); typename ConstBufferSequence::const_iterator end = buffers.end(); size_t i = 0; size_t total_buffer_size = 0; for (; iter != end && i < max_buffers; ++iter, ++i) { boost::asio::const_buffer buffer(*iter); socket_ops::init_buf(bufs[i], boost::asio::buffer_cast<const void*>(buffer), boost::asio::buffer_size(buffer)); total_buffer_size += boost::asio::buffer_size(buffer); } // A request to receive 0 bytes on a stream socket is a no-op. if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) { ec = boost::system::error_code(); return 0; } // Send the data. for (;;) { // Try to complete the operation without blocking. int bytes_sent = socket_ops::send(impl.socket_, bufs, i, flags, ec); // Check if operation succeeded. if (bytes_sent >= 0) return bytes_sent; // Operation failed. if ((impl.flags_ & implementation_type::user_set_non_blocking) || (ec != boost::asio::error::would_block && ec != boost::asio::error::try_again)) return 0; // __SP__ // do not poll if flag MSG_DONTWAIT is set if (flags & MSG_DONTWAIT) return 0; // __SP__ // Wait for socket to become ready. if (socket_ops::poll_write(impl.socket_, ec) < 0) return 0; } }

Stefan Pledl wrote:
If i pass the flag MSG_DONTWAIT to basic_stream_socket::send(buffers, flags) the call blocks in the following situation.
-> Connection is established (boost::asio::socket_base::send_buffer_size option(2048) is set) -> sending data form local endpoint to remote endpoint -> remote endpoint does not read the data due to software bug/crash etc. -> sendqueue (see netstat) on local endpoint runs full -> send on local endpoint blocks at this point
I think the flag MSG_DONTWAIT should avoid this blocking call.
If i do the same using C-function send(fd, buf*, len, flag) the call does not block.
I have analyzed the code and i think the problem is in send function in asio/detail/reactive_socket_service.hpp socket_ops::send returns but socket_ops::poll_write blocks. I have added the following changes (see comments __SP__) and all works ok for me. Is the blocking desired or where is my mistake?
modified code form asio/detail/reactive_socket_service.hpp ----------------------------------------------------------
// Send the given data to the peer. template <typename ConstBufferSequence> size_t send(implementation_type& impl, const ConstBufferSequence& buffers, socket_base::message_flags flags, boost::system::error_code& ec) { if (!is_open(impl)) { ec = boost::asio::error::bad_descriptor; return 0; }
// Copy buffers into array. socket_ops::buf bufs[max_buffers]; typename ConstBufferSequence::const_iterator iter = buffers.begin(); typename ConstBufferSequence::const_iterator end = buffers.end(); size_t i = 0; size_t total_buffer_size = 0; for (; iter != end && i < max_buffers; ++iter, ++i) { boost::asio::const_buffer buffer(*iter); socket_ops::init_buf(bufs[i], boost::asio::buffer_cast<const void*>(buffer), boost::asio::buffer_size(buffer)); total_buffer_size += boost::asio::buffer_size(buffer); }
// A request to receive 0 bytes on a stream socket is a no-op. if (impl.protocol_.type() == SOCK_STREAM && total_buffer_size == 0) { ec = boost::system::error_code(); return 0; }
// Send the data. for (;;) { // Try to complete the operation without blocking. int bytes_sent = socket_ops::send(impl.socket_, bufs, i, flags, ec);
// Check if operation succeeded. if (bytes_sent >= 0) return bytes_sent;
// Operation failed. if ((impl.flags_ & implementation_type::user_set_non_blocking) || (ec != boost::asio::error::would_block && ec != boost::asio::error::try_again)) return 0;
// __SP__ // do not poll if flag MSG_DONTWAIT is set if (flags & MSG_DONTWAIT) return 0; // __SP__
// Wait for socket to become ready. if (socket_ops::poll_write(impl.socket_, ec) < 0) return 0; } }
You can switch your socket in the non-blocking mode with basic_stream_socket::non_blocking_io(). Though asio should handle MSG_DONTWAIT as well. BR, Dmitry
participants (2)
-
Dmitry Goncharov
-
Stefan Pledl