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 
get_map()23 static map_type & get_map()
24 {
25     static map_type m;
26     return m;
27 }
28 
29 typedef boost::detail::lightweight_mutex mutex_type;
30 
get_mutex()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         boost::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 
scan_and_count(void const * area,size_t size,map_type const & m,map2_type & m2)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 == boost::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 
scan_and_mark(void const * area,size_t size,map2_type & m2,open_type & open)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 == boost::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 
find_unreachable_objects_impl(map_type const & m,map2_type & m2)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             boost::detail::sp_counted_base const * p = static_cast<boost::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             boost::detail::sp_counted_base const * p = static_cast<boost::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 
find_unreachable_objects(bool report)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< boost::shared_ptr<X> > free_list_type;
179 
scan_and_free(void * area,size_t size,map2_type const & m2,free_list_type & free)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 == boost::detail::shared_count_id && q->pn.pi != 0 && m2.count(q->pn.pi) != 0 && q->px != 0)
189         {
190             boost::shared_ptr<X> * ppx = reinterpret_cast< boost::shared_ptr<X> * >(p);
191             free.push_back(*ppx);
192             ppx->reset();
193         }
194     }
195 }
196 
free_unreachable_objects()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 boost
228 {
229 
sp_scalar_constructor_hook(void *)230 void sp_scalar_constructor_hook(void *)
231 {
232 }
233 
sp_scalar_constructor_hook(void * px,std::size_t size,void * pn)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 
sp_scalar_destructor_hook(void *)245 void sp_scalar_destructor_hook(void *)
246 {
247 }
248 
sp_scalar_destructor_hook(void *,std::size_t,void * pn)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 
sp_array_constructor_hook(void *)260 void sp_array_constructor_hook(void *)
261 {
262 }
263 
sp_array_destructor_hook(void *)264 void sp_array_destructor_hook(void *)
265 {
266 }
267 
268 } // namespace boost
269 
270 #endif // defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
271