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