Boost logo

Boost :

Subject: [boost] [asio] send blocks with MSG_DONTWAIT (linux)
From: Stefan Pledl (stefan.pledl_at_[hidden])
Date: 2009-04-27 05:24:57


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;
  }
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk