diff --git a/include/boost/detail/shared_count.hpp b/include/boost/detail/shared_count.hpp index a7b6aaa..cb9e713 100644 --- a/include/boost/detail/shared_count.hpp +++ b/include/boost/detail/shared_count.hpp @@ -355,6 +355,9 @@ public: ~shared_count() // nothrow { if(pi_ != 0) pi_->release(); +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + id_ = 0; +#endif } shared_count(shared_count const & r): pi_(r.pi_) // nothrow @@ -455,6 +458,9 @@ public: ~weak_count() // nothrow { if(pi_ != 0) pi_->weak_release(); +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + id_ = 0; +#endif } weak_count & operator= (shared_count const & r) // nothrow diff --git a/src/sp_collector.cpp b/src/sp_collector.cpp new file mode 100644 index 0000000..d56bf1a --- /dev/null +++ b/src/sp_collector.cpp @@ -0,0 +1,267 @@ +// +// sp_collector.cpp +// +// Copyright (c) 2002, 2003 Peter Dimov +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// + +#if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) + +#include +#include +#include +#include +#include +#include +#include + +typedef std::map< void const *, std::pair > map_type; + +static map_type & get_map() +{ + static map_type m; + return m; +} + +typedef boost::detail::lightweight_mutex mutex_type; + +static mutex_type & get_mutex() +{ + static mutex_type m; + return m; +} + +static void * init_mutex_before_main = &get_mutex(); + +namespace +{ + class X; + + struct count_layout + { + boost::detail::sp_counted_base * pi; + int id; + }; + + struct shared_ptr_layout + { + X * px; + count_layout pn; + }; +} + +// assume 4 byte alignment for pointers when scanning +size_t const pointer_align = 4; + +typedef std::map map2_type; + +static void scan_and_count(void const * area, size_t size, map_type const & m, map2_type & m2) +{ + unsigned char const * p = static_cast(area); + + for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p += pointer_align, n += pointer_align) + { + shared_ptr_layout const * q = reinterpret_cast(p); + + if(q->pn.id == boost::detail::shared_count_id && q->pn.pi != 0 && m.count(q->pn.pi) != 0) + { + ++m2[q->pn.pi]; + } + } +} + +typedef std::deque open_type; + +static void scan_and_mark(void const * area, size_t size, map2_type & m2, open_type & open) +{ + unsigned char const * p = static_cast(area); + + for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p += pointer_align, n += pointer_align) + { + shared_ptr_layout const * q = reinterpret_cast(p); + + if(q->pn.id == boost::detail::shared_count_id && q->pn.pi != 0 && m2.count(q->pn.pi) != 0) + { + open.push_back(q->pn.pi); + m2.erase(q->pn.pi); + } + } +} + +static void find_unreachable_objects_impl(map_type const & m, map2_type & m2) +{ + // scan objects for shared_ptr members, compute internal counts + + { + std::cout << "... " << m.size() << " objects in m.\n"; + + for(map_type::const_iterator i = m.begin(); i != m.end(); ++i) + { + boost::detail::sp_counted_base const * p = static_cast(i->first); + + BOOST_ASSERT(p->use_count() != 0); // there should be no inactive counts in the map + + scan_and_count(i->second.first, i->second.second, m, m2); + } + + std::cout << "... " << m2.size() << " objects in m2.\n"; + } + + // mark reachable objects + + { + open_type open; + + for(map2_type::iterator i = m2.begin(); i != m2.end(); ++i) + { + boost::detail::sp_counted_base const * p = static_cast(i->first); + if(p->use_count() != i->second) open.push_back(p); + } + + std::cout << "... " << m2.size() << " objects in open.\n"; + + for(open_type::iterator j = open.begin(); j != open.end(); ++j) + { + m2.erase(*j); + } + + while(!open.empty()) + { + void const * p = open.front(); + open.pop_front(); + + map_type::const_iterator i = m.find(p); + BOOST_ASSERT(i != m.end()); + + scan_and_mark(i->second.first, i->second.second, m2, open); + } + } + + // m2 now contains the unreachable objects +} + +std::size_t find_unreachable_objects(bool report) +{ + map2_type m2; + +#ifdef BOOST_HAS_THREADS + + // This will work without the #ifdef, but some compilers warn + // that lock is not referenced + + mutex_type::scoped_lock lock(get_mutex()); + +#endif + + map_type const & m = get_map(); + + find_unreachable_objects_impl(m, m2); + + if(report) + { + for(map2_type::iterator j = m2.begin(); j != m2.end(); ++j) + { + map_type::const_iterator i = m.find(j->first); + BOOST_ASSERT(i != m.end()); + std::cout << "Unreachable object at " << i->second.first << ", " << i->second.second << " bytes long.\n"; + } + } + + return m2.size(); +} + +typedef std::deque< boost::shared_ptr > free_list_type; + +static void scan_and_free(void * area, size_t size, map2_type const & m2, free_list_type & free) +{ + unsigned char * p = static_cast(area); + + for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p += pointer_align, n += pointer_align) + { + shared_ptr_layout * q = reinterpret_cast(p); + + if(q->pn.id == boost::detail::shared_count_id && q->pn.pi != 0 && m2.count(q->pn.pi) != 0 && q->px != 0) + { + boost::shared_ptr * ppx = reinterpret_cast< boost::shared_ptr * >(p); + free.push_back(*ppx); + ppx->reset(); + } + } +} + +void free_unreachable_objects() +{ + map2_type m2; + +#ifdef BOOST_HAS_THREADS + + mutex_type::scoped_lock lock(get_mutex()); + +#endif + + map_type const & m = get_map(); + + find_unreachable_objects_impl(m, m2); + + free_list_type free; + + for(map2_type::iterator j = m2.begin(); j != m2.end(); ++j) + { + map_type::const_iterator i = m.find(j->first); + BOOST_ASSERT(i != m.end()); + scan_and_free(i->second.first, i->second.second, m2, free); + } + + std::cout << "... about to free " << free.size() << " objects.\n"; +} + +// debug hooks + +namespace boost +{ + +void sp_scalar_constructor_hook(void *) +{ +} + +void sp_scalar_constructor_hook(void * px, std::size_t size, void * pn) +{ +#ifdef BOOST_HAS_THREADS + + mutex_type::scoped_lock lock(get_mutex()); + +#endif + + get_map()[pn] = std::make_pair(px, size); +} + +void sp_scalar_destructor_hook(void *) +{ +} + +void sp_scalar_destructor_hook(void *, std::size_t, void * pn) +{ +#ifdef BOOST_HAS_THREADS + + mutex_type::scoped_lock lock(get_mutex()); + +#endif + + get_map().erase(pn); +} + +void sp_array_constructor_hook(void *) +{ +} + +void sp_array_destructor_hook(void *) +{ +} + +} // namespace boost + +#endif // defined(BOOST_ENABLE_SP_DEBUG_HOOKS) diff --git a/src/sp_debug_hooks.cpp b/src/sp_debug_hooks.cpp index 0f7e365..6ad1edc 100644 --- a/src/sp_debug_hooks.cpp +++ b/src/sp_debug_hooks.cpp @@ -8,19 +8,12 @@ // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // -// http://www.boost.org/libs/smart_ptr/debug_hooks.html -// #if defined(BOOST_ENABLE_SP_DEBUG_HOOKS) #include -#include -#include #include #include -#include -#include -#include int const m = 2; // m * sizeof(int) must be aligned appropriately @@ -112,192 +105,6 @@ void * operator new[](size_t n, nothrow_t const &) throw() #endif -// cycle detection - -typedef std::map< void const *, std::pair > map_type; - -static map_type & get_map() -{ - static map_type m; - return m; -} - -typedef boost::detail::lightweight_mutex mutex_type; - -static mutex_type & get_mutex() -{ - static mutex_type m; - return m; -} - -static void * init_mutex_before_main = &get_mutex(); - -namespace -{ - class X; - - struct count_layout - { - boost::detail::sp_counted_base * pi; - int id; - }; - - struct shared_ptr_layout - { - X * px; - count_layout pn; - }; -} - -// assume 4 byte alignment for pointers when scanning -size_t const pointer_align = 4; - -typedef std::map map2_type; - -static void scan_and_count(void const * area, size_t size, map_type const & m, map2_type & m2) -{ - unsigned char const * p = static_cast(area); - - for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p += pointer_align, n += pointer_align) - { - shared_ptr_layout const * q = reinterpret_cast(p); - - if(q->pn.id == boost::detail::shared_count_id && q->pn.pi != 0 && m.count(q->pn.pi) != 0) - { - ++m2[q->pn.pi]; - } - } -} - -typedef std::deque open_type; - -static void scan_and_mark(void const * area, size_t size, map2_type & m2, open_type & open) -{ - unsigned char const * p = static_cast(area); - - for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p += pointer_align, n += pointer_align) - { - shared_ptr_layout const * q = reinterpret_cast(p); - - if(q->pn.id == boost::detail::shared_count_id && q->pn.pi != 0 && m2.count(q->pn.pi) != 0) - { - open.push_back(q->pn.pi); - m2.erase(q->pn.pi); - } - } -} - -static void find_unreachable_objects(map_type const & m, map2_type & m2) -{ - // scan objects for shared_ptr members, compute internal counts - - { - for(map_type::const_iterator i = m.begin(); i != m.end(); ++i) - { - boost::detail::sp_counted_base const * p = static_cast(i->first); - - BOOST_ASSERT(p->use_count() != 0); // there should be no inactive counts in the map - - scan_and_count(i->second.first, i->second.second, m, m2); - } - } - - // mark reachable objects - - { - open_type open; - - for(map2_type::iterator i = m2.begin(); i != m2.end(); ++i) - { - boost::detail::sp_counted_base const * p = static_cast(i->first); - if(p->use_count() != i->second) open.push_back(p); - } - - for(open_type::iterator j = open.begin(); j != open.end(); ++j) - { - m2.erase(*j); - } - - while(!open.empty()) - { - void const * p = open.front(); - open.pop_front(); - - map_type::const_iterator i = m.find(p); - BOOST_ASSERT(i != m.end()); - - scan_and_mark(i->second.first, i->second.second, m2, open); - } - } - - // m2 now contains the unreachable objects -} - -void report_unreachable_objects(bool verbose) -{ - map2_type m2; - - mutex_type::scoped_lock lock(get_mutex()); - - map_type const & m = get_map(); - - find_unreachable_objects(m, m2); - - if(verbose) - { - for(map2_type::iterator j = m2.begin(); j != m2.end(); ++j) - { - map_type::const_iterator i = m.find(j->first); - BOOST_ASSERT(i != m.end()); -// std::cerr << "Unreachable object at " << i->second.first << ", " << i->second.second << " bytes long.\n"; - } - } - - if(verbose || !m2.empty()) - { - std::cerr << m2.size() << " unreachable objects.\n"; - } -} - -typedef std::deque< boost::shared_ptr > free_list_type; - -static void scan_and_free(void * area, size_t size, map2_type const & m2, free_list_type & free) -{ - unsigned char * p = static_cast(area); - - for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p += pointer_align, n += pointer_align) - { - shared_ptr_layout * q = reinterpret_cast(p); - - if(q->pn.id == boost::detail::shared_count_id && q->pn.pi != 0 && m2.count(q->pn.pi) != 0 && q->px != 0) - { - boost::shared_ptr * ppx = reinterpret_cast< boost::shared_ptr * >(p); - free.push_back(*ppx); - ppx->reset(); - } - } -} - -void free_unreachable_objects() -{ - map2_type m2; - - mutex_type::scoped_lock lock(get_mutex()); - - map_type const & m = get_map(); - - find_unreachable_objects(m, m2); - - free_list_type free; - - for(map2_type::iterator j = m2.begin(); j != m2.end(); ++j) - { - map_type::const_iterator i = m.find(j->first); - BOOST_ASSERT(i != m.end()); - scan_and_free(i->second.first, i->second.second, m2, free); - } -} - // debug hooks namespace boost @@ -317,11 +124,9 @@ void sp_scalar_constructor_hook(void * p) *pm = adopted_scalar; } -void sp_scalar_constructor_hook(void * px, std::size_t size, void * pn) +void sp_scalar_constructor_hook(void * px, std::size_t, void *) { sp_scalar_constructor_hook(px); - mutex_type::scoped_lock lock(get_mutex()); - get_map()[pn] = std::make_pair(px, size); } void sp_scalar_destructor_hook(void * p) @@ -336,11 +141,9 @@ void sp_scalar_destructor_hook(void * p) *pm = allocated_scalar; } -void sp_scalar_destructor_hook(void * px, std::size_t /*size*/, void * pn) +void sp_scalar_destructor_hook(void * px, std::size_t, void *) { sp_scalar_destructor_hook(px); - mutex_type::scoped_lock lock(get_mutex()); - get_map().erase(pn); } // It is not possible to handle the array hooks in a portable manner. diff --git a/test/collector_test.cpp b/test/collector_test.cpp new file mode 100644 index 0000000..89fd208 --- /dev/null +++ b/test/collector_test.cpp @@ -0,0 +1,100 @@ +// +// collector_test.cpp +// +// Copyright (c) 2003 Peter Dimov +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// + +#include +#include +#include +#include +#include + +// sp_collector.cpp exported functions + +std::size_t find_unreachable_objects(bool report); +void free_unreachable_objects(); + +struct X +{ + void* fill[32]; + boost::shared_ptr p; +}; + +void report() +{ + std::cout << "Calling find_unreachable_objects:\n"; + + std::clock_t t = std::clock(); + + std::size_t n = find_unreachable_objects(false); + + t = std::clock() - t; + + std::cout << n << " unreachable objects.\n"; + std::cout << " " << static_cast(t) / CLOCKS_PER_SEC << " seconds.\n"; +} + +void free() +{ + std::cout << "Calling free_unreachable_objects:\n"; + + std::clock_t t = std::clock(); + + free_unreachable_objects(); + + t = std::clock() - t; + + std::cout << " " << static_cast(t) / CLOCKS_PER_SEC << " seconds.\n"; +} + +int main() +{ + std::vector< boost::shared_ptr > v1, v2; + + int const n = 256 * 1024; + + std::cout << "Filling v1 and v2\n"; + + for(int i = 0; i < n; ++i) + { + v1.push_back(boost::shared_ptr(new X)); + v2.push_back(boost::shared_ptr(new X)); + } + + report(); + + std::cout << "Creating the cycles\n"; + + for(int i = 0; i < n - 1; ++i) + { + v2[i]->p = v2[i+1]; + } + + v2[n-1]->p = v2[0]; + + report(); + + std::cout << "Resizing v2 to size of 1\n"; + + v2.resize(1); + report(); + + std::cout << "Clearing v2\n"; + + v2.clear(); + report(); + + std::cout << "Clearing v1\n"; + + v1.clear(); + report(); + + free(); + report(); +}