1 /* Copyright (C) 2000, 2001 Stephen Cleary
2 * Copyright (C) 2011 Kwan Ting Chan
3 *
4 * Use, modification and distribution is subject to the
5 * Boost Software License, Version 1.0. (See accompanying
6 * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
7 */
8
9 #include "random_shuffle.hpp"
10 #include <boost/pool/pool_alloc.hpp>
11 #include <boost/pool/object_pool.hpp>
12
13 #include <boost/detail/lightweight_test.hpp>
14
15 #include <algorithm>
16 #include <deque>
17 #include <list>
18 #include <set>
19 #include <stdexcept>
20 #include <vector>
21
22 #include <cstdlib>
23 #include <ctime>
24
25 // Each "tester" object below checks into and out of the "cdtor_checker",
26 // which will check for any problems related to the construction/destruction of
27 // "tester" objects.
28 class cdtor_checker
29 {
30 private:
31 // Each constructed object registers its "this" pointer into "objs"
32 std::set<void*> objs;
33
34 public:
35 // True iff all objects that have checked in have checked out
ok() const36 bool ok() const { return objs.empty(); }
37
~cdtor_checker()38 ~cdtor_checker()
39 {
40 BOOST_TEST(ok());
41 }
42
check_in(void * const This)43 void check_in(void * const This)
44 {
45 BOOST_TEST(objs.find(This) == objs.end());
46 objs.insert(This);
47 }
48
check_out(void * const This)49 void check_out(void * const This)
50 {
51 BOOST_TEST(objs.find(This) != objs.end());
52 objs.erase(This);
53 }
54 };
55 static cdtor_checker mem;
56
57 struct tester
58 {
testertester59 tester(bool throw_except = false)
60 {
61 if(throw_except)
62 {
63 throw std::logic_error("Deliberate constructor exception");
64 }
65
66 mem.check_in(this);
67 }
68
testertester69 tester(const tester &)
70 {
71 mem.check_in(this);
72 }
73
~testertester74 ~tester()
75 {
76 mem.check_out(this);
77 }
78 };
79
80 // This is a wrapper around a UserAllocator. It just registers alloc/dealloc
81 // to/from the system memory. It's used to make sure pool's are allocating
82 // and deallocating system memory properly.
83 // Do NOT use this class with static or singleton pools.
84 template <typename UserAllocator>
85 struct TrackAlloc
86 {
87 typedef typename UserAllocator::size_type size_type;
88 typedef typename UserAllocator::difference_type difference_type;
89
90 static std::set<char *> allocated_blocks;
91
mallocTrackAlloc92 static char * malloc(const size_type bytes)
93 {
94 char * const ret = UserAllocator::malloc(bytes);
95 allocated_blocks.insert(ret);
96 return ret;
97 }
98
freeTrackAlloc99 static void free(char * const block)
100 {
101 BOOST_TEST(allocated_blocks.find(block) != allocated_blocks.end());
102 allocated_blocks.erase(block);
103 UserAllocator::free(block);
104 }
105
okTrackAlloc106 static bool ok()
107 {
108 return allocated_blocks.empty();
109 }
110 };
111 template <typename UserAllocator>
112 std::set<char *> TrackAlloc<UserAllocator>::allocated_blocks;
113
114 typedef TrackAlloc<boost::default_user_allocator_new_delete> track_alloc;
115
test()116 void test()
117 {
118 {
119 // Do nothing pool
120 boost::object_pool<tester> pool;
121 }
122
123 {
124 // Construct several tester objects. Don't delete them (i.e.,
125 // test pool's garbage collection).
126 boost::object_pool<tester> pool;
127 for(int i=0; i < 10; ++i)
128 {
129 pool.construct();
130 }
131 }
132
133 {
134 // Construct several tester objects. Delete some of them.
135 boost::object_pool<tester> pool;
136 std::vector<tester *> v;
137 for(int i=0; i < 10; ++i)
138 {
139 v.push_back(pool.construct());
140 }
141 pool_test_random_shuffle(v.begin(), v.end());
142 for(int j=0; j < 5; ++j)
143 {
144 pool.destroy(v[j]);
145 }
146 }
147
148 {
149 // Test how pool reacts with constructors that throw exceptions.
150 // Shouldn't have any memory leaks.
151 boost::object_pool<tester> pool;
152 for(int i=0; i < 5; ++i)
153 {
154 pool.construct();
155 }
156 for(int j=0; j < 5; ++j)
157 {
158 try
159 {
160 // The following constructions will raise an exception.
161 pool.construct(true);
162 }
163 catch(const std::logic_error &) {}
164 }
165 }
166 }
167
test_alloc()168 void test_alloc()
169 {
170 {
171 // Allocate several tester objects. Delete one.
172 std::vector<tester, boost::pool_allocator<tester> > l;
173 for(int i=0; i < 10; ++i)
174 {
175 l.push_back(tester());
176 }
177 l.pop_back();
178 }
179
180 {
181 // Allocate several tester objects. Delete two.
182 std::deque<tester, boost::pool_allocator<tester> > l;
183 for(int i=0; i < 10; ++i)
184 {
185 l.push_back(tester());
186 }
187 l.pop_back();
188 l.pop_front();
189 }
190
191 {
192 // Allocate several tester objects. Delete two.
193 std::list<tester, boost::fast_pool_allocator<tester> > l;
194 // lists rebind their allocators, so dumping is useless
195 for(int i=0; i < 10; ++i)
196 {
197 l.push_back(tester());
198 }
199 l.pop_back();
200 l.pop_front();
201 }
202
203 tester * tmp;
204 {
205 // Create a memory leak on purpose. (Allocator doesn't have
206 // garbage collection)
207 // (Note: memory leak)
208 boost::pool_allocator<tester> a;
209 tmp = a.allocate(1, 0);
210 new (tmp) tester();
211 }
212 if(mem.ok())
213 {
214 BOOST_ERROR("Pool allocator cleaned up itself");
215 }
216 // Remove memory checker entry (to avoid error later) and
217 // clean up memory leak
218 tmp->~tester();
219 boost::pool_allocator<tester>::deallocate(tmp, 1);
220
221 // test allocating zero elements
222 {
223 boost::pool_allocator<tester> alloc;
224 tester* ip = alloc.allocate(0);
225 alloc.deallocate(ip, 0);
226 }
227 }
228
test_mem_usage()229 void test_mem_usage()
230 {
231 typedef boost::pool<track_alloc> pool_type;
232
233 {
234 // Constructor should do nothing; no memory allocation
235 pool_type pool(sizeof(int));
236 BOOST_TEST(track_alloc::ok());
237 BOOST_TEST(!pool.release_memory());
238 BOOST_TEST(!pool.purge_memory());
239
240 // Should allocate from system
241 pool.free(pool.malloc());
242 BOOST_TEST(!track_alloc::ok());
243
244 // Ask pool to give up memory it's not using; this should succeed
245 BOOST_TEST(pool.release_memory());
246 BOOST_TEST(track_alloc::ok());
247
248 // Should allocate from system again
249 pool.malloc(); // loses the pointer to the returned chunk (*A*)
250
251 // Ask pool to give up memory it's not using; this should fail
252 BOOST_TEST(!pool.release_memory());
253
254 // Force pool to give up memory it's not using; this should succeed
255 // This will clean up the memory leak from (*A*)
256 BOOST_TEST(pool.purge_memory());
257 BOOST_TEST(track_alloc::ok());
258
259 // Should allocate from system again
260 pool.malloc(); // loses the pointer to the returned chunk (*B*)
261
262 // pool's destructor should purge the memory
263 // This will clean up the memory leak from (*B*)
264 }
265
266 BOOST_TEST(track_alloc::ok());
267 }
268
test_void()269 void test_void()
270 {
271 typedef boost::pool_allocator<void> void_allocator;
272 typedef boost::fast_pool_allocator<void> fast_void_allocator;
273
274 typedef void_allocator::rebind<int>::other int_allocator;
275 typedef fast_void_allocator::rebind<int>::other fast_int_allocator;
276
277 std::vector<int, int_allocator> v1;
278 std::vector<int, fast_int_allocator> v2;
279 }
280
main()281 int main()
282 {
283 std::srand(static_cast<unsigned>(std::time(0)));
284
285 test();
286 test_alloc();
287 test_mem_usage();
288 test_void();
289
290 return boost::report_errors();
291 }
292