]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
b32b8144 | 9 | #include "random_shuffle.hpp" |
7c673cae FG |
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 | |
36 | bool ok() const { return objs.empty(); } | |
37 | ||
38 | ~cdtor_checker() | |
39 | { | |
40 | BOOST_TEST(ok()); | |
41 | } | |
42 | ||
43 | void check_in(void * const This) | |
44 | { | |
45 | BOOST_TEST(objs.find(This) == objs.end()); | |
46 | objs.insert(This); | |
47 | } | |
48 | ||
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 | { | |
59 | 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 | ||
69 | tester(const tester &) | |
70 | { | |
71 | mem.check_in(this); | |
72 | } | |
73 | ||
74 | ~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 | ||
92 | 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 | ||
99 | 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 | ||
106 | 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 | ||
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 | } | |
b32b8144 | 141 | pool_test_random_shuffle(v.begin(), v.end()); |
7c673cae FG |
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 | ||
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 | ||
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 | ||
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 | ||
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 | } |