forked from boostorg/beast
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:
1 parent
1a26484eca
commit
01e1fa2dc9
21 files changed
+574
-322
No files matched your search
@@ -11,6 +11,7 @@
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
@@ -23,34 +24,28 @@ template<class Stream,
|
||||
class DynamicBuffer, class Parser, class Handler>
|
||||
class parse_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
Parser& p;
|
||||
Handler h;
|
||||
bool got_some = false;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& sb_, Parser& p_)
|
||||
: s(s_)
|
||||
: cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, p(p_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
BOOST_ASSERT(! p.complete());
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
parse_op(parse_op&&) = default;
|
||||
@@ -58,7 +53,7 @@ public:
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
parse_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
: d_(make_handler_ptr<data, Handler>(
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
@@ -74,7 +69,7 @@ public:
|
||||
std::size_t size, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -82,7 +77,7 @@ public:
|
||||
void* p, std::size_t size, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -96,7 +91,7 @@ public:
|
||||
void asio_handler_invoke(Function&& f, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -214,7 +209,7 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
@@ -27,9 +28,6 @@ template<class Stream, class DynamicBuffer,
|
||||
class Handler>
|
||||
class read_header_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
using parser_type =
|
||||
header_parser_v1<isRequest, Fields>;
|
||||
|
||||
@@ -38,29 +36,26 @@ class read_header_op
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
Handler h;
|
||||
bool started = false;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& sb_, message_type& m_)
|
||||
: s(s_)
|
||||
: cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, m(m_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_header_op(read_header_op&&) = default;
|
||||
@@ -69,7 +64,7 @@ public:
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_header_op(
|
||||
DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
: d_(make_handler_ptr<data, Handler>(
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
@@ -84,7 +79,7 @@ public:
|
||||
std::size_t size, read_header_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -92,7 +87,7 @@ public:
|
||||
void* p, std::size_t size, read_header_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -106,7 +101,7 @@ public:
|
||||
void asio_handler_invoke(Function&& f, read_header_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -135,7 +130,7 @@ operator()(error_code ec, bool again)
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
@@ -206,9 +201,6 @@ template<class Stream, class DynamicBuffer,
|
||||
class Handler>
|
||||
class read_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
using parser_type =
|
||||
parser_v1<isRequest, Body, Fields>;
|
||||
|
||||
@@ -217,29 +209,26 @@ class read_op
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
Handler h;
|
||||
bool started = false;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& sb_, message_type& m_)
|
||||
: s(s_)
|
||||
: cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, m(m_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
@@ -247,7 +236,7 @@ public:
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
: d_(make_handler_ptr<data, Handler>(
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
@@ -262,7 +251,7 @@ public:
|
||||
std::size_t size, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -270,7 +259,7 @@ public:
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -284,7 +273,7 @@ public:
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -313,7 +302,7 @@ operator()(error_code ec, bool again)
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/core/write_dynabuf.hpp>
|
||||
@@ -99,30 +100,24 @@ namespace detail {
|
||||
template<class Stream, class Handler>
|
||||
class write_streambuf_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
streambuf sb;
|
||||
Handler h;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
data(Handler& handler, Stream& s_,
|
||||
streambuf&& sb_)
|
||||
: s(s_)
|
||||
: cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, sb(std::move(sb_))
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_streambuf_op(write_streambuf_op&&) = default;
|
||||
@@ -131,19 +126,13 @@ public:
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_streambuf_op(DeducedHandler&& h, Stream& s,
|
||||
Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
: d_(make_handler_ptr<data, Handler>(
|
||||
std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
explicit
|
||||
write_streambuf_op(std::shared_ptr<data> d)
|
||||
: d_(std::move(d))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
@@ -153,7 +142,7 @@ public:
|
||||
std::size_t size, write_streambuf_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -161,7 +150,7 @@ public:
|
||||
void* p, std::size_t size, write_streambuf_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -175,7 +164,7 @@ public:
|
||||
void asio_handler_invoke(Function&& f, write_streambuf_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -199,7 +188,7 @@ operator()(error_code ec, std::size_t, bool again)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
@@ -301,29 +290,23 @@ template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
// VFALCO How do we use handler_alloc in write_preparation?
|
||||
write_preparation<
|
||||
isRequest, Body, Fields> wp;
|
||||
Handler h;
|
||||
resume_context resume;
|
||||
resume_context copy;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
data(Handler& handler, Stream& s_,
|
||||
message<isRequest, Body, Fields> const& m_)
|
||||
: s(s_)
|
||||
: cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, wp(m_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -382,7 +365,7 @@ class write_op
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
@@ -390,7 +373,7 @@ public:
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
: d_(make_handler_ptr<data, Handler>(
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
@@ -410,7 +393,7 @@ public:
|
||||
}
|
||||
|
||||
explicit
|
||||
write_op(std::shared_ptr<data> d)
|
||||
write_op(handler_ptr<data, Handler> d)
|
||||
: d_(std::move(d))
|
||||
{
|
||||
}
|
||||
@@ -424,7 +407,7 @@ public:
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -432,7 +415,7 @@ public:
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -446,7 +429,7 @@ public:
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -552,9 +535,9 @@ operator()(error_code ec, std::size_t, bool again)
|
||||
break;
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
d.resume = {};
|
||||
d.copy = {};
|
||||
d.resume = {};
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<class SyncWriteStream, class DynamicBuffer>
|
||||
|
||||
Reference in new issue
Block a user