Jeff Thompson | a28eed8 | 2013-08-22 16:21:10 -0700 | [diff] [blame^] | 1 | // |
| 2 | // sp_collector.cpp |
| 3 | // |
| 4 | // Copyright (c) 2002, 2003 Peter Dimov |
| 5 | // |
| 6 | // Distributed under the Boost Software License, Version 1.0. (See |
| 7 | // accompanying file LICENSE_1_0.txt or copy at |
| 8 | // http://www.boost.org/LICENSE_1_0.txt) |
| 9 | // |
| 10 | |
| 11 | #if defined(BOOST_SP_ENABLE_DEBUG_HOOKS) |
| 12 | |
| 13 | #include <boost/assert.hpp> |
| 14 | #include <boost/shared_ptr.hpp> |
| 15 | #include <boost/detail/lightweight_mutex.hpp> |
| 16 | #include <cstdlib> |
| 17 | #include <map> |
| 18 | #include <deque> |
| 19 | #include <iostream> |
| 20 | |
| 21 | typedef std::map< void const *, std::pair<void *, size_t> > map_type; |
| 22 | |
| 23 | static map_type & get_map() |
| 24 | { |
| 25 | static map_type m; |
| 26 | return m; |
| 27 | } |
| 28 | |
| 29 | typedef ndnboost::detail::lightweight_mutex mutex_type; |
| 30 | |
| 31 | static mutex_type & get_mutex() |
| 32 | { |
| 33 | static mutex_type m; |
| 34 | return m; |
| 35 | } |
| 36 | |
| 37 | static void * init_mutex_before_main = &get_mutex(); |
| 38 | |
| 39 | namespace |
| 40 | { |
| 41 | class X; |
| 42 | |
| 43 | struct count_layout |
| 44 | { |
| 45 | ndnboost::detail::sp_counted_base * pi; |
| 46 | int id; |
| 47 | }; |
| 48 | |
| 49 | struct shared_ptr_layout |
| 50 | { |
| 51 | X * px; |
| 52 | count_layout pn; |
| 53 | }; |
| 54 | } |
| 55 | |
| 56 | // assume 4 byte alignment for pointers when scanning |
| 57 | size_t const pointer_align = 4; |
| 58 | |
| 59 | typedef std::map<void const *, long> map2_type; |
| 60 | |
| 61 | static void scan_and_count(void const * area, size_t size, map_type const & m, map2_type & m2) |
| 62 | { |
| 63 | unsigned char const * p = static_cast<unsigned char const *>(area); |
| 64 | |
| 65 | for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p += pointer_align, n += pointer_align) |
| 66 | { |
| 67 | shared_ptr_layout const * q = reinterpret_cast<shared_ptr_layout const *>(p); |
| 68 | |
| 69 | if(q->pn.id == ndnboost::detail::shared_count_id && q->pn.pi != 0 && m.count(q->pn.pi) != 0) |
| 70 | { |
| 71 | ++m2[q->pn.pi]; |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | typedef std::deque<void const *> open_type; |
| 77 | |
| 78 | static void scan_and_mark(void const * area, size_t size, map2_type & m2, open_type & open) |
| 79 | { |
| 80 | unsigned char const * p = static_cast<unsigned char const *>(area); |
| 81 | |
| 82 | for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p += pointer_align, n += pointer_align) |
| 83 | { |
| 84 | shared_ptr_layout const * q = reinterpret_cast<shared_ptr_layout const *>(p); |
| 85 | |
| 86 | if(q->pn.id == ndnboost::detail::shared_count_id && q->pn.pi != 0 && m2.count(q->pn.pi) != 0) |
| 87 | { |
| 88 | open.push_back(q->pn.pi); |
| 89 | m2.erase(q->pn.pi); |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | static void find_unreachable_objects_impl(map_type const & m, map2_type & m2) |
| 95 | { |
| 96 | // scan objects for shared_ptr members, compute internal counts |
| 97 | |
| 98 | { |
| 99 | std::cout << "... " << m.size() << " objects in m.\n"; |
| 100 | |
| 101 | for(map_type::const_iterator i = m.begin(); i != m.end(); ++i) |
| 102 | { |
| 103 | ndnboost::detail::sp_counted_base const * p = static_cast<ndnboost::detail::sp_counted_base const *>(i->first); |
| 104 | |
| 105 | BOOST_ASSERT(p->use_count() != 0); // there should be no inactive counts in the map |
| 106 | |
| 107 | m2[ i->first ]; |
| 108 | |
| 109 | scan_and_count(i->second.first, i->second.second, m, m2); |
| 110 | } |
| 111 | |
| 112 | std::cout << "... " << m2.size() << " objects in m2.\n"; |
| 113 | } |
| 114 | |
| 115 | // mark reachable objects |
| 116 | |
| 117 | { |
| 118 | open_type open; |
| 119 | |
| 120 | for(map2_type::iterator i = m2.begin(); i != m2.end(); ++i) |
| 121 | { |
| 122 | ndnboost::detail::sp_counted_base const * p = static_cast<ndnboost::detail::sp_counted_base const *>(i->first); |
| 123 | if(p->use_count() != i->second) open.push_back(p); |
| 124 | } |
| 125 | |
| 126 | std::cout << "... " << open.size() << " objects in open.\n"; |
| 127 | |
| 128 | for(open_type::iterator j = open.begin(); j != open.end(); ++j) |
| 129 | { |
| 130 | m2.erase(*j); |
| 131 | } |
| 132 | |
| 133 | while(!open.empty()) |
| 134 | { |
| 135 | void const * p = open.front(); |
| 136 | open.pop_front(); |
| 137 | |
| 138 | map_type::const_iterator i = m.find(p); |
| 139 | BOOST_ASSERT(i != m.end()); |
| 140 | |
| 141 | scan_and_mark(i->second.first, i->second.second, m2, open); |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | // m2 now contains the unreachable objects |
| 146 | } |
| 147 | |
| 148 | std::size_t find_unreachable_objects(bool report) |
| 149 | { |
| 150 | map2_type m2; |
| 151 | |
| 152 | #ifdef BOOST_HAS_THREADS |
| 153 | |
| 154 | // This will work without the #ifdef, but some compilers warn |
| 155 | // that lock is not referenced |
| 156 | |
| 157 | mutex_type::scoped_lock lock(get_mutex()); |
| 158 | |
| 159 | #endif |
| 160 | |
| 161 | map_type const & m = get_map(); |
| 162 | |
| 163 | find_unreachable_objects_impl(m, m2); |
| 164 | |
| 165 | if(report) |
| 166 | { |
| 167 | for(map2_type::iterator j = m2.begin(); j != m2.end(); ++j) |
| 168 | { |
| 169 | map_type::const_iterator i = m.find(j->first); |
| 170 | BOOST_ASSERT(i != m.end()); |
| 171 | std::cout << "Unreachable object at " << i->second.first << ", " << i->second.second << " bytes long.\n"; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | return m2.size(); |
| 176 | } |
| 177 | |
| 178 | typedef std::deque< ndnboost::shared_ptr<X> > free_list_type; |
| 179 | |
| 180 | static void scan_and_free(void * area, size_t size, map2_type const & m2, free_list_type & free) |
| 181 | { |
| 182 | unsigned char * p = static_cast<unsigned char *>(area); |
| 183 | |
| 184 | for(size_t n = 0; n + sizeof(shared_ptr_layout) <= size; p += pointer_align, n += pointer_align) |
| 185 | { |
| 186 | shared_ptr_layout * q = reinterpret_cast<shared_ptr_layout *>(p); |
| 187 | |
| 188 | if(q->pn.id == ndnboost::detail::shared_count_id && q->pn.pi != 0 && m2.count(q->pn.pi) != 0 && q->px != 0) |
| 189 | { |
| 190 | ndnboost::shared_ptr<X> * ppx = reinterpret_cast< ndnboost::shared_ptr<X> * >(p); |
| 191 | free.push_back(*ppx); |
| 192 | ppx->reset(); |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | void free_unreachable_objects() |
| 198 | { |
| 199 | free_list_type free; |
| 200 | |
| 201 | { |
| 202 | map2_type m2; |
| 203 | |
| 204 | #ifdef BOOST_HAS_THREADS |
| 205 | |
| 206 | mutex_type::scoped_lock lock(get_mutex()); |
| 207 | |
| 208 | #endif |
| 209 | |
| 210 | map_type const & m = get_map(); |
| 211 | |
| 212 | find_unreachable_objects_impl(m, m2); |
| 213 | |
| 214 | for(map2_type::iterator j = m2.begin(); j != m2.end(); ++j) |
| 215 | { |
| 216 | map_type::const_iterator i = m.find(j->first); |
| 217 | BOOST_ASSERT(i != m.end()); |
| 218 | scan_and_free(i->second.first, i->second.second, m2, free); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | std::cout << "... about to free " << free.size() << " objects.\n"; |
| 223 | } |
| 224 | |
| 225 | // debug hooks |
| 226 | |
| 227 | namespace ndnboost |
| 228 | { |
| 229 | |
| 230 | void sp_scalar_constructor_hook(void *) |
| 231 | { |
| 232 | } |
| 233 | |
| 234 | void sp_scalar_constructor_hook(void * px, std::size_t size, void * pn) |
| 235 | { |
| 236 | #ifdef BOOST_HAS_THREADS |
| 237 | |
| 238 | mutex_type::scoped_lock lock(get_mutex()); |
| 239 | |
| 240 | #endif |
| 241 | |
| 242 | get_map()[pn] = std::make_pair(px, size); |
| 243 | } |
| 244 | |
| 245 | void sp_scalar_destructor_hook(void *) |
| 246 | { |
| 247 | } |
| 248 | |
| 249 | void sp_scalar_destructor_hook(void *, std::size_t, void * pn) |
| 250 | { |
| 251 | #ifdef BOOST_HAS_THREADS |
| 252 | |
| 253 | mutex_type::scoped_lock lock(get_mutex()); |
| 254 | |
| 255 | #endif |
| 256 | |
| 257 | get_map().erase(pn); |
| 258 | } |
| 259 | |
| 260 | void sp_array_constructor_hook(void *) |
| 261 | { |
| 262 | } |
| 263 | |
| 264 | void sp_array_destructor_hook(void *) |
| 265 | { |
| 266 | } |
| 267 | |
| 268 | } // namespace ndnboost |
| 269 | |
| 270 | #endif // defined(BOOST_SP_ENABLE_DEBUG_HOOKS) |