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
* Simplify websocket::detail::prng
* 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
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 const& alloc) noexcept
: boost::empty_value<base_alloc_type>(
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(alloc_traits::max_size(this->get()))
, out_(list_.end())
@ -498,8 +463,8 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer(
std::size_t limit,
Allocator const& alloc) noexcept
: boost::empty_value<
base_alloc_type>(boost::empty_init_t(), alloc)
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(limit)
, out_(list_.end())
{
@ -509,7 +474,7 @@ template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer&& other) noexcept
: boost::empty_value<base_alloc_type>(
: boost::empty_value<Allocator>(
boost::empty_init_t(), std::move(other.get()))
, max_(other.max_)
, in_size_(boost::exchange(other.in_size_, 0))
@ -529,8 +494,8 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer&& other,
Allocator const& alloc)
: boost::empty_value<
base_alloc_type>(boost::empty_init_t(), alloc)
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(other.max_)
{
if(this->get() != other.get())
@ -561,7 +526,7 @@ template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer const& other)
: boost::empty_value<base_alloc_type>(
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc_traits::
select_on_container_copy_construction(
other.get()))
@ -576,7 +541,7 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer const& other,
Allocator const& alloc)
: boost::empty_value<base_alloc_type>(
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(other.max_)
, out_(list_.end())
@ -600,8 +565,8 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other,
allocator_type const& alloc)
: boost::empty_value<
base_alloc_type>(boost::empty_init_t(), alloc)
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(other.max_)
, out_(list_.end())
{
@ -991,6 +956,7 @@ consume(size_type n) noexcept
{
if(list_.empty())
return;
auto a = rebind_type{this->get()};
for(;;)
{
if(list_.begin() != out_)
@ -1013,8 +979,8 @@ consume(size_type n) noexcept
list_.erase(list_.iterator_to(e));
auto const len = sizeof(e) + e.size();
e.~element();
alloc_traits::deallocate(this->get(),
reinterpret_cast<char*>(&e), len);
alloc_traits::deallocate(a,
reinterpret_cast<align_type*>(&e), len);
#if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
@ -1215,10 +1181,13 @@ void
basic_multi_buffer<Allocator>::
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();
alloc_traits::deallocate(this->get(),
reinterpret_cast<char*>(&e), len);
alloc_traits::deallocate(a,
reinterpret_cast<align_type*>(&e), n);
}
template<class Allocator>
@ -1230,9 +1199,11 @@ alloc(std::size_t size) ->
if(size > alloc_traits::max_size(this->get()))
BOOST_THROW_EXCEPTION(std::length_error(
"A basic_multi_buffer exceeded the allocator's maximum size"));
return *::new(alloc_traits::allocate(
this->get(),
sizeof(element) + size)) element(size);
auto a = rebind_type{this->get()};
auto const p = alloc_traits::allocate(a,
(sizeof(element) + size + sizeof(align_type) - 1) /
sizeof(align_type));
return *(::new(p) element(size));
}
template<class Allocator>

View File

@ -15,6 +15,7 @@
#include <boost/asio/buffer.hpp>
#include <boost/core/empty_value.hpp>
#include <boost/intrusive/list.hpp>
#include <boost/type_traits/type_with_alignment.hpp>
#include <iterator>
#include <limits>
#include <memory>
@ -62,14 +63,13 @@ namespace beast {
template<class Allocator>
class basic_multi_buffer
#if ! BOOST_BEAST_DOXYGEN
: private boost::empty_value<
typename detail::allocator_traits<Allocator>::
template rebind_alloc<char>>
: private boost::empty_value<Allocator>
#endif
{
using base_alloc_type = typename
detail::allocator_traits<Allocator>::
template rebind_alloc<char>;
// Fancy pointers are not supported
static_assert(std::is_pointer<typename
std::allocator_traits<Allocator>::pointer>::value,
"Allocator must use regular pointers");
static bool constexpr default_nothrow =
std::is_nothrow_default_constructible<Allocator>::value;
@ -77,19 +77,61 @@ class basic_multi_buffer
// Storage for the list of buffers representing the input
// and output sequences. The allocation for each element
// 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>
class readable_bytes;
using alloc_traits =
beast::detail::allocator_traits<base_alloc_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
detail::allocator_traits<Allocator>::size_type;
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
alloc_traits::propagate_on_container_move_assignment;