Stewart Stewart - 9 months ago 69
C++ Question

Reserving memory for asynchronous send buffers (boost asio sockets)

I'm trying to change the implementation of a fire-and-forget UDP send-function from being synchronous to asynchronous.

The current simplified synchronous function looks like this:

ssize_t UDPTransport::send_to(const char * buffer, size_t bufferSize) {
return mPSocket->send_to(boost::asio::buffer(buffer, bufferSize), mOutputEndpoint);
}


I've got a
thread_group
set up and
io_service::run()
is set to use it. However, the problem is that I have no guarantee that
buffer
will exist after this call has completed. I need to store the contents of the buffer and then know when it is free so that I can re-use it later or delete it. The following is simple, but if I fire off two
send_to
calls, then I have no guarantee that the
handle_send
will be called in the same order and I might
pop
something that is still needed!

ssize_t UDPTransport::send_to(const char * buffer, size_t bufferSize) {

boost::asio::mutable_buffer b1 = boost::asio::buffer(buffer,bufferSize);
mMutex.lock();
mQueue.push(b1);

mPSocket->async_send_to(mQueue.back(), mOutputEndpoint,
boost::bind(&UDPTransport::handle_send, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));

mMutex.unlock();
return bufferSize;
}

void UDPTransport::handle_send(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
mMutex.lock();
mQueue.pop();
mMutex.unlock();
}


What's a good way to store an asynchronous buffer, then clean it up once it's no longer needed?

Reading online an even simpler way may be below, but I don't know if I trust it. Why would a shared pointer decide to not de-allocate itself until after the handler has been called?

ssize_t UDPTransport::send_to(const char * buffer, size_t bufferSize)
{
auto buf = std::make_shared<std::string>(buffer, bufferSize);
mPSocket->async_send_to(boost::asio::buffer(*buf), mOutputEndpoint,
boost::bind(&UDPTransport::handle_send, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
return bufferSize;
}

Answer Source

What I usually do is to wrap it in a class that inherits from std::enable_shared_from_this<> something along the following lines:

class Sender : public std::enable_shared_from_this<Sender> {
 public:
  using CompletionHandler =
      std::function<void(const boost::system::error_code& ec,
                         size_t bytes_transferred,
                         std::shared_ptr<Sender> sender)>;

  ~Sender() = default;

  template<typename... Args>
  static std::shared_ptr<Sender> Create(Args&&... args) {
    return std::shared_ptr<Sender>(new Sender(std::forward<Args>(args)...));
  }

  void AsyncSendTo(const char* buffer, size_t buffer_size,
                   CompletionHandler completion_handler) {
    data_.append(buffer, buffer_size);
    socket.async_send_to(
        boost::asio::buffer(data_), endpoint_,
        [self = shared_from_this(),
         completion_handler = std::move(completion_handler)]
        (const boost::system::error_code& ec,
         size_t bytes_transferred) mutable {
          completion_handler(ec, bytes_transferred, std::move(self));
        });
  }

 private:
  Sender() = default;
  Sender(const Sender&) = delete;
  Sender(Sender&&) = delete;
  Sender& operator=(const Sender&) = delete;
  Sender& operator=(Sender&&) = delete;

  SocketType socket_;
  EndpointType endpoint_;
  std::string data_;
}

Obviously, you have to guarantee the completion_handler's lifetime. But other than that, the completion handler is gonna come back with a valid std::shared_ptr<Sender> whenever it's done and you can do whatever you need with the data Sender carries.

In the example you posted, buf would leave scope and get destroyed on send_to return, unless you first captured it in bind.

Footnote1: Those std::move()s might need to be removed depending on whether your compiler is C++14 compatible when it comes to lambdas.

Footnote2: Stay away from bind unless you absolutely need to exploit its dynamic nature.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download