Fixed #9940 ("bad bug in intrusive list with safe_link (or auto_unlink) hooks")

This commit is contained in:
Ion Gaztañaga
2014-04-24 10:47:20 +02:00
parent 4aa74df026
commit d7212a1a7c
6 changed files with 103 additions and 134 deletions

View File

@@ -3798,6 +3798,7 @@ to be inserted in intrusive containers are allocated using `std::vector` or `std
* [@https://svn.boost.org/trac/boost/ticket/8468 #8468: Compile error on visual studio 2010/2012 using vector with custom allocator and aligned types]
* [@https://svn.boost.org/trac/boost/ticket/9332 #9332: ['"has_member_function_callable_with.hpp compile error on msvc-12.0"]].
* [@https://svn.boost.org/trac/boost/ticket/9746 #9746: Modern Sun CC compiler detects error in intrusive library header]
* [@https://svn.boost.org/trac/boost/ticket/9940 #9940: bad bug in intrusive list with safe_link (or auto_unlink) hooks]
* Optimized tree rebalancing code to avoid redundant assignments.

View File

@@ -28,7 +28,6 @@
#include <boost/intrusive/detail/ebo_functor_holder.hpp>
#include <boost/intrusive/detail/mpl.hpp>
#include <boost/intrusive/pointer_traits.hpp>
#include <boost/intrusive/detail/clear_on_destructor_base.hpp>
#include <boost/intrusive/detail/function_detector.hpp>
#include <boost/intrusive/detail/utilities.hpp>
#include <boost/intrusive/options.hpp>
@@ -557,7 +556,6 @@ template<class ValueTraits, class VoidKeyComp, class SizeType, bool ConstantTime
class bstree_impl
: public bstbase<ValueTraits, VoidKeyComp, ConstantTimeSize, SizeType, AlgoType>
{
template<class C, bool> friend class detail::clear_on_destructor_base;
public:
/// @cond
typedef bstbase<ValueTraits, VoidKeyComp, ConstantTimeSize, SizeType, AlgoType> data_type;
@@ -618,7 +616,7 @@ class bstree_impl
//!
//! <b>Throws</b>: If value_traits::node_traits::node
//! constructor throws (this does not happen with predefined Boost.Intrusive hooks)
//! or the copy constructorof the value_compare object throws. Basic guarantee.
//! or the copy constructor of the value_compare object throws. Basic guarantee.
explicit bstree_impl( const value_compare &cmp = value_compare()
, const value_traits &v_traits = value_traits())
: data_type(cmp, v_traits)
@@ -642,6 +640,7 @@ class bstree_impl
, const value_traits &v_traits = value_traits())
: data_type(cmp, v_traits)
{
//bstbase releases elements in case of exceptions
if(unique)
this->insert_unique(b, e);
else

View File

@@ -1,40 +0,0 @@
//////} // ///////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2008-2013. 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)
//
// See http://www.boost.org/libs/intrusive for documentation.
//
/////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_INTRUSIVE_DETAIL_CLEAR_ON_DESTRUCTOR_HPP
#define BOOST_INTRUSIVE_DETAIL_CLEAR_ON_DESTRUCTOR_HPP
#include <boost/intrusive/detail/config_begin.hpp>
namespace boost {
namespace intrusive {
namespace detail {
template<class Derived, bool DoClear = true>
class clear_on_destructor_base
{
protected:
~clear_on_destructor_base()
{
static_cast<Derived*>(this)->clear();
}
};
template<class Derived>
class clear_on_destructor_base<Derived, false>
{};
} // namespace detail {
} // namespace intrusive {
} // namespace boost {
#include <boost/intrusive/detail/config_end.hpp>
#endif //#ifndef BOOST_INTRUSIVE_DETAIL_CLEAR_ON_DESTRUCTOR_HPP

View File

@@ -510,7 +510,7 @@ struct bucket_plus_vtraits : public ValueTraits
typedef typename
detail::get_slist_impl_from_supposed_value_traits
<value_traits>::type slist_impl;
typedef typename value_traits::node_traits node_traits;
typedef typename value_traits::node_traits node_traits;
typedef unordered_group_adapter<node_traits> group_traits;
typedef typename slist_impl::iterator siterator;
typedef typename slist_impl::size_type size_type;
@@ -532,6 +532,8 @@ struct bucket_plus_vtraits : public ValueTraits
<const bucket_plus_vtraits>::type const_bucket_value_traits_ptr;
typedef typename detail::unordered_bucket_ptr_impl
<value_traits>::type bucket_ptr;
typedef detail::bool_<detail::optimize_multikey_is_true
<node_traits>::value> optimize_multikey_t;
template<class BucketTraitsType>
bucket_plus_vtraits(const ValueTraits &val_traits, BOOST_FWD_REF(BucketTraitsType) b_traits)
@@ -684,6 +686,20 @@ struct bucket_plus_vtraits : public ValueTraits
const value_type &priv_value_from_slist_node(slist_node_ptr n) const
{ return *this->priv_value_traits().to_value_ptr(detail::dcast_bucket_ptr<node>(n)); }
void priv_clear_buckets(const bucket_ptr buckets_ptr, const size_type bucket_cnt)
{
bucket_ptr buckets_it = buckets_ptr;
for(size_type bucket_i = 0; bucket_i != bucket_cnt; ++buckets_it, ++bucket_i){
if(safemode_or_autounlink){
bucket_plus_vtraits::priv_clear_group_nodes(*buckets_it, optimize_multikey_t());
buckets_it->clear_and_dispose(detail::init_disposer<node_algorithms>());
}
else{
buckets_it->clear();
}
}
}
bucket_traits bucket_traits_;
};
@@ -943,6 +959,32 @@ struct hashdata_internal
const split_traits &priv_split_traits() const
{ return *this; }
~hashdata_internal()
{ this->priv_clear_buckets(); }
void priv_clear_buckets()
{
this->internal.internal.internal.priv_clear_buckets
( this->internal.priv_get_cache()
, this->internal.internal.internal.priv_bucket_count()
- (this->internal.priv_get_cache()
- this->internal.internal.internal.priv_bucket_pointer()));
}
void priv_clear_buckets_and_cache()
{
this->priv_clear_buckets();
this->internal.priv_initialize_cache();
}
void priv_initialize_buckets_and_cache()
{
this->internal.internal.internal.priv_clear_buckets
( this->internal.internal.internal.priv_bucket_pointer()
, this->internal.internal.internal.priv_bucket_count());
this->internal.priv_initialize_cache();
}
internal_type internal; //2
};
@@ -1021,18 +1063,15 @@ template<class T, class ...Options>
template<class ValueTraits, class VoidOrKeyHash, class VoidOrKeyEqual, class SizeType, class BucketTraits, std::size_t BoolFlags>
#endif
class hashtable_impl
: private detail::clear_on_destructor_base
< hashtable_impl<ValueTraits, VoidOrKeyHash, VoidOrKeyEqual, SizeType, BucketTraits, BoolFlags>
, true //To always clear the bucket array
//is_safe_autounlink<detail::get_value_traits<ValueTraits>::type::link_mode>::value
>
: private hashtable_data_t
< SizeType
, BoolFlags & hashtable_data_bool_flags_mask
, VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTraits>
{
template<class C, bool> friend class detail::clear_on_destructor_base;
typedef hashtable_data_t
< SizeType
, BoolFlags & hashtable_data_bool_flags_mask
, VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTraits> data_type;
data_type data;
, VoidOrKeyHash, VoidOrKeyEqual, ValueTraits, BucketTraits> data_type;
public:
typedef ValueTraits value_traits;
@@ -1174,9 +1213,9 @@ class hashtable_impl
, const hasher & hash_func = hasher()
, const key_equal &equal_func = key_equal()
, const value_traits &v_traits = value_traits())
: data(b_traits, hash_func, equal_func, v_traits)
: data_type(b_traits, hash_func, equal_func, v_traits)
{
this->priv_initialize_buckets();
this->data_type::internal.priv_initialize_buckets_and_cache();
this->priv_size_traits().set_size(size_type(0));
size_type bucket_sz = this->priv_bucket_count();
BOOST_INTRUSIVE_INVARIANT_ASSERT(bucket_sz != 0);
@@ -1189,7 +1228,7 @@ class hashtable_impl
//! <b>Effects</b>: to-do
//!
hashtable_impl(BOOST_RV_REF(hashtable_impl) x)
: data( ::boost::move(x.priv_bucket_traits())
: data_type( ::boost::move(x.priv_bucket_traits())
, ::boost::move(x.priv_hasher())
, ::boost::move(x.priv_equal())
, ::boost::move(x.priv_value_traits())
@@ -1221,8 +1260,7 @@ class hashtable_impl
//! it's a safe-mode or auto-unlink value. Otherwise constant.
//!
//! <b>Throws</b>: Nothing.
~hashtable_impl()
{}
~hashtable_impl();
#endif
//! <b>Effects</b>: Returns an iterator pointing to the beginning of the unordered_set.
@@ -1863,7 +1901,7 @@ class hashtable_impl
//! to the erased elements. No destructors are called.
void clear()
{
this->priv_clear_buckets();
this->data_type::internal.priv_clear_buckets_and_cache();
this->priv_size_traits().set_size(size_type(0));
}
@@ -2606,102 +2644,101 @@ class hashtable_impl
bound -= (bound != primes);
return size_type(*bound);
}
/// @cond
private:
size_traits &priv_size_traits()
{ return this->data; }
{ return static_cast<size_traits&>(static_cast<data_type&>(*this)); }
const size_traits &priv_size_traits() const
{ return this->data; }
{ return static_cast<const size_traits&>(static_cast<const data_type&>(*this)); }
bucket_ptr priv_bucket_pointer() const
{ return this->data.internal.internal.internal.internal.priv_bucket_pointer(); }
{ return this->data_type::internal.internal.internal.internal.priv_bucket_pointer(); }
SizeType priv_bucket_count() const
{ return this->data.internal.internal.internal.internal.priv_bucket_count(); }
{ return this->data_type::internal.internal.internal.internal.priv_bucket_count(); }
const bucket_plus_vtraits<ValueTraits, BucketTraits> &get_bucket_value_traits() const
{ return this->data.internal.internal.internal.internal.get_bucket_value_traits(); }
{ return this->data_type::internal.internal.internal.internal.get_bucket_value_traits(); }
bucket_plus_vtraits<ValueTraits, BucketTraits> &get_bucket_value_traits()
{ return this->data.internal.internal.internal.internal.get_bucket_value_traits(); }
{ return this->data_type::internal.internal.internal.internal.get_bucket_value_traits(); }
bucket_traits &priv_bucket_traits()
{ return this->data.internal.internal.internal.internal.priv_bucket_traits(); }
{ return this->data_type::internal.internal.internal.internal.priv_bucket_traits(); }
const bucket_traits &priv_bucket_traits() const
{ return this->data.internal.internal.internal.internal.priv_bucket_traits(); }
{ return this->data_type::internal.internal.internal.internal.priv_bucket_traits(); }
value_traits &priv_value_traits()
{ return this->data.internal.internal.internal.internal.priv_value_traits(); }
{ return this->data_type::internal.internal.internal.internal.priv_value_traits(); }
const value_traits &priv_value_traits() const
{ return this->data.internal.internal.internal.internal.priv_value_traits(); }
{ return this->data_type::internal.internal.internal.internal.priv_value_traits(); }
const_value_traits_ptr value_traits_ptr() const
{ return this->data.internal.internal.internal.internal.value_traits_ptr(); }
{ return this->data_type::internal.internal.internal.internal.value_traits_ptr(); }
siterator priv_invalid_local_it() const
{ return this->data.internal.internal.internal.internal.priv_invalid_local_it(); }
{ return this->data_type::internal.internal.internal.internal.priv_invalid_local_it(); }
split_traits &priv_split_traits()
{ return this->data.internal.priv_split_traits(); }
{ return this->data_type::internal.priv_split_traits(); }
const split_traits &priv_split_traits() const
{ return this->data.internal.priv_split_traits(); }
{ return this->data_type::internal.priv_split_traits(); }
bucket_ptr priv_get_cache()
{ return this->data.internal.internal.priv_get_cache(); }
{ return this->data_type::internal.internal.priv_get_cache(); }
void priv_initialize_cache()
{ return this->data.internal.internal.priv_initialize_cache(); }
{ return this->data_type::internal.internal.priv_initialize_cache(); }
siterator priv_begin() const
{ return this->data.internal.internal.priv_begin(); }
{ return this->data_type::internal.internal.priv_begin(); }
const value_equal &priv_equal() const
{ return this->data.internal.internal.priv_equal(); }
{ return this->data_type::internal.internal.priv_equal(); }
value_equal &priv_equal()
{ return this->data.internal.internal.priv_equal(); }
{ return this->data_type::internal.internal.priv_equal(); }
const hasher &priv_hasher() const
{ return this->data.internal.internal.internal.priv_hasher(); }
{ return this->data_type::internal.internal.internal.priv_hasher(); }
hasher &priv_hasher()
{ return this->data.internal.internal.internal.priv_hasher(); }
{ return this->data_type::internal.internal.internal.priv_hasher(); }
void priv_swap_cache(hashtable_impl &h)
{ this->data.internal.internal.priv_swap_cache(h.data.internal.internal); }
{ this->data_type::internal.internal.priv_swap_cache(h.data_type::internal.internal); }
node &priv_value_to_node(value_type &v)
{ return this->data.internal.internal.internal.internal.priv_value_to_node(v); }
{ return this->data_type::internal.internal.internal.internal.priv_value_to_node(v); }
const node &priv_value_to_node(const value_type &v) const
{ return this->data.internal.internal.internal.internal.priv_value_to_node(v); }
{ return this->data_type::internal.internal.internal.internal.priv_value_to_node(v); }
SizeType priv_get_cache_bucket_num()
{ return this->data.internal.internal.priv_get_cache_bucket_num(); }
{ return this->data_type::internal.internal.priv_get_cache_bucket_num(); }
void priv_insertion_update_cache(SizeType n)
{ return this->data.internal.internal.priv_insertion_update_cache(n); }
{ return this->data_type::internal.internal.priv_insertion_update_cache(n); }
template<bool Boolean>
std::size_t priv_stored_or_compute_hash(const value_type &v, detail::bool_<Boolean> b) const
{ return this->data.internal.internal.internal.priv_stored_or_compute_hash(v, b); }
{ return this->data_type::internal.internal.internal.priv_stored_or_compute_hash(v, b); }
value_type &priv_value_from_slist_node(slist_node_ptr n)
{ return this->data.internal.internal.internal.internal.priv_value_from_slist_node(n); }
{ return this->data_type::internal.internal.internal.internal.priv_value_from_slist_node(n); }
const value_type &priv_value_from_slist_node(slist_node_ptr n) const
{ return this->data.internal.internal.internal.internal.priv_value_from_slist_node(n); }
{ return this->data_type::internal.internal.internal.internal.priv_value_from_slist_node(n); }
void priv_erasure_update_cache_range(SizeType first_bucket_num, SizeType last_bucket_num)
{ return this->data.internal.internal.priv_erasure_update_cache_range(first_bucket_num, last_bucket_num); }
{ return this->data_type::internal.internal.priv_erasure_update_cache_range(first_bucket_num, last_bucket_num); }
void priv_erasure_update_cache()
{ return this->data.internal.internal.priv_erasure_update_cache(); }
{ return this->data_type::internal.internal.priv_erasure_update_cache(); }
static std::size_t priv_stored_hash(slist_node_ptr n, detail::true_ true_value)
{ return bucket_plus_vtraits<ValueTraits, BucketTraits>::priv_stored_hash(n, true_value); }
@@ -2709,31 +2746,6 @@ class hashtable_impl
static std::size_t priv_stored_hash(slist_node_ptr n, detail::false_ false_value)
{ return bucket_plus_vtraits<ValueTraits, BucketTraits>::priv_stored_hash(n, false_value); }
void priv_clear_buckets(const bucket_ptr buckets_ptr, const size_type bucket_cnt)
{
bucket_ptr buckets_it = buckets_ptr;
for(size_type bucket_i = 0; bucket_i != bucket_cnt; ++buckets_it, ++bucket_i){
if(safemode_or_autounlink){
bucket_plus_vtraits_t::priv_clear_group_nodes(*buckets_it, optimize_multikey_t());
buckets_it->clear_and_dispose(detail::init_disposer<node_algorithms>());
}
else{
buckets_it->clear();
}
}
this->priv_initialize_cache();
}
void priv_initialize_buckets()
{ this->priv_clear_buckets(this->priv_bucket_pointer(), this->priv_bucket_count()); }
void priv_clear_buckets()
{
this->priv_clear_buckets
( this->priv_get_cache()
, this->priv_bucket_count() - (this->priv_get_cache() - this->priv_bucket_pointer()));
}
std::size_t priv_hash_to_bucket(std::size_t hash_value) const
{
return detail::hash_to_bucket_split<power_2_buckets, incremental>
@@ -2815,7 +2827,7 @@ class hashtable_impl
}
std::size_t priv_get_bucket_num_hash_dispatch(siterator it, detail::false_) //NO store_hash
{ return this->data.internal.internal.internal.internal.priv_get_bucket_num_no_hash_store(it, optimize_multikey_t()); }
{ return this->data_type::internal.internal.internal.internal.priv_get_bucket_num_no_hash_store(it, optimize_multikey_t()); }
static siterator priv_get_previous(bucket_type &b, siterator i)
{ return bucket_plus_vtraits_t::priv_get_previous(b, i, optimize_multikey_t()); }

View File

@@ -20,7 +20,6 @@
#include <boost/intrusive/list_hook.hpp>
#include <boost/intrusive/circular_list_algorithms.hpp>
#include <boost/intrusive/pointer_traits.hpp>
#include <boost/intrusive/detail/clear_on_destructor_base.hpp>
#include <boost/intrusive/detail/mpl.hpp>
#include <boost/intrusive/link_mode.hpp>
#include <boost/static_assert.hpp>
@@ -63,12 +62,7 @@ template<class T, class ...Options>
template <class ValueTraits, class SizeType, bool ConstantTimeSize>
#endif
class list_impl
: private detail::clear_on_destructor_base
< list_impl<ValueTraits, SizeType, ConstantTimeSize>
, is_safe_autounlink<ValueTraits::link_mode>::value
>
{
template<class C, bool> friend class detail::clear_on_destructor_base;
//Public typedefs
public:
typedef ValueTraits value_traits;
@@ -175,8 +169,10 @@ class list_impl
list_impl(Iterator b, Iterator e, const value_traits &v_traits = value_traits())
: data_(v_traits)
{
//nothrow, no need to rollback to release elements on exception
this->priv_size_traits().set_size(size_type(0));
node_algorithms::init_header(this->get_root_node());
//nothrow, no need to rollback to release elements on exception
this->insert(this->cend(), b, e);
}
@@ -187,6 +183,7 @@ class list_impl
{
this->priv_size_traits().set_size(size_type(0));
node_algorithms::init_header(this->get_root_node());
//nothrow, no need to rollback to release elements on exception
this->swap(x);
}
@@ -195,7 +192,6 @@ class list_impl
list_impl& operator=(BOOST_RV_REF(list_impl) x)
{ this->swap(x); return *this; }
#ifdef BOOST_INTRUSIVE_DOXYGEN_INVOKED
//! <b>Effects</b>: If it's not a safe-mode or an auto-unlink value_type
//! the destructor does nothing
//! (ie. no code is generated). Otherwise it detaches all elements from this.
@@ -206,8 +202,11 @@ class list_impl
//! <b>Complexity</b>: Linear to the number of elements in the list, if
//! it's a safe-mode or auto-unlink value . Otherwise constant.
~list_impl()
{}
#endif
{
if(is_safe_autounlink<ValueTraits::link_mode>::value){
this->clear();
}
}
//! <b>Requires</b>: value must be an lvalue.
//!

View File

@@ -22,7 +22,6 @@
#include <boost/intrusive/circular_slist_algorithms.hpp>
#include <boost/intrusive/linear_slist_algorithms.hpp>
#include <boost/intrusive/pointer_traits.hpp>
#include <boost/intrusive/detail/clear_on_destructor_base.hpp>
#include <boost/intrusive/link_mode.hpp>
#include <boost/intrusive/options.hpp>
#include <boost/intrusive/detail/utilities.hpp>
@@ -99,12 +98,7 @@ template<class T, class ...Options>
template<class ValueTraits, class SizeType, std::size_t BoolFlags>
#endif
class slist_impl
: private detail::clear_on_destructor_base
< slist_impl<ValueTraits, SizeType, BoolFlags>
, is_safe_autounlink<ValueTraits::link_mode>::value
>
{
template<class C, bool> friend class detail::clear_on_destructor_base;
//Public typedefs
public:
typedef ValueTraits value_traits;
@@ -312,6 +306,7 @@ class slist_impl
: data_(v_traits)
{
this->set_default_constructed_state();
//nothrow, no need to rollback to release elements on exception
this->insert_after(this->cbefore_begin(), b, e);
}
@@ -322,6 +317,7 @@ class slist_impl
{
this->priv_size_traits().set_size(size_type(0));
node_algorithms::init_header(this->get_root_node());
//nothrow, no need to rollback to release elements on exception
this->swap(x);
}
@@ -330,7 +326,6 @@ class slist_impl
slist_impl& operator=(BOOST_RV_REF(slist_impl) x)
{ this->swap(x); return *this; }
#ifdef BOOST_INTRUSIVE_DOXYGEN_INVOKED
//! <b>Effects</b>: If it's a safe-mode
//! or auto-unlink value, the destructor does nothing
//! (ie. no code is generated). Otherwise it detaches all elements from this.
@@ -341,8 +336,11 @@ class slist_impl
//! <b>Complexity</b>: Linear to the number of elements in the list, if
//! it's a safe-mode or auto-unlink value. Otherwise constant.
~slist_impl()
{}
#endif
{
if(is_safe_autounlink<ValueTraits::link_mode>::value){
this->clear();
}
}
//! <b>Effects</b>: Erases all the elements of the container.
//!