Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r81340 - in sandbox-branches/geometry/index_dev: boost/geometry/extensions/index/rtree boost/geometry/extensions/index/rtree/node boost/geometry/extensions/index/rtree/visitors test/rtree
From: adam.wulkiewicz_at_[hidden]
Date: 2012-11-14 09:37:10


Author: awulkiew
Date: 2012-11-14 09:37:09 EST (Wed, 14 Nov 2012)
New Revision: 81340
URL: http://svn.boost.org/trac/boost/changeset/81340

Log:
mem leaks related exceptions in rtree copying fixed

Text files modified:
   sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/node/node.hpp | 38 ++++++
   sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/rtree.hpp | 231 ++++++++++++++++++++++++---------------
   sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/visitors/copy.hpp | 20 ++-
   sandbox-branches/geometry/index_dev/test/rtree/rtree_exceptions.cpp | 31 +++++
   4 files changed, 225 insertions(+), 95 deletions(-)

Modified: sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/node/node.hpp
==============================================================================
--- sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/node/node.hpp (original)
+++ sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/node/node.hpp 2012-11-14 09:37:09 EST (Wed, 14 Nov 2012)
@@ -31,6 +31,8 @@
 
 #include <boost/geometry/algorithms/expand.hpp>
 
+#include <boost/geometry/extensions/index/rtree/visitors/is_leaf.hpp>
+
 namespace boost { namespace geometry { namespace index {
 
 namespace detail { namespace rtree {
@@ -113,6 +115,42 @@
     {}
 };
 
+// clears node, deletes all subtrees stored in node
+template <typename Value, typename Options, typename Translator, typename Box, typename Allocators>
+struct clear_node
+{
+ typedef typename Options::parameters_type parameters_type;
+
+ typedef typename rtree::node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type node;
+ typedef typename rtree::internal_node<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type internal_node;
+ typedef typename rtree::leaf<Value, parameters_type, Box, Allocators, typename Options::node_tag>::type leaf;
+
+ inline static void apply(node & node, Allocators & allocators)
+ {
+ rtree::visitors::is_leaf<Value, Options, Box, Allocators> ilv;
+ rtree::apply_visitor(ilv, node);
+ if ( ilv.result )
+ {
+ apply(rtree::get<leaf>(node), allocators);
+ }
+ else
+ {
+ apply(rtree::get<internal_node>(node), allocators);
+ }
+ }
+
+ inline static void apply(internal_node & internal_node, Allocators & allocators)
+ {
+ destroy_elements<Value, Options, Translator, Box, Allocators>::apply(rtree::elements(internal_node), allocators);
+ rtree::elements(internal_node).clear();
+ }
+
+ inline static void apply(leaf & leaf, Allocators &)
+ {
+ rtree::elements(leaf).clear();
+ }
+};
+
 }} // namespace detail::rtree
 
 }}} // namespace boost::geometry::index

Modified: sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/rtree.hpp
==============================================================================
--- sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/rtree.hpp (original)
+++ sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/rtree.hpp 2012-11-14 09:37:09 EST (Wed, 14 Nov 2012)
@@ -96,6 +96,8 @@
     /*!
     The constructor.
 
+ \note Exception-safety: strong
+
     \param parameters The parameters object.
     \param translator The translator object.
     \param allocator The allocator object.
@@ -114,6 +116,8 @@
     /*!
     The constructor.
 
+ \note Exception-safety: strong
+
     \param first The beginning of the range of Values.
     \param last The end of the range of Values.
     \param parameters The parameters object.
@@ -130,60 +134,64 @@
         , m_allocators(allocator)
     {
         this->create();
- this->insert(first, last);
+
+ try
+ {
+ this->insert(first, last);
+ }
+ catch(...)
+ {
+ this->destroy(*this);
+ throw;
+ }
     }
 
     /*!
     The destructor.
+
+ \note Exception-safety: strong
     */
     inline ~rtree()
     {
+// TODO: after moving assignment, rvalue will have root == 0
         if ( m_root )
             this->destroy(*this);
     }
 
     /*!
     The copy constructor.
+
+ \note Exception-safety: strong
     */
     inline rtree(rtree const& src)
- : m_parameters(src.m_parameters)
+ : m_root(0)
+ , m_parameters(src.m_parameters)
         , m_translator(src.m_translator)
         , m_allocators(src.m_allocators)
     {
         //TODO use Boost.Container allocator_traits_type::select_on_container_copy_construction()
 
- try
- {
- this->copy(src, *this);
- }
- catch(...)
- {
- this->destroy(*this);
- throw;
- }
+ this->copy(src, *this, m_allocators);
     }
 
     /*!
     The copy constructor.
+
+ \note Exception-safety: strong
     */
     inline rtree(rtree const& src, Allocator const& allocator)
- : m_parameters(src.m_parameters)
+ : m_root(0)
+ , m_parameters(src.m_parameters)
         , m_translator(src.m_translator)
         , m_allocators(allocator)
     {
- try
- {
- this->copy(src, *this);
- }
- catch(...)
- {
- this->destroy(*this);
- throw;
- }
+ this->copy(src, *this, m_allocators);
     }
 
     /*!
     The moving constructor.
+
+ \note Exception-safety: strong
     */
     inline rtree(BOOST_RV_REF(rtree) src)
         : m_values_count(src.m_values_count)
@@ -196,73 +204,60 @@
         src.m_values_count = 0;
         src.m_root = 0;
         src.m_leafs_level = 0;
+// TODO: after moving, rvalue will have root == 0
+// This would leave the tree unusable
+// Write the test
     }
 
     /*!
     The assignment operator.
+
+ \note Exception-safety: strong
     */
     inline rtree & operator=(BOOST_COPY_ASSIGN_REF(rtree) src)
     {
         if ( this == &src )
             return *this;
 
- if ( !this->empty() )
- this->destroy(*this);
-
- m_parameters = src.m_parameters;
- m_translator = src.m_translator;
         //TODO use Boost.Container allocator_traits_type::propagate_on_container_move_assignment
- //m_allocators = src.m_allocators;
 
- try
- {
- this->copy(src, *this);
- }
- catch(...)
- {
- this->destroy(*this);
- throw;
- }
+ this->copy(src, *this, m_allocators);
 
         return *this;
     }
 
     /*!
     The moving assignment.
+
+ \note Exception-safety: strong
     */
     inline rtree & operator=(BOOST_RV_REF(rtree) src)
     {
         if ( this == &src )
             return *this;
 
- if ( !this->empty() )
- this->destroy(*this);
-
- m_parameters = src.m_parameters;
- m_translator = src.m_translator;
         //TODO use Boost.Container allocator_traits_type::propagate_on_container_move_assignment
- //m_allocators = src.m_allocators;
 
         if ( m_allocators.allocator == src.m_allocators.allocator )
         {
+ m_parameters = src.m_parameters;
+ m_translator = src.m_translator;
+ //m_allocators = src.m_allocators;
+
             m_values_count = src.m_values_count;
             src.m_values_count = 0;
             m_root = src.m_root;
             src.m_root = 0;
             m_leafs_level = src.m_leafs_level;
             src.m_leafs_level = 0;
+
+// TODO: after moving, rvalue will have root == 0
+// This would leave the tree unusable
+// Write the test
         }
         else
         {
- try
- {
- this->copy(src, *this);
- }
- catch(...)
- {
- this->destroy(*this);
- throw;
- }
+ this->copy(src, *this, m_allocators);
         }
 
         return *this;
@@ -271,6 +266,8 @@
     /*!
     Insert a value to the index.
 
+ \note Exception-safety: basic
+
     \param value The value which will be stored in the container.
     */
     inline void insert(value_type const& value)
@@ -279,23 +276,26 @@
 
         detail::rtree::visitors::insert<
             value_type,
- value_type,
- options_type,
- translator_type,
- box_type,
- allocators_type,
+ value_type, options_type, translator_type, box_type, allocators_type,
             typename options_type::insert_tag
> insert_v(m_root, m_leafs_level, value, m_parameters, m_translator, m_allocators);
 
         detail::rtree::apply_visitor(insert_v, *m_root);
 
- // If exception is thrown, m_values_count is invalid
+// TODO
+// Think about this: If exception is thrown, may the root be removed?
+// Or it is just cleared?
+
+// TODO
+// If exception is thrown, m_values_count may be invalid
         ++m_values_count;
     }
 
     /*!
     Insert a range of values to the index.
 
+ \note Exception-safety: basic
+
     \param first The beginning of the range of values.
     \param last The end of the range of values.
     */
@@ -309,6 +309,8 @@
     /*!
     Remove the value from the container.
 
+ \note Exception-safety: basic
+
     \param value The value which will be removed from the container.
     */
     inline void remove(value_type const& value)
@@ -317,30 +319,26 @@
 
         BOOST_GEOMETRY_INDEX_ASSERT(0 < m_values_count, "can't remove, there are no elements in the rtree");
 
- try
- {
- detail::rtree::visitors::remove<
- value_type,
- options_type,
- translator_type,
- box_type,
- allocators_type
- > remove_v(m_root, m_leafs_level, value, m_parameters, m_translator, m_allocators);
-
- detail::rtree::apply_visitor(remove_v, *m_root);
-
- --m_values_count;
- }
- catch(...)
- {
- this->destroy(*this);
- throw;
- }
+ detail::rtree::visitors::remove<
+ value_type, options_type, translator_type, box_type, allocators_type
+ > remove_v(m_root, m_leafs_level, value, m_parameters, m_translator, m_allocators);
+
+ detail::rtree::apply_visitor(remove_v, *m_root);
+
+// TODO
+// Think about this: If exception is thrown, may the root be removed?
+// Or it is just cleared?
+
+// TODO
+// If exception is thrown, m_values_count may be invalid
+ --m_values_count;
     }
 
     /*!
     Remove the range of values from the container.
 
+ \note Exception-safety: basic
+
     \param first The beginning of the range of values.
     \param last The end of the range of values.
     */
@@ -354,6 +352,8 @@
     /*!
     Find values meeting spatial predicates, e.g. intersecting some box.
 
+ \note Exception-safety: strong
+
     \param pred The spatial predicates. May be a Geometry (in this case default
                     predicate - intersects is used) or generated by bgi::covered_by(geometry),
                     bgi::disjoint(geometry), bgi::intersects(geometry), bgi::overlaps(geometry),
@@ -381,6 +381,8 @@
     /*!
     Find one value meeting distances predicates, e.g. nearest to some point.
 
+ \note Exception-safety: strong
+
     \param dpred The distances predicates. May be a Point. This is default case where Value which
                     nearest point is closest to Point is returned. May be a PointRelation which define
                     how distance to Value is calculated. This may be generated by bgi::to_nearest(Point),
@@ -405,6 +407,8 @@
     Find one value meeting distances predicates and spatial predicates,
     e.g. nearest to some point and intersecting some box.
 
+ \note Exception-safety: strong
+
     \param dpred The distances predicates. May be a Point. This is default case where Value which
                     nearest point is closest to Point is returned. May be a PointRelation which define
                     how distance to Value is calculated. This may be generated by bgi::to_nearest(Point),
@@ -434,6 +438,8 @@
     /*!
     Find k values meeting distances predicates, e.g. k nearest values to some point.
 
+ \note Exception-safety: strong
+
     \param dpred The distances predicates. May be a Point. This is default case where Value which
                     nearest point is closest to Point is returned. May be a PointRelation which define
                     how distance to Value is calculated. This may be generated by bgi::to_nearest(Point),
@@ -458,6 +464,8 @@
     Find k values meeting distances predicates and spatial predicates,
     e.g. k nearest values to some point and intersecting some box.
 
+ \note Exception-safety: strong
+
     \param dpred The distances predicates. May be a Point. This is default case where Value which
                     nearest point is closest to Point is returned. May be a PointRelation which define
                     how distance to Value is calculated. This may be generated by bgi::to_nearest(Point),
@@ -488,6 +496,8 @@
     /*!
     Returns the number of stored values.
 
+ \note Exception-safety: nothrow
+
     \return The number of stored values.
     */
     inline size_type size() const
@@ -498,6 +508,8 @@
     /*!
     Query if the container is empty.
 
+ \note Exception-safety: nothrow
+
     \return true if the container is empty.
     */
     inline bool empty() const
@@ -507,17 +519,20 @@
 
     /*!
     Removes all values stored in the container.
+
+ \note Exception-safety: nothrow.
     */
     inline void clear()
     {
- this->destroy(*this);
- this->create();
+ this->destroy(*this, false);
     }
 
     /*!
     Returns the box containing all values stored in the container.
     If the container is empty the result of geometry::assign_inverse() is returned.
 
+ \note Exception-safety: nothrow.
+
     \return The box containing all values stored in the container or an invalid box if
                 there are no values in the container.
     */
@@ -541,6 +556,8 @@
     /*!
     Returns allocator used by the rtree.
 
+ \note Exception-safety: nothrow if allocator copy can't throw.
+
     \return The allocator.
     */
     allocator_type get_allocator() const
@@ -553,6 +570,8 @@
     This function is not a part of the 'official' interface. However it makes
     possible to e.g. draw the tree structure.
 
+ \note Exception-safety: the same as visitor.
+
     \param visitor The visitor object.
     */
     template <typename Visitor>
@@ -565,6 +584,8 @@
     Returns the translator object.
     This function is not a part of the 'official' interface.
 
+ \note Exception-safety: nothrow.
+
     \return The translator object.
     */
     inline translator_type const& translator() const
@@ -574,7 +595,9 @@
 
     /*!
     Returns the number of stored objects. Same as size()
- This function is not a part of the 'official' interface.
+ This function is not a part of the 'official' interface.
+
+ \note Exception-safety: nothrow.
 
     \return The number of stored objects.
     */
@@ -585,7 +608,9 @@
 
     /*!
     Returns the depth of the R-tree.
- This function is not a part of the 'official' interface.
+ This function is not a part of the 'official' interface.
+
+ \note Exception-safety: nothrow.
 
     \return The depth of the R-tree.
     */
@@ -597,12 +622,14 @@
 private:
     /*!
     Create an empty R-tree i.e. new empty root node and clear other attributes.
+
+ \note Exception-safety: strong.
     */
     inline void create()
     {
- assert(0 == m_root);
+ BOOST_GEOMETRY_INDEX_ASSERT(0 == m_root, "the tree is already created");
 
- m_root = detail::rtree::create_node<allocators_type, leaf>::apply(m_allocators);
+ m_root = detail::rtree::create_node<allocators_type, leaf>::apply(m_allocators); // MAY THROW (N: alloc)
         m_values_count = 0;
         m_leafs_level = 0;
     }
@@ -610,12 +637,23 @@
     /*!
     Destroy the R-tree i.e. all nodes and clear attributes.
 
+ \note Exception-safety: nothrow.
+
     \param t The container which is going to be destroyed.
     */
- inline void destroy(rtree & t)
+ inline void destroy(rtree & t, bool destroy_root = true)
     {
- detail::rtree::visitors::destroy<value_type, options_type, translator_type, box_type, allocators_type> del_v(t.m_root, t.m_allocators);
- detail::rtree::apply_visitor(del_v, *t.m_root);
+ BOOST_GEOMETRY_INDEX_ASSERT(t.m_root, "can't destroy empty tree");
+
+ if ( destroy_root )
+ {
+ detail::rtree::visitors::destroy<value_type, options_type, translator_type, box_type, allocators_type> del_v(t.m_root, t.m_allocators);
+ detail::rtree::apply_visitor(del_v, *t.m_root);
+ }
+ else
+ {
+ detail::rtree::clear_node<value_type, options_type, translator_type, box_type, allocators_type>::apply(*t.m_root, t.m_allocators);
+ }
 
         t.m_root = 0;
         t.m_values_count = 0;
@@ -625,13 +663,26 @@
     /*!
     Copy the R-tree i.e. whole nodes structure, values and other attributes.
 
+ \note Exception-safety: strong.
+
     \param src The source R-tree.
     \param dst The destination R-tree.
     */
- inline void copy(rtree const& src, rtree & dst) const
+ inline void copy(rtree const& src, rtree & dst, allocators_type & allocators) const
     {
- detail::rtree::visitors::copy<value_type, options_type, translator_type, box_type, allocators_type> copy_v(dst.m_allocators);
- detail::rtree::apply_visitor(copy_v, *src.m_root);
+ detail::rtree::visitors::copy<value_type, options_type, translator_type, box_type, allocators_type> copy_v(allocators);
+ detail::rtree::apply_visitor(copy_v, *src.m_root); // MAY THROW (V: alloc, copy, E: alloc, N: alloc)
+
+ if ( dst.m_root )
+ {
+ detail::rtree::visitors::destroy<value_type, options_type, translator_type, box_type, allocators_type> del_v(dst.m_root, dst.m_allocators);
+ detail::rtree::apply_visitor(del_v, *dst.m_root);
+ dst.m_root = 0;
+ }
+
+ dst.m_parameters = src.m_parameters;
+ dst.m_translator = src.m_translator;
+ dst.m_allocators = allocators;
 
         dst.m_root = copy_v.result;
         dst.m_values_count = src.m_values_count;
@@ -640,6 +691,8 @@
 
     /*!
     Find one value meeting distances and spatial predicates.
+
+ \note Exception-safety: strong.
     */
     template <typename DistancesPredicates, typename Predicates>
     inline size_type nearest_one(DistancesPredicates const& dpred, Predicates const& pred, value_type & v) const
@@ -673,6 +726,8 @@
 
     /*!
     Find k values meeting distances and spatial predicates.
+
+ \note Exception-safety: strong.
     */
     template <typename DistancesPredicates, typename Predicates, typename OutIter>
     inline size_type nearest_k(DistancesPredicates const& dpred, size_t k, Predicates const& pred, OutIter out_it) const

Modified: sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/visitors/copy.hpp
==============================================================================
--- sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/visitors/copy.hpp (original)
+++ sandbox-branches/geometry/index_dev/boost/geometry/extensions/index/rtree/visitors/copy.hpp 2012-11-14 09:37:09 EST (Wed, 14 Nov 2012)
@@ -27,6 +27,8 @@
     typedef typename rtree::internal_node<Value, typename Options::parameters_type, Box, Allocators, typename Options::node_tag>::type internal_node;
     typedef typename rtree::leaf<Value, typename Options::parameters_type, Box, Allocators, typename Options::node_tag>::type leaf;
 
+ typedef rtree::node_auto_ptr<Value, Options, Translator, Box, Allocators> node_auto_ptr;
+
     explicit inline copy(Allocators & allocators)
         : result(0)
         , m_allocators(allocators)
@@ -34,7 +36,8 @@
 
     inline void operator()(internal_node & n)
     {
- node * new_node = rtree::create_node<Allocators, internal_node>::apply(m_allocators);
+ node * raw_new_node = rtree::create_node<Allocators, internal_node>::apply(m_allocators); // MAY THROW (N: alloc)
+ node_auto_ptr new_node(raw_new_node, m_allocators);
 
         typedef typename rtree::elements_type<internal_node>::type elements_type;
         elements_type & elements = rtree::elements(n);
@@ -46,16 +49,18 @@
         {
             rtree::apply_visitor(*this, *it->second);
 
- elements_dst.push_back( std::make_pair(it->first, result) );
+ elements_dst.push_back( std::make_pair(it->first, result) ); // MAY THROW (E: alloc)
         }
 
- result = new_node;
+ result = new_node.get();
+ new_node.release();
     }
 
     inline void operator()(leaf & l)
     {
- node * new_node = rtree::create_node<Allocators, leaf>::apply(m_allocators);
-
+ node * raw_new_node = rtree::create_node<Allocators, leaf>::apply(m_allocators); // MAY THROW (N: alloc)
+ node_auto_ptr new_node(raw_new_node, m_allocators);
+
         typedef typename rtree::elements_type<leaf>::type elements_type;
         elements_type & elements = rtree::elements(l);
 
@@ -64,10 +69,11 @@
         for (typename elements_type::iterator it = elements.begin();
             it != elements.end(); ++it)
         {
- elements_dst.push_back(*it);
+ elements_dst.push_back(*it); // MAY THROW (V: alloc, copy)
         }
 
- result = new_node;
+ result = new_node.get();
+ new_node.release();
     }
 
     node * result;

Modified: sandbox-branches/geometry/index_dev/test/rtree/rtree_exceptions.cpp
==============================================================================
--- sandbox-branches/geometry/index_dev/test/rtree/rtree_exceptions.cpp (original)
+++ sandbox-branches/geometry/index_dev/test/rtree/rtree_exceptions.cpp 2012-11-14 09:37:09 EST (Wed, 14 Nov 2012)
@@ -54,6 +54,37 @@
 
         BOOST_CHECK_THROW( tree.remove(input.begin(), input.end()), throwing_value_copy_exception );
     }
+
+ for ( size_t i = 0 ; i < 20 ; i += 2 )
+ {
+ throwing_value::reset_calls_counter();
+ throwing_value::set_max_calls(10000);
+
+ Tree tree(parameters);
+
+ tree.insert(input.begin(), input.end());
+
+ throwing_value::reset_calls_counter();
+ throwing_value::set_max_calls(i);
+
+ BOOST_CHECK_THROW( Tree tree2(tree), throwing_value_copy_exception );
+ }
+
+ for ( size_t i = 0 ; i < 20 ; i += 2 )
+ {
+ throwing_value::reset_calls_counter();
+ throwing_value::set_max_calls(10000);
+
+ Tree tree(parameters);
+ Tree tree2(parameters);
+
+ tree.insert(input.begin(), input.end());
+
+ throwing_value::reset_calls_counter();
+ throwing_value::set_max_calls(i);
+
+ BOOST_CHECK_THROW(tree2 = tree, throwing_value_copy_exception );
+ }
 }
 
 int test_main(int, char* [])


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk