forked from boostorg/beast
fix #215 This change guarantees that temporary memory allocated through the asio hooks by the Beast implementation is deallocated before invoking the final handler when performing composed operations. The change is accomplished by replacing std::shared_ptr with a thread-safe custom container handler_ptr to manage composed operation state. The container tracks other instances which manage the same object and resets them in a safe way before invoking the final handler. handler_ptr is provided as a public interface so that users of this library can utilize the same idiom to write their own composed operations.
174 lines
4.7 KiB
C++
174 lines
4.7 KiB
C++
//
|
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
|
|
#ifndef BEAST_HANDLER_PTR_HPP
|
|
#define BEAST_HANDLER_PTR_HPP
|
|
|
|
#include <beast/core/detail/type_traits.hpp>
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
#include <utility>
|
|
|
|
namespace beast {
|
|
|
|
/** A smart pointer container.
|
|
|
|
This is a smart pointer that retains shared ownership of an
|
|
object through a pointer. Memory is managed using the allocation
|
|
and deallocation functions associated with a completion handler,
|
|
which is also stored in the object. The object is destroyed and
|
|
its memory deallocated when one of the following happens:
|
|
|
|
@li The function @ref invoke is called.
|
|
|
|
@li The function @ref release_handler is called
|
|
|
|
@li The last remaining container owning the object is destroyed
|
|
|
|
Objects of this type are used in the implementation of
|
|
composed operations. Typically the composed operation's shared
|
|
state is managed by the @ref handler_ptr and an allocator
|
|
associated with the final handler is used to create the managed
|
|
object.
|
|
|
|
@note The reference count is stored using a 16 bit unsigned
|
|
integer. Making more than 2^16 copies of one object results
|
|
in undefined behavior.
|
|
*/
|
|
template<class T, class Handler>
|
|
class handler_ptr
|
|
{
|
|
struct P
|
|
{
|
|
T* t;
|
|
std::atomic<std::uint16_t> n;
|
|
|
|
// There's no way to put the handler anywhere else
|
|
// without exposing ourselves to race conditions
|
|
// and all sorts of ugliness.
|
|
// See:
|
|
// https://github.com/vinniefalco/Beast/issues/215
|
|
Handler handler;
|
|
|
|
template<class DeducedHandler, class... Args>
|
|
P(DeducedHandler&& handler, Args&&... args);
|
|
};
|
|
|
|
P* p_;
|
|
|
|
template<class DeducedHandler, class... Args>
|
|
handler_ptr(int, DeducedHandler&& handler, Args&&... args);
|
|
|
|
public:
|
|
/// The type of handler this object stores
|
|
using handler_type = Handler;
|
|
|
|
/// Copy assignment (disallowed).
|
|
handler_ptr& operator=(handler_ptr const&) = delete;
|
|
|
|
/** Destructs the owned object if no more @ref handler_ptr link to it.
|
|
|
|
If `*this` owns an object and it is the last @ref handler_ptr
|
|
owning it, the object is destroyed and the memory deallocated
|
|
using the associated deallocator.
|
|
*/
|
|
~handler_ptr();
|
|
|
|
/** Move constructor.
|
|
|
|
When this call returns, the moved-from container
|
|
will have no owned object.
|
|
*/
|
|
handler_ptr(handler_ptr&& other);
|
|
|
|
/// Copy constructor
|
|
handler_ptr(handler_ptr const& other);
|
|
|
|
/// Returns a reference to the handler
|
|
handler_type&
|
|
handler() const
|
|
{
|
|
return p_->handler;
|
|
}
|
|
|
|
/// Returns a pointer to the owned object
|
|
T*
|
|
get() const
|
|
{
|
|
return p_->t;
|
|
}
|
|
|
|
/// Return a reference to the owned object.
|
|
T&
|
|
operator*() const
|
|
{
|
|
return *get();
|
|
}
|
|
|
|
/// Return a pointer to the owned object.
|
|
T*
|
|
operator->() const
|
|
{
|
|
return get();
|
|
}
|
|
|
|
/** Release ownership of the handler
|
|
|
|
If `*this` owns an object, it is first destroyed.
|
|
|
|
@return The released handler.
|
|
*/
|
|
handler_type
|
|
release_handler();
|
|
|
|
/** Invoke the handler in the owned object.
|
|
|
|
This function invokes the handler in the owned object
|
|
with a forwarded argument list. Before the invocation,
|
|
the owned object is destroyed, satisfying the
|
|
deallocation-before-invocation Asio guarantee. All
|
|
instances of @ref handler_ptr which refer to the
|
|
same owned object will be reset, including this instance.
|
|
*/
|
|
template<class... Args>
|
|
void
|
|
invoke(Args&&... args);
|
|
|
|
// VFALCO The free function interface works around
|
|
// a horrible Visual Studio 15 Update 3 bug
|
|
|
|
/** Construct a new `handler_ptr`.
|
|
|
|
@param handler The handler. The allocator associated with
|
|
the handler will be used to allocate memory for the owned
|
|
object. This argument will be forwarded to the owned object's
|
|
constructor.
|
|
|
|
@param args Optional arguments forwarded to
|
|
the owned object's constructor.
|
|
*/
|
|
/** @{ */
|
|
template<class U, class CompletionHandler, class... Args>
|
|
friend
|
|
handler_ptr<U, CompletionHandler>
|
|
make_handler_ptr(
|
|
CompletionHandler&& handler, Args&&... args);
|
|
|
|
template<class U, class CompletionHandler, class... Args>
|
|
friend
|
|
handler_ptr<U, CompletionHandler>
|
|
make_handler_ptr(
|
|
CompletionHandler const& handler, Args&&... args);
|
|
/** @} */
|
|
};
|
|
|
|
} // beast
|
|
|
|
#include <beast/core/impl/handler_ptr.ipp>
|
|
|
|
#endif
|