1

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; } 
1
  • 1
    The shared_ptr solution you linked probably is correct, because it captures the shared_ptr by value, in the lambda. So once the handler is called, it should deallocate itself. However your code with shared_ptr doesn't do that. Commented Aug 16, 2017 at 16:36

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.