Implement asio dealloc-before-invoke guarantee:

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.
This commit is contained in:
Vinnie Falco committed 2017-07-20 08:12:14 -07:00
1 parent 1a26484eca
commit 01e1fa2dc9
21 files changed
+574 -322

No files matched your search

+10 -12
View File
@@ -12,6 +12,7 @@
#include <beast/core/error.hpp>
#include <beast/core/handler_concepts.hpp>
#include <beast/core/handler_alloc.hpp>
#include <beast/core/handler_ptr.hpp>
namespace beast {
@@ -23,25 +24,22 @@ class dynabuf_readstream<
using alloc_type =
handler_alloc<char, Handler>;
// VFALCO What about bool cont for is_continuation?
struct data
{
dynabuf_readstream& srs;
MutableBufferSequence bs;
Handler h;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_,
dynabuf_readstream& srs_,
data(Handler&, dynabuf_readstream& srs_,
MutableBufferSequence const& bs_)
: srs(srs_)
, bs(bs_)
, h(std::forward<DeducedHandler>(h_))
{
}
};
std::shared_ptr<data> d_;
handler_ptr<data, Handler> d_;
public:
read_some_op(read_some_op&&) = default;
@@ -50,7 +48,7 @@ public:
template<class DeducedHandler, class... Args>
read_some_op(DeducedHandler&& h,
dynabuf_readstream& srs, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
: d_(make_handler_ptr<data, Handler>(
std::forward<DeducedHandler>(h), srs,
std::forward<Args>(args)...))
{
@@ -66,7 +64,7 @@ public:
std::size_t size, read_some_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
allocate(size, op->d_.handler());
}
friend
@@ -74,14 +72,14 @@ public:
void* p, std::size_t size, read_some_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
deallocate(p, size, op->d_.handler());
}
friend
bool asio_handler_is_continuation(read_some_op* op)
{
return boost_asio_handler_cont_helpers::
is_continuation(op->d_->h);
is_continuation(op->d_.handler());
}
template<class Function>
@@ -89,7 +87,7 @@ public:
void asio_handler_invoke(Function&& f, read_some_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
invoke(f, op->d_.handler());
}
};
@@ -149,7 +147,7 @@ read_some_op<MutableBufferSequence, Handler>::operator()(
break;
}
}
d.h(ec, bytes_transferred);
d_.invoke(ec, bytes_transferred);
}
//------------------------------------------------------------------------------
+137
View File
@@ -0,0 +1,137 @@
//
// 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_IMPL_HANDLER_PTR_HPP
#define BEAST_IMPL_HANDLER_PTR_HPP
#include <boost/asio/detail/handler_alloc_helpers.hpp>
#include <boost/assert.hpp>
#include <memory>
namespace beast {
template<class T, class Handler>
template<class DeducedHandler, class... Args>
inline
handler_ptr<T, Handler>::P::
P(DeducedHandler&& h, Args&&... args)
: n(1)
, handler(std::forward<DeducedHandler>(h))
{
t = reinterpret_cast<T*>(
boost_asio_handler_alloc_helpers::
allocate(sizeof(T), handler));
try
{
t = new(t) T{handler,
std::forward<Args>(args)...};
}
catch(...)
{
boost_asio_handler_alloc_helpers::
deallocate(t, sizeof(T), handler);
throw;
}
}
template<class T, class Handler>
template<class DeducedHandler, class... Args>
handler_ptr<T, Handler>::
handler_ptr(int, DeducedHandler&& handler, Args&&... args)
: p_(new P(std::forward<DeducedHandler>(handler),
std::forward<Args>(args)...))
{
}
template<class T, class Handler>
handler_ptr<T, Handler>::
~handler_ptr()
{
if(! p_)
return;
if(--p_->n)
return;
if(p_->t)
{
p_->t->~T();
boost_asio_handler_alloc_helpers::
deallocate(p_->t, sizeof(T), p_->handler);
}
delete p_;
}
template<class T, class Handler>
handler_ptr<T, Handler>::
handler_ptr(handler_ptr&& other)
: p_(other.p_)
{
other.p_ = nullptr;
}
template<class T, class Handler>
handler_ptr<T, Handler>::
handler_ptr(handler_ptr const& other)
: p_(other.p_)
{
if(p_)
++p_->n;
}
template<class T, class Handler>
auto
handler_ptr<T, Handler>::
release_handler() ->
handler_type
{
BOOST_ASSERT(p_);
BOOST_ASSERT(p_->t);
p_->t->~T();
boost_asio_handler_alloc_helpers::
deallocate(p_->t, sizeof(T), p_->handler);
p_->t = nullptr;
return std::move(p_->handler);
}
template<class T, class Handler>
template<class... Args>
void
handler_ptr<T, Handler>::
invoke(Args&&... args)
{
BOOST_ASSERT(p_);
BOOST_ASSERT(p_->t);
p_->t->~T();
boost_asio_handler_alloc_helpers::
deallocate(p_->t, sizeof(T), p_->handler);
p_->t = nullptr;
p_->handler(std::forward<Args>(args)...);
}
template<
class T, class CompletionHandler, class... Args>
handler_ptr<T, CompletionHandler>
make_handler_ptr(
CompletionHandler&& handler, Args&&... args)
{
return handler_ptr<T, CompletionHandler>{0,
std::move(handler),
std::forward<Args>(args)...};
}
template<
class T, class CompletionHandler, class... Args>
handler_ptr<T, CompletionHandler>
make_handler_ptr(
CompletionHandler const& handler, Args&&... args)
{
return handler_ptr<T, CompletionHandler>{0,
handler, std::forward<Args>(args)...};
}
} // beast
#endif