mirror of
https://github.com/boostorg/container.git
synced 2025-08-02 22:14:26 +02:00
Fixes #145 ("Allocations not handled correctly in some cases of vector move with unequal allocators")
This commit is contained in:
@@ -73,12 +73,10 @@ instructions, that's already been done for you.
|
|||||||
|
|
||||||
[section:tested_compilers Tested compilers]
|
[section:tested_compilers Tested compilers]
|
||||||
|
|
||||||
[*Boost.Container] requires a decent C++98 compatibility. Some compilers known to work are:
|
[*Boost.Container] requires a decent C++03 compatibility. Some compilers known to work are:
|
||||||
|
|
||||||
* Visual C++ >= 7.1.
|
* Visual C++ >= 10.0
|
||||||
* GCC >= 4.1.
|
* GCC >= 4.8
|
||||||
|
|
||||||
[warning GCC < 4.3 and MSVC < 9.0 are deprecated and will be removed in the next version.]
|
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
@@ -1322,7 +1320,8 @@ use [*Boost.Container]? There are several reasons for that:
|
|||||||
[section:release_notes_boost_1_74_00 Boost 1.74 Release]
|
[section:release_notes_boost_1_74_00 Boost 1.74 Release]
|
||||||
|
|
||||||
* Fixed bugs:
|
* Fixed bugs:
|
||||||
* [@https://github.com/boostorg/container/issues/144 GitHub #148: ['"GCC suggest-override warnings"]].
|
* [@https://github.com/boostorg/container/issues/144 GitHub #144: ['"GCC suggest-override warnings"]].
|
||||||
|
* [@https://github.com/boostorg/container/issues/145 GitHub #145: ['"Allocations not handled correctly in some cases of vector move with unequal allocators"]].
|
||||||
* [@https://github.com/boostorg/container/pull/148 GitHub #148: ['"Fix static initialization issues in pmr global resources"]].
|
* [@https://github.com/boostorg/container/pull/148 GitHub #148: ['"Fix static initialization issues in pmr global resources"]].
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@@ -359,35 +359,6 @@ struct vector_alloc_holder
|
|||||||
holder.m_size = holder.m_capacity = 0;
|
holder.m_size = holder.m_capacity = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector_alloc_holder(initial_capacity_t, pointer p, size_type capacity, BOOST_RV_REF(vector_alloc_holder) holder)
|
|
||||||
: allocator_type(BOOST_MOVE_BASE(allocator_type, holder))
|
|
||||||
, m_start(p)
|
|
||||||
, m_size(holder.m_size)
|
|
||||||
, m_capacity(static_cast<stored_size_type>(capacity))
|
|
||||||
{
|
|
||||||
allocator_type &this_alloc = this->alloc();
|
|
||||||
allocator_type &x_alloc = holder.alloc();
|
|
||||||
if(this->is_propagable_from(x_alloc, holder.start(), this_alloc, true)){
|
|
||||||
if(this->m_capacity){
|
|
||||||
this->deallocate(this->m_start, this->m_capacity);
|
|
||||||
}
|
|
||||||
m_start = holder.m_start;
|
|
||||||
m_capacity = holder.m_capacity;
|
|
||||||
holder.m_start = pointer();
|
|
||||||
holder.m_capacity = holder.m_size = 0;
|
|
||||||
}
|
|
||||||
else if(this->m_capacity < holder.m_size){
|
|
||||||
size_type const n = holder.m_size;
|
|
||||||
pointer reuse = pointer();
|
|
||||||
size_type final_cap = n;
|
|
||||||
m_start = this->allocation_command(allocate_new, n, final_cap, reuse);
|
|
||||||
m_capacity = static_cast<stored_size_type>(final_cap);
|
|
||||||
#ifdef BOOST_CONTAINER_VECTOR_ALLOC_STATS
|
|
||||||
this->num_alloc += n != 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vector_alloc_holder(initial_capacity_t, pointer p, size_type n)
|
vector_alloc_holder(initial_capacity_t, pointer p, size_type n)
|
||||||
BOOST_NOEXCEPT_IF(dtl::is_nothrow_default_constructible<allocator_type>::value)
|
BOOST_NOEXCEPT_IF(dtl::is_nothrow_default_constructible<allocator_type>::value)
|
||||||
: allocator_type()
|
: allocator_type()
|
||||||
@@ -1081,10 +1052,12 @@ private:
|
|||||||
//! <b>Complexity</b>: Constant if a == x.get_allocator(), linear otherwise.
|
//! <b>Complexity</b>: Constant if a == x.get_allocator(), linear otherwise.
|
||||||
vector(BOOST_RV_REF(vector) x, const allocator_type &a)
|
vector(BOOST_RV_REF(vector) x, const allocator_type &a)
|
||||||
: m_holder( vector_uninitialized_size, a
|
: m_holder( vector_uninitialized_size, a
|
||||||
, is_propagable_from(x.get_stored_allocator(), x.m_holder.start(), a, true) ? 0 : x.size()
|
//In this allocator move constructor the allocator won't be propagated --v
|
||||||
|
, is_propagable_from(x.get_stored_allocator(), x.m_holder.start(), a, false) ? 0 : x.size()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if(is_propagable_from(x.get_stored_allocator(), x.m_holder.start(), a, true)){
|
//In this allocator move constructor the allocator won't be propagated ---v
|
||||||
|
if(is_propagable_from(x.get_stored_allocator(), x.m_holder.start(), a, false)){
|
||||||
this->m_holder.steal_resources(x.m_holder);
|
this->m_holder.steal_resources(x.m_holder);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -2438,6 +2411,7 @@ private:
|
|||||||
allocator_type &x_alloc = x.m_holder.alloc();
|
allocator_type &x_alloc = x.m_holder.alloc();
|
||||||
const bool propagate_alloc = allocator_traits_type::propagate_on_container_move_assignment::value;
|
const bool propagate_alloc = allocator_traits_type::propagate_on_container_move_assignment::value;
|
||||||
|
|
||||||
|
//In this allocator move constructor the allocator maybe will be propagated -----------------------v
|
||||||
const bool is_propagable_from_x = is_propagable_from(x_alloc, x.m_holder.start(), this_alloc, propagate_alloc);
|
const bool is_propagable_from_x = is_propagable_from(x_alloc, x.m_holder.start(), this_alloc, propagate_alloc);
|
||||||
|
|
||||||
//Resources can be transferred if both allocators are
|
//Resources can be transferred if both allocators are
|
||||||
|
@@ -78,6 +78,7 @@ template< class T
|
|||||||
, bool PropagateOnContMoveAssign
|
, bool PropagateOnContMoveAssign
|
||||||
, bool PropagateOnContSwap
|
, bool PropagateOnContSwap
|
||||||
, bool CopyOnPropagateOnContSwap
|
, bool CopyOnPropagateOnContSwap
|
||||||
|
, bool EqualIfEqualIds
|
||||||
>
|
>
|
||||||
class propagation_test_allocator
|
class propagation_test_allocator
|
||||||
{
|
{
|
||||||
@@ -99,7 +100,8 @@ class propagation_test_allocator
|
|||||||
, PropagateOnContCopyAssign
|
, PropagateOnContCopyAssign
|
||||||
, PropagateOnContMoveAssign
|
, PropagateOnContMoveAssign
|
||||||
, PropagateOnContSwap
|
, PropagateOnContSwap
|
||||||
, CopyOnPropagateOnContSwap> other;
|
, CopyOnPropagateOnContSwap
|
||||||
|
, EqualIfEqualIds> other;
|
||||||
};
|
};
|
||||||
|
|
||||||
propagation_test_allocator select_on_container_copy_construction() const
|
propagation_test_allocator select_on_container_copy_construction() const
|
||||||
@@ -129,7 +131,8 @@ class propagation_test_allocator
|
|||||||
, PropagateOnContCopyAssign
|
, PropagateOnContCopyAssign
|
||||||
, PropagateOnContMoveAssign
|
, PropagateOnContMoveAssign
|
||||||
, PropagateOnContSwap
|
, PropagateOnContSwap
|
||||||
, CopyOnPropagateOnContSwap> &x)
|
, CopyOnPropagateOnContSwap
|
||||||
|
, EqualIfEqualIds> &x)
|
||||||
: id_(x.id_)
|
: id_(x.id_)
|
||||||
, ctr_copies_(x.ctr_copies_+1)
|
, ctr_copies_(x.ctr_copies_+1)
|
||||||
, ctr_moves_(0)
|
, ctr_moves_(0)
|
||||||
@@ -178,11 +181,11 @@ class propagation_test_allocator
|
|||||||
void deallocate(T*p, std::size_t)
|
void deallocate(T*p, std::size_t)
|
||||||
{ delete[] ((char*)p);}
|
{ delete[] ((char*)p);}
|
||||||
|
|
||||||
friend bool operator==(const propagation_test_allocator &, const propagation_test_allocator &)
|
friend bool operator==(const propagation_test_allocator &a, const propagation_test_allocator &b)
|
||||||
{ return true; }
|
{ return EqualIfEqualIds ? a.id_ == b.id_ : true; }
|
||||||
|
|
||||||
friend bool operator!=(const propagation_test_allocator &, const propagation_test_allocator &)
|
friend bool operator!=(const propagation_test_allocator &a, const propagation_test_allocator &b)
|
||||||
{ return false; }
|
{ return EqualIfEqualIds ? a.id_ != b.id_ : false; }
|
||||||
|
|
||||||
void swap(propagation_test_allocator &r)
|
void swap(propagation_test_allocator &r)
|
||||||
{
|
{
|
||||||
@@ -214,12 +217,15 @@ template< class T
|
|||||||
, bool PropagateOnContMoveAssign
|
, bool PropagateOnContMoveAssign
|
||||||
, bool PropagateOnContSwap
|
, bool PropagateOnContSwap
|
||||||
, bool CopyOnPropagateOnContSwap
|
, bool CopyOnPropagateOnContSwap
|
||||||
|
, bool EqualIfEqualIds
|
||||||
>
|
>
|
||||||
unsigned int propagation_test_allocator< T
|
unsigned int propagation_test_allocator< T
|
||||||
, PropagateOnContCopyAssign
|
, PropagateOnContCopyAssign
|
||||||
, PropagateOnContMoveAssign
|
, PropagateOnContMoveAssign
|
||||||
, PropagateOnContSwap
|
, PropagateOnContSwap
|
||||||
, CopyOnPropagateOnContSwap>::unique_id_ = 0;
|
, CopyOnPropagateOnContSwap
|
||||||
|
, EqualIfEqualIds
|
||||||
|
>::unique_id_ = 0;
|
||||||
|
|
||||||
|
|
||||||
} //namespace test {
|
} //namespace test {
|
||||||
|
@@ -110,7 +110,7 @@ template<class Selector>
|
|||||||
bool test_propagate_allocator()
|
bool test_propagate_allocator()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
typedef propagation_test_allocator<char, true, true, true, true> AlwaysPropagate;
|
typedef propagation_test_allocator<char, true, true, true, true, true> AlwaysPropagate;
|
||||||
typedef alloc_propagate_wrapper<char, AlwaysPropagate, Selector> PropagateCont;
|
typedef alloc_propagate_wrapper<char, AlwaysPropagate, Selector> PropagateCont;
|
||||||
typedef typename get_real_stored_allocator<typename PropagateCont::Base>::type StoredAllocator;
|
typedef typename get_real_stored_allocator<typename PropagateCont::Base>::type StoredAllocator;
|
||||||
{
|
{
|
||||||
@@ -213,7 +213,7 @@ bool test_propagate_allocator()
|
|||||||
//Test NeverPropagate allocator propagation
|
//Test NeverPropagate allocator propagation
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
{
|
{
|
||||||
typedef propagation_test_allocator<char, false, false, false, false> NeverPropagate;
|
typedef propagation_test_allocator<char, false, false, false, false, true> NeverPropagate;
|
||||||
typedef alloc_propagate_wrapper<char, NeverPropagate, Selector> NoPropagateCont;
|
typedef alloc_propagate_wrapper<char, NeverPropagate, Selector> NoPropagateCont;
|
||||||
typedef typename get_real_stored_allocator<typename NoPropagateCont::Base>::type StoredAllocator;
|
typedef typename get_real_stored_allocator<typename NoPropagateCont::Base>::type StoredAllocator;
|
||||||
{
|
{
|
||||||
@@ -281,6 +281,15 @@ bool test_propagate_allocator()
|
|||||||
BOOST_TEST (c2.get_stored_allocator().assign_moves_ == 0);
|
BOOST_TEST (c2.get_stored_allocator().assign_moves_ == 0);
|
||||||
BOOST_TEST (c2.get_stored_allocator().swaps_ == 0);
|
BOOST_TEST (c2.get_stored_allocator().swaps_ == 0);
|
||||||
}
|
}
|
||||||
|
//And now allocator argument constructors
|
||||||
|
test_propagate_allocator_allocator_arg<NoPropagateCont>();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
//Don't use unequal ids as unequal allocators -------------------------|,
|
||||||
|
//because swap requires equal allocators v
|
||||||
|
typedef propagation_test_allocator<char, false, false, false, false, false> NeverPropagate;
|
||||||
|
typedef alloc_propagate_wrapper<char, NeverPropagate, Selector> NoPropagateCont;
|
||||||
|
typedef typename get_real_stored_allocator<typename NoPropagateCont::Base>::type StoredAllocator;
|
||||||
{
|
{
|
||||||
//swap
|
//swap
|
||||||
StoredAllocator::reset_unique_id(666);
|
StoredAllocator::reset_unique_id(666);
|
||||||
@@ -302,8 +311,6 @@ bool test_propagate_allocator()
|
|||||||
BOOST_TEST (c.get_stored_allocator().assign_moves_ == 0);
|
BOOST_TEST (c.get_stored_allocator().assign_moves_ == 0);
|
||||||
BOOST_TEST (c.get_stored_allocator().swaps_ == 0);
|
BOOST_TEST (c.get_stored_allocator().swaps_ == 0);
|
||||||
}
|
}
|
||||||
//And now allocator argument constructors
|
|
||||||
test_propagate_allocator_allocator_arg<NoPropagateCont>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return report_errors() == 0;
|
return report_errors() == 0;
|
||||||
|
@@ -59,18 +59,18 @@ public:
|
|||||||
|
|
||||||
template <typename T> friend class SimpleAllocator;
|
template <typename T> friend class SimpleAllocator;
|
||||||
|
|
||||||
friend bool operator == (const SimpleAllocator &, const SimpleAllocator &)
|
friend bool operator == (const SimpleAllocator &a, const SimpleAllocator &b)
|
||||||
{ return true; }
|
{ return a.m_state == b.m_state; }
|
||||||
|
|
||||||
friend bool operator != (const SimpleAllocator &, const SimpleAllocator &)
|
friend bool operator != (const SimpleAllocator &a, const SimpleAllocator &b)
|
||||||
{ return false; }
|
{ return a.m_state != b.m_state; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class alloc_int
|
class alloc_int
|
||||||
{
|
{
|
||||||
private: // Not copyable
|
private: // Not copyable
|
||||||
|
|
||||||
BOOST_MOVABLE_BUT_NOT_COPYABLE(alloc_int)
|
BOOST_COPYABLE_AND_MOVABLE(alloc_int)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef SimpleAllocator<int> allocator_type;
|
typedef SimpleAllocator<int> allocator_type;
|
||||||
@@ -87,13 +87,30 @@ class alloc_int
|
|||||||
other.m_value = -1;
|
other.m_value = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alloc_int(const alloc_int &other)
|
||||||
|
: m_value(other.m_value), m_allocator(boost::move(other.m_allocator))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_int(const alloc_int &other, const allocator_type &allocator)
|
||||||
|
: m_value(other.m_value), m_allocator(allocator)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
alloc_int(int value, const allocator_type &allocator)
|
alloc_int(int value, const allocator_type &allocator)
|
||||||
: m_value(value), m_allocator(allocator)
|
: m_value(value), m_allocator(allocator)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
alloc_int & operator=(BOOST_RV_REF(alloc_int)other)
|
alloc_int & operator=(BOOST_RV_REF(alloc_int)other)
|
||||||
{
|
{
|
||||||
other.m_value = other.m_value;
|
m_value = other.m_value;
|
||||||
|
other.m_value = -1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_int & operator=(const alloc_int &other)
|
||||||
|
{
|
||||||
|
m_value = other.m_value;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,24 +385,47 @@ bool one_level_allocator_propagation_test()
|
|||||||
{
|
{
|
||||||
allocator_type al(SimpleAllocator<value_type>(4));
|
allocator_type al(SimpleAllocator<value_type>(4));
|
||||||
ContainerWrapper c2(al);
|
ContainerWrapper c2(al);
|
||||||
|
{
|
||||||
|
iterator it = c2.emplace(c2.cbegin(), 41);
|
||||||
|
if(!test_value_and_state_equals(*it, 41, 4))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ContainerWrapper c(::boost::move(c2), allocator_type(SimpleAllocator<value_type>(5)));
|
ContainerWrapper c(::boost::move(c2), allocator_type(SimpleAllocator<value_type>(5)));
|
||||||
|
|
||||||
c.clear();
|
if(!test_value_and_state_equals(*c.begin(), 41, 5))
|
||||||
iterator it = c.emplace(c.cbegin(), 42);
|
|
||||||
|
|
||||||
if(!test_value_and_state_equals(*it, 42, 5))
|
|
||||||
return false;
|
return false;
|
||||||
}/*
|
|
||||||
|
{
|
||||||
|
c.clear();
|
||||||
|
iterator it = c.emplace(c.cbegin(), 42);
|
||||||
|
|
||||||
|
if(!test_value_and_state_equals(*it, 42, 5))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
ContainerWrapper c2(allocator_type(SimpleAllocator<value_type>(3)));
|
allocator_type al(SimpleAllocator<value_type>(4));
|
||||||
|
ContainerWrapper c2(al);
|
||||||
|
{
|
||||||
|
iterator it = c2.emplace(c2.cbegin(), 41);
|
||||||
|
if(!test_value_and_state_equals(*it, 41, 4))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ContainerWrapper c(c2, allocator_type(SimpleAllocator<value_type>(5)));
|
ContainerWrapper c(c2, allocator_type(SimpleAllocator<value_type>(5)));
|
||||||
|
|
||||||
c.clear();
|
if(!test_value_and_state_equals(*c.begin(), 41, 5))
|
||||||
iterator it = c.emplace(c.cbegin(), 42);
|
|
||||||
|
|
||||||
if(!test_value_and_state_equals(*it, 42, 5))
|
|
||||||
return false;
|
return false;
|
||||||
}*/
|
|
||||||
|
{
|
||||||
|
c.clear();
|
||||||
|
iterator it = c.emplace(c.cbegin(), 42);
|
||||||
|
|
||||||
|
if(!test_value_and_state_equals(*it, 42, 5))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user