Fix multi_buffer allocation alignment

This commit is contained in:
Vinnie Falco
2019-06-30 19:18:37 -07:00
parent d5cd3e829b
commit ce1c6d3222
3 changed files with 81 additions and 67 deletions

View File

@ -5,6 +5,7 @@ Version 261:
* Remove redundant includes in websocket * Remove redundant includes in websocket
* Simplify websocket::detail::prng * Simplify websocket::detail::prng
* Don't over-allocate in http::basic_fields * Don't over-allocate in http::basic_fields
* Fix multi_buffer allocation alignment
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -87,41 +87,6 @@ namespace beast {
in_pos_ out_pos_ == 0 in_pos_ out_pos_ == 0
out_end_ == 0 out_end_ == 0
*/ */
//------------------------------------------------------------------------------
template<class Allocator>
class basic_multi_buffer<Allocator>::element
: public boost::intrusive::list_base_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
{
using size_type = typename
detail::allocator_traits<Allocator>::size_type;
size_type const size_;
public:
element(element const&) = delete;
explicit
element(size_type n) noexcept
: size_(n)
{
}
size_type
size() const noexcept
{
return size_;
}
char*
data() const noexcept
{
return const_cast<char*>(
reinterpret_cast<char const*>(this + 1));
}
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -486,7 +451,7 @@ template<class Allocator>
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
Allocator const& alloc) noexcept Allocator const& alloc) noexcept
: boost::empty_value<base_alloc_type>( : boost::empty_value<Allocator>(
boost::empty_init_t(), alloc) boost::empty_init_t(), alloc)
, max_(alloc_traits::max_size(this->get())) , max_(alloc_traits::max_size(this->get()))
, out_(list_.end()) , out_(list_.end())
@ -498,8 +463,8 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
std::size_t limit, std::size_t limit,
Allocator const& alloc) noexcept Allocator const& alloc) noexcept
: boost::empty_value< : boost::empty_value<Allocator>(
base_alloc_type>(boost::empty_init_t(), alloc) boost::empty_init_t(), alloc)
, max_(limit) , max_(limit)
, out_(list_.end()) , out_(list_.end())
{ {
@ -509,7 +474,7 @@ template<class Allocator>
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
basic_multi_buffer&& other) noexcept basic_multi_buffer&& other) noexcept
: boost::empty_value<base_alloc_type>( : boost::empty_value<Allocator>(
boost::empty_init_t(), std::move(other.get())) boost::empty_init_t(), std::move(other.get()))
, max_(other.max_) , max_(other.max_)
, in_size_(boost::exchange(other.in_size_, 0)) , in_size_(boost::exchange(other.in_size_, 0))
@ -529,8 +494,8 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
basic_multi_buffer&& other, basic_multi_buffer&& other,
Allocator const& alloc) Allocator const& alloc)
: boost::empty_value< : boost::empty_value<Allocator>(
base_alloc_type>(boost::empty_init_t(), alloc) boost::empty_init_t(), alloc)
, max_(other.max_) , max_(other.max_)
{ {
if(this->get() != other.get()) if(this->get() != other.get())
@ -561,7 +526,7 @@ template<class Allocator>
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
basic_multi_buffer const& other) basic_multi_buffer const& other)
: boost::empty_value<base_alloc_type>( : boost::empty_value<Allocator>(
boost::empty_init_t(), alloc_traits:: boost::empty_init_t(), alloc_traits::
select_on_container_copy_construction( select_on_container_copy_construction(
other.get())) other.get()))
@ -576,7 +541,7 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
basic_multi_buffer const& other, basic_multi_buffer const& other,
Allocator const& alloc) Allocator const& alloc)
: boost::empty_value<base_alloc_type>( : boost::empty_value<Allocator>(
boost::empty_init_t(), alloc) boost::empty_init_t(), alloc)
, max_(other.max_) , max_(other.max_)
, out_(list_.end()) , out_(list_.end())
@ -600,8 +565,8 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer( basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other, basic_multi_buffer<OtherAlloc> const& other,
allocator_type const& alloc) allocator_type const& alloc)
: boost::empty_value< : boost::empty_value<Allocator>(
base_alloc_type>(boost::empty_init_t(), alloc) boost::empty_init_t(), alloc)
, max_(other.max_) , max_(other.max_)
, out_(list_.end()) , out_(list_.end())
{ {
@ -991,6 +956,7 @@ consume(size_type n) noexcept
{ {
if(list_.empty()) if(list_.empty())
return; return;
auto a = rebind_type{this->get()};
for(;;) for(;;)
{ {
if(list_.begin() != out_) if(list_.begin() != out_)
@ -1013,8 +979,8 @@ consume(size_type n) noexcept
list_.erase(list_.iterator_to(e)); list_.erase(list_.iterator_to(e));
auto const len = sizeof(e) + e.size(); auto const len = sizeof(e) + e.size();
e.~element(); e.~element();
alloc_traits::deallocate(this->get(), alloc_traits::deallocate(a,
reinterpret_cast<char*>(&e), len); reinterpret_cast<align_type*>(&e), len);
#if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check(); debug_check();
#endif #endif
@ -1215,10 +1181,13 @@ void
basic_multi_buffer<Allocator>:: basic_multi_buffer<Allocator>::
destroy(element& e) destroy(element& e)
{ {
auto const len = sizeof(e) + e.size(); auto a = rebind_type{this->get()};
auto const n =
(sizeof(element) + e.size() + sizeof(align_type) - 1) /
sizeof(align_type);
e.~element(); e.~element();
alloc_traits::deallocate(this->get(), alloc_traits::deallocate(a,
reinterpret_cast<char*>(&e), len); reinterpret_cast<align_type*>(&e), n);
} }
template<class Allocator> template<class Allocator>
@ -1230,9 +1199,11 @@ alloc(std::size_t size) ->
if(size > alloc_traits::max_size(this->get())) if(size > alloc_traits::max_size(this->get()))
BOOST_THROW_EXCEPTION(std::length_error( BOOST_THROW_EXCEPTION(std::length_error(
"A basic_multi_buffer exceeded the allocator's maximum size")); "A basic_multi_buffer exceeded the allocator's maximum size"));
return *::new(alloc_traits::allocate( auto a = rebind_type{this->get()};
this->get(), auto const p = alloc_traits::allocate(a,
sizeof(element) + size)) element(size); (sizeof(element) + size + sizeof(align_type) - 1) /
sizeof(align_type));
return *(::new(p) element(size));
} }
template<class Allocator> template<class Allocator>

View File

@ -15,6 +15,7 @@
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/core/empty_value.hpp> #include <boost/core/empty_value.hpp>
#include <boost/intrusive/list.hpp> #include <boost/intrusive/list.hpp>
#include <boost/type_traits/type_with_alignment.hpp>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <memory> #include <memory>
@ -62,14 +63,13 @@ namespace beast {
template<class Allocator> template<class Allocator>
class basic_multi_buffer class basic_multi_buffer
#if ! BOOST_BEAST_DOXYGEN #if ! BOOST_BEAST_DOXYGEN
: private boost::empty_value< : private boost::empty_value<Allocator>
typename detail::allocator_traits<Allocator>::
template rebind_alloc<char>>
#endif #endif
{ {
using base_alloc_type = typename // Fancy pointers are not supported
detail::allocator_traits<Allocator>:: static_assert(std::is_pointer<typename
template rebind_alloc<char>; std::allocator_traits<Allocator>::pointer>::value,
"Allocator must use regular pointers");
static bool constexpr default_nothrow = static bool constexpr default_nothrow =
std::is_nothrow_default_constructible<Allocator>::value; std::is_nothrow_default_constructible<Allocator>::value;
@ -77,19 +77,61 @@ class basic_multi_buffer
// Storage for the list of buffers representing the input // Storage for the list of buffers representing the input
// and output sequences. The allocation for each element // and output sequences. The allocation for each element
// contains `element` followed by raw storage bytes. // contains `element` followed by raw storage bytes.
class element; class element
: public boost::intrusive::list_base_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
{
using size_type = typename
detail::allocator_traits<Allocator>::size_type;
size_type const size_;
public:
element(element const&) = delete;
explicit
element(size_type n) noexcept
: size_(n)
{
}
size_type
size() const noexcept
{
return size_;
}
char*
data() const noexcept
{
return const_cast<char*>(
reinterpret_cast<char const*>(this + 1));
}
};
template<bool> template<bool>
class readable_bytes; class readable_bytes;
using alloc_traits = using size_type = typename
beast::detail::allocator_traits<base_alloc_type>; detail::allocator_traits<Allocator>::size_type;
using list_type = typename boost::intrusive::make_list<element,
boost::intrusive::constant_time_size<true>>::type;
using iter = typename list_type::iterator;
using const_iter = typename list_type::const_iterator;
using size_type = typename alloc_traits::size_type; using align_type = typename
boost::type_with_alignment<alignof(element)>::type;
using rebind_type = typename
beast::detail::allocator_traits<Allocator>::
template rebind_alloc<align_type>;
using alloc_traits =
beast::detail::allocator_traits<rebind_type>;
using list_type = typename boost::intrusive::make_list<
element, boost::intrusive::constant_time_size<true>>::type;
using iter = typename list_type::iterator;
using const_iter = typename list_type::const_iterator;
using pocma = typename using pocma = typename
alloc_traits::propagate_on_container_move_assignment; alloc_traits::propagate_on_container_move_assignment;