Boost async_* functions and shared_ptr's
In short, boost::bind
creates a copy of the boost::shared_ptr<Connection>
that is returned from shared_from_this()
, and boost::asio
may create a copy of the handler. The copy of the handler will remain alive until one of the following occurs:
- The handler has been called by a thread from which the service's
run()
,run_one()
,poll()
orpoll_one()
member function has been invoked. - The
io_service
is destroyed. - The
io_service::service
that owns the handler is shutdown viashutdown_service()
.
Here are the relevant excerpts from the documentation:
boost::bind documentation:
The arguments that
bind
takes are copied and held internally by the returned function object.boost::asio
io_service::post
:The
io_service
guarantees that the handler will only be called in a thread in which therun()
,run_one()
,poll()
orpoll_one()
member functions is currently being invoked. [...] Theio_service
will make a copy of the handler object as required.boost::asio
io_service::~io_service
:Uninvoked handler objects that were scheduled for deferred invocation on the
io_service
, or any associated strand, are destroyed.
Where an object's lifetime is tied to the lifetime of a connection (or some other sequence of asynchronous operations), ashared_ptr
to the object would be bound into the handlers for all asynchronous operations associated with it. [...] When a single connection ends, all associated asynchronous operations complete. The corresponding handler objects are destroyed, and allshared_ptr
references to the objects are destroyed.
While dated (2007), the Networking Library Proposal for TR2 (Revision 1) was derived from Boost.Asio. Section 5.3.2.7. Requirements on asynchronous operations
provides some details for the arguments to async_
functions:
In this clause, an asynchronous operation is initiated by a function that is named with the prefix
async_
. These functions shall be known as initiating functions. [...] The library implementation may make copies of the handler argument, and
the original handler argument and all copies are interchangeable.The lifetime of arguments to initiating functions shall be treated as follows:
- If the parameter is declared as a const reference or by-value [...] the implementation may make copies of the argument, and all copies shall be destroyed no later than immediately after invocation of the handler.
[...] Any calls made by the library implementation to functions associated with the initiating function's arguments will be performed such that calls occur in a sequence call1 to calln, where for all i, 1 ≤ i < n, calli precedes call i+1.
Thus:
- The implementation may create a copy of the handler. In the example, the copied handler will create a copy of the
shared_ptr<Connection>
, increasing the reference count of theConnection
instance while the copies of handler remain alive. - The implementation may destroy the handler prior to invoking handler. This occurs if the async operation is outstanding when
io_serive::service
is shutdown or theio_service
is destroyed. In the example, the copies of handler will be destroyed, decreasing the reference count ofConnection
, and potentially causing theConnection
instance to be destroyed. - If handler is invoked, then all copies of handler will immediately be destroyed once execution returns from the handler. Again, the copies of handler will be destroyed, decreasing the reference count of
Connection
, and potentially causing it to be destroyed. - The functions associated with the
asnyc_
's arguments, will be executed sequentially, and not concurrent. This includesio_handler_deallocate
andio_handler_invoke
. This guarantees that the handler will not be deallocated while the handler is being invoked. In most areas of theboost::asio
implementation, the handler is copied or moved to stack variables, allowing the destruction to occur once execution exits the block in which it was declared. In the example, this ensures that the reference count forConnection
will be at least one during the invocation of the handler.
C++ boost::asio how to properly use std::shared_ptr on async functions
@Mendez I have several introductory samples when people ran into the requirement for this "ASIO pattern".
You could look at them, because I do explain the pattern and why it's introduced:
- boost asio deadline_timer async_wait(N seconds) twice within N seconds cause operation canceled
C++: Boost.Asio: Start SSL Server session in a new thread
This one was live-coded, so you could watch the recorded sessions
Member std::future preventing boost::shared_ptr from going out of scope
Your bind
is holding onto a shared_ptr
longer than it needs to. You can change it to explicitly release the captured pointer when it is done.
f_read = std::async(std::launch::async, [=, self = shared_from_this()]() mutable { self->handle_add_data(bytes_transferred); self = nullptr; });
Prior to C++14, you need to do the initialisation as a local that gets captured
auto self = shared_from_this();
f_read = std::async(std::launch::async, [=]() mutable { self->handle_add_data(bytes_transferred); self = nullptr; });
Does an instance of boost::bind retain a shared_ptr for it's lifetime?
Yes, boost::bind
(as well as std::bind
) creates functor that holds copies of the parameters, but one can't count on the number of copies made. So, all you can assume is that at the point (B) the number of references is greater than it was at point (A). Certainly, when the functor gets detroyed, all the shared_ptr
's that it held are released.
Reserving memory for asynchronous send buffers (boost asio sockets)
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.
Related Topics
How Define an Array of Function Pointers in C
How to Calculate a Time Difference in C++
What's the Precedence of Comma Operator Inside Conditional Operator in C++
C++:Creating an Array with a Size Entered by the User
Override Compile Flags for Single Files
C++ Singleton VS. Global Static Object
Std::String Length() and Size() Member Functions
Gcc Linker Can't Find Standard Library
How to Validate Input Using Scanf
Why Is the Empty Base Class Optimization (Ebo) Is Not Working in Msvc
How to Navigate Through a Vector Using Iterators? (C++)
What Are Declarations and Declarators and How Are Their Types Interpreted by the Standard
C++ Template Copy Constructor on Template Class
How to Store Functional Objects with Different Signatures in a Container
Code Runs Perfect in G++ But Not in Xcode - Cannot Find File