1 | ////////////////////////////////////////////////////////////////////////////// |
2 | // |
3 | // (C) Copyright Ion Gaztanaga 2004-2019. Distributed under the Boost |
4 | // Software License, Version 1.0. (See accompanying file |
5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
6 | // |
7 | // See http://www.boost.org/libs/interprocess for documentation. |
8 | // |
9 | ////////////////////////////////////////////////////////////////////////////// |
10 | |
11 | #include <boost/interprocess/indexes/flat_map_index.hpp> |
12 | #include <boost/interprocess/indexes/map_index.hpp> |
13 | #include <boost/interprocess/indexes/null_index.hpp> |
14 | #include <boost/interprocess/indexes/iset_index.hpp> |
15 | #include <boost/interprocess/indexes/iunordered_set_index.hpp> |
16 | |
17 | #include <boost/interprocess/mem_algo/simple_seq_fit.hpp> |
18 | #include <boost/interprocess/mem_algo/rbtree_best_fit.hpp> |
19 | #include <boost/interprocess/mapped_region.hpp> |
20 | #include <boost/interprocess/segment_manager.hpp> |
21 | #include <boost/interprocess/shared_memory_object.hpp> |
22 | #include <boost/interprocess/sync/mutex_family.hpp> |
23 | #include <boost/interprocess/exceptions.hpp> |
24 | #include "get_process_id_name.hpp" |
25 | #include <cstddef> |
26 | #include <new> |
27 | #include <cstring> |
28 | #include <typeinfo> |
29 | |
30 | using namespace boost::interprocess; |
31 | |
32 | template <class SegmentManager> |
33 | struct atomic_func_test |
34 | { |
35 | SegmentManager &rsm; |
36 | int *object; |
37 | |
38 | atomic_func_test(SegmentManager &sm) |
39 | : rsm(sm), object() |
40 | {} |
41 | |
42 | void operator()() |
43 | { |
44 | object = rsm.template find<int>("atomic_func_find_object" ).first; |
45 | } |
46 | private: |
47 | atomic_func_test operator=(const atomic_func_test&); |
48 | atomic_func_test(const atomic_func_test&); |
49 | }; |
50 | |
51 | template <class SegmentManager> |
52 | bool test_segment_manager() |
53 | { |
54 | typedef typename SegmentManager::size_type size_type; |
55 | const unsigned int ShmSizeSize = 1024*64u; |
56 | std::string shmname(test::get_process_id_name()); |
57 | |
58 | shared_memory_object::remove(filename: shmname.c_str()); |
59 | shared_memory_object sh_mem( create_only, shmname.c_str(), read_write ); |
60 | sh_mem.truncate( length: ShmSizeSize ); |
61 | mapped_region mapping( sh_mem, read_write ); |
62 | |
63 | SegmentManager* seg_mgr = new( mapping.get_address() ) SegmentManager( ShmSizeSize ); |
64 | std::size_t free_mem_before = seg_mgr->get_free_memory(); |
65 | std::size_t size_before = seg_mgr->get_size(); |
66 | |
67 | if(size_before != ShmSizeSize) |
68 | return false; |
69 | if(!seg_mgr->all_memory_deallocated()) |
70 | return false; |
71 | if(seg_mgr->get_min_size() >= ShmSizeSize) |
72 | return false; |
73 | |
74 | {//test get_free_memory() / allocate()/deallocate() |
75 | const size_type Size = ShmSizeSize/2; |
76 | void *mem = seg_mgr->allocate(Size+1); |
77 | const size_type free_mem = seg_mgr->get_free_memory(); |
78 | if(free_mem >= Size) |
79 | return false; |
80 | if(seg_mgr->all_memory_deallocated()) |
81 | return false; |
82 | const size_type Size2 = free_mem/2; |
83 | void *mem2 = seg_mgr->allocate(size_type(Size2+1), std::nothrow); |
84 | if(seg_mgr->get_free_memory() >= Size2) |
85 | return false; |
86 | if(seg_mgr->size(mem) < (Size+1)) |
87 | return false; |
88 | if(seg_mgr->size(mem2) < (Size2+1)) |
89 | return false; |
90 | seg_mgr->deallocate(mem); |
91 | seg_mgr->deallocate(mem2); |
92 | if(!seg_mgr->all_memory_deallocated()) |
93 | return false; |
94 | if(seg_mgr->get_free_memory() != free_mem_before) |
95 | return false; |
96 | BOOST_TRY{ seg_mgr->allocate(ShmSizeSize*2); }BOOST_CATCH(interprocess_exception&){} BOOST_CATCH_END |
97 | if(seg_mgr->get_free_memory() != free_mem_before) |
98 | return false; |
99 | if(seg_mgr->allocate(ShmSizeSize*2, std::nothrow)) |
100 | return false; |
101 | if(seg_mgr->get_free_memory() != free_mem_before) |
102 | return false; |
103 | } |
104 | {//test allocate_aligned |
105 | const std::size_t Alignment = 128u; |
106 | void *mem = seg_mgr->allocate_aligned(ShmSizeSize/4, Alignment); |
107 | if(seg_mgr->all_memory_deallocated()) |
108 | return false; |
109 | std::size_t offset = static_cast<std::size_t> |
110 | (static_cast<const char *>(mem) - static_cast<const char *>(mapping.get_address())); |
111 | if(offset & (Alignment-1)) |
112 | return false; |
113 | void *mem2 = seg_mgr->allocate_aligned(ShmSizeSize/4, Alignment, std::nothrow); |
114 | std::size_t offset2 = static_cast<std::size_t> |
115 | (static_cast<const char *>(mem2) - static_cast<const char *>(mapping.get_address())); |
116 | if(offset2 & (Alignment-1)) |
117 | return false; |
118 | seg_mgr->deallocate(mem); |
119 | seg_mgr->deallocate(mem2); |
120 | if(!seg_mgr->all_memory_deallocated()) |
121 | return false; |
122 | if(seg_mgr->get_free_memory() != free_mem_before) |
123 | return false; |
124 | BOOST_TRY{ seg_mgr->allocate_aligned(ShmSizeSize*2, Alignment); }BOOST_CATCH(interprocess_exception&){} BOOST_CATCH_END |
125 | if(seg_mgr->get_free_memory() != free_mem_before) |
126 | return false; |
127 | if(seg_mgr->allocate_aligned(ShmSizeSize*2, Alignment, std::nothrow)) |
128 | return false; |
129 | if(seg_mgr->get_free_memory() != free_mem_before) |
130 | return false; |
131 | } |
132 | {//test shrink_to_fit |
133 | |
134 | seg_mgr->shrink_to_fit(); |
135 | if(!seg_mgr->all_memory_deallocated()) |
136 | return false; |
137 | std::size_t empty_shrunk_size = seg_mgr->get_size(); |
138 | std::size_t empty_shrunk_free_mem = seg_mgr->get_free_memory(); |
139 | if(empty_shrunk_size >= size_before) |
140 | return false; |
141 | if(empty_shrunk_free_mem >= size_before) |
142 | return false; |
143 | seg_mgr->grow(size_type(size_before - empty_shrunk_size)); |
144 | if(seg_mgr->get_size() != size_before) |
145 | return false; |
146 | if(seg_mgr->get_free_memory() != free_mem_before) |
147 | return false; |
148 | if(!seg_mgr->all_memory_deallocated()) |
149 | return false; |
150 | } |
151 | {//test zero_free_memory |
152 | const size_type Size(ShmSizeSize/2+1), Size2(ShmSizeSize/8); |
153 | void *mem = seg_mgr->allocate(Size); |
154 | void *mem2 = seg_mgr->allocate(Size2); |
155 | //Mark memory to non-zero |
156 | std::memset(s: mem, c: 0xFF, n: Size); |
157 | std::memset(s: mem2, c: 0xFF, n: Size2); |
158 | //Deallocate and check still non-zero |
159 | seg_mgr->deallocate(mem); |
160 | seg_mgr->deallocate(mem2); |
161 | { //Use byte per byte comparison as "static unsigned char zerobuf[Size]" |
162 | //seems to be problematic in some compilers |
163 | unsigned char *const mem_uch_ptr = static_cast<unsigned char *>(mem); |
164 | unsigned char *const mem2_uch_ptr = static_cast<unsigned char *>(mem2); |
165 | size_type zeroes = 0; |
166 | for(size_type i = 0; i != Size; ++i){ |
167 | if(!mem_uch_ptr[i]) |
168 | ++zeroes; |
169 | } |
170 | if(zeroes == Size) |
171 | return false; |
172 | |
173 | zeroes = 0; |
174 | for(size_type i = 0; i != Size2; ++i){ |
175 | if(!mem2_uch_ptr[i]) |
176 | ++zeroes; |
177 | } |
178 | if(zeroes == Size2) |
179 | return false; |
180 | } |
181 | //zero_free_memory and check it's zeroed |
182 | seg_mgr->zero_free_memory(); |
183 | //TODO: some parts are not zeroed because they are used |
184 | //as internal metadata, find a way to test this |
185 | //if(std::memcmp(mem, zerobuf, Size)) |
186 | //return false; |
187 | //if(std::memcmp(mem2, zerobuf, Size2)) |
188 | //return false; |
189 | if(seg_mgr->get_free_memory() != free_mem_before) |
190 | return false; |
191 | if(!seg_mgr->all_memory_deallocated()) |
192 | return false; |
193 | } |
194 | |
195 | {//test anonymous object |
196 | int *int_object = seg_mgr->template construct<int>(anonymous_instance)(); |
197 | if(1 != seg_mgr->get_instance_length(int_object)) |
198 | return false; |
199 | if(anonymous_type != seg_mgr->get_instance_type(int_object)) |
200 | return false; |
201 | if(seg_mgr->get_instance_name(int_object)) |
202 | return false; |
203 | seg_mgr->destroy_ptr(int_object); |
204 | int const int_array_values[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
205 | int *int_array = seg_mgr->template construct_it<int>(anonymous_instance, std::nothrow)[10](&int_array_values[0]); |
206 | if(10 != seg_mgr->get_instance_length(int_object)) |
207 | return false; |
208 | if(anonymous_type != seg_mgr->get_instance_type(int_array)) |
209 | return false; |
210 | if(seg_mgr->get_instance_name(int_array)) |
211 | return false; |
212 | seg_mgr->destroy_ptr(int_array); |
213 | BOOST_TRY{ seg_mgr->template construct<int>(anonymous_instance)[ShmSizeSize](); }BOOST_CATCH(interprocess_exception&){} BOOST_CATCH_END |
214 | if(seg_mgr->template construct<int>(anonymous_instance, std::nothrow)[ShmSizeSize]()) |
215 | BOOST_TRY{ seg_mgr->template construct_it<long int>(anonymous_instance)[ShmSizeSize](&int_array_values[0]); }BOOST_CATCH(interprocess_exception&){} BOOST_CATCH_END |
216 | if(seg_mgr->template construct_it<int>(anonymous_instance, std::nothrow)[ShmSizeSize](&int_array_values[0])) |
217 | return false; |
218 | if(seg_mgr->get_free_memory() != free_mem_before) |
219 | return false; |
220 | if(!seg_mgr->all_memory_deallocated()) |
221 | return false; |
222 | } |
223 | |
224 | {//test named object |
225 | const char *const object1_name = "object1" ; |
226 | const char *const object2_name = "object2" ; |
227 | int const int_array_values[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
228 | |
229 | for(std::size_t i = 0; i != 1/*4*/; ++i){ |
230 | if(seg_mgr->template find<unsigned int>(object1_name).first) |
231 | return false; |
232 | //Single element construction |
233 | unsigned int *uint_object = 0; |
234 | switch(i){ |
235 | case 0: |
236 | uint_object = seg_mgr->template construct<unsigned int>(object1_name)(); |
237 | break; |
238 | case 1: |
239 | uint_object = seg_mgr->template construct<unsigned int>(object1_name, std::nothrow)(); |
240 | break; |
241 | case 2: |
242 | uint_object = seg_mgr->template find_or_construct<unsigned int>(object1_name)(); |
243 | break; |
244 | case 3: |
245 | uint_object = seg_mgr->template find_or_construct<unsigned int>(object1_name, std::nothrow)(); |
246 | break; |
247 | } |
248 | std::pair<unsigned int*, std::size_t> find_ret = seg_mgr->template find<unsigned int>(object1_name); |
249 | if(uint_object != find_ret.first) |
250 | return false; |
251 | if(1 != find_ret.second) |
252 | return false; |
253 | if(1 != seg_mgr->get_instance_length(uint_object)) |
254 | return false; |
255 | if(named_type != seg_mgr->get_instance_type(uint_object)) |
256 | return false; |
257 | if(std::strcmp(s1: object1_name, s2: seg_mgr->get_instance_name(uint_object))) |
258 | return false; |
259 | //Array construction |
260 | if(seg_mgr->template find<int>(object2_name).first) |
261 | return false; |
262 | int *int_array = 0; |
263 | switch(i){ |
264 | case 0: |
265 | int_array = seg_mgr->template construct_it<int>(object2_name)[10](&int_array_values[0]); |
266 | break; |
267 | case 1: |
268 | int_array = seg_mgr->template construct_it<int>(object2_name, std::nothrow)[10](&int_array_values[0]); |
269 | break; |
270 | case 2: |
271 | int_array = seg_mgr->template find_or_construct_it<int>(object2_name)[10](&int_array_values[0]); |
272 | break; |
273 | case 3: |
274 | int_array = seg_mgr->template find_or_construct_it<int>(object2_name, std::nothrow)[10](&int_array_values[0]); |
275 | break; |
276 | } |
277 | std::pair<int*, std::size_t> find_ret2 = seg_mgr->template find<int>(object2_name); |
278 | if(int_array != find_ret2.first) |
279 | return false; |
280 | if(10 != find_ret2.second) |
281 | return false; |
282 | if(10 != seg_mgr->get_instance_length(int_array)) |
283 | return false; |
284 | if(named_type != seg_mgr->get_instance_type(int_array)) |
285 | return false; |
286 | if(std::strcmp(s1: object2_name, s2: seg_mgr->get_instance_name(int_array))) |
287 | return false; |
288 | if(seg_mgr->get_num_named_objects() != 2) |
289 | return false; |
290 | typename SegmentManager::const_named_iterator nb(seg_mgr->named_begin()); |
291 | typename SegmentManager::const_named_iterator ne(seg_mgr->named_end()); |
292 | for(std::size_t j = 0, imax = seg_mgr->get_num_named_objects(); j != imax; ++j){ ++nb; } |
293 | if(nb != ne) |
294 | return false; |
295 | seg_mgr->destroy_ptr(uint_object); |
296 | seg_mgr->template destroy<int>(object2_name); |
297 | } |
298 | BOOST_TRY{ seg_mgr->template construct<unsigned int>(object1_name)[ShmSizeSize](); }BOOST_CATCH(interprocess_exception&){} BOOST_CATCH_END |
299 | if(seg_mgr->template construct<int>(object2_name, std::nothrow)[ShmSizeSize]()) |
300 | BOOST_TRY{ seg_mgr->template construct_it<int>(object1_name)[ShmSizeSize](&int_array_values[0]); }BOOST_CATCH(interprocess_exception&){} BOOST_CATCH_END |
301 | if(seg_mgr->template construct_it<int>(object2_name, std::nothrow)[ShmSizeSize](&int_array_values[0])) |
302 | return false; |
303 | seg_mgr->shrink_to_fit_indexes(); |
304 | if(seg_mgr->get_free_memory() != free_mem_before) |
305 | return false; |
306 | if(!seg_mgr->all_memory_deallocated()) |
307 | return false; |
308 | seg_mgr->reserve_named_objects(1); |
309 | //In indexes with no capacity() memory won't be allocated so don't check anything was allocated. |
310 | //if(seg_mgr->all_memory_deallocated()) return false; |
311 | seg_mgr->shrink_to_fit_indexes(); |
312 | if(!seg_mgr->all_memory_deallocated()) |
313 | return false; |
314 | } |
315 | |
316 | {//test unique object |
317 | int const int_array_values[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; |
318 | |
319 | for(std::size_t i = 0; i != 4; ++i){ |
320 | if(seg_mgr->template find<unsigned int>(unique_instance).first) |
321 | return false; |
322 | //Single element construction |
323 | unsigned int *uint_object = 0; |
324 | switch(i){ |
325 | case 0: |
326 | uint_object = seg_mgr->template construct<unsigned int>(unique_instance)(); |
327 | break; |
328 | case 1: |
329 | uint_object = seg_mgr->template construct<unsigned int>(unique_instance, std::nothrow)(); |
330 | break; |
331 | case 2: |
332 | uint_object = seg_mgr->template find_or_construct<unsigned int>(unique_instance)(); |
333 | break; |
334 | case 3: |
335 | uint_object = seg_mgr->template find_or_construct<unsigned int>(unique_instance, std::nothrow)(); |
336 | break; |
337 | } |
338 | std::pair<unsigned int*, std::size_t> find_ret = seg_mgr->template find<unsigned int>(unique_instance); |
339 | if(uint_object != find_ret.first) |
340 | return false; |
341 | if(1 != find_ret.second) |
342 | return false; |
343 | if(1 != seg_mgr->get_instance_length(uint_object)) |
344 | return false; |
345 | if(unique_type != seg_mgr->get_instance_type(uint_object)) |
346 | return false; |
347 | if(std::strcmp(s1: typeid(unsigned int).name(), s2: seg_mgr->get_instance_name(uint_object))) |
348 | return false; |
349 | //Array construction |
350 | if(seg_mgr->template find<int>(unique_instance).first) |
351 | return false; |
352 | int *int_array = 0; |
353 | switch(i){ |
354 | case 0: |
355 | int_array = seg_mgr->template construct_it<int>(unique_instance)[10](&int_array_values[0]); |
356 | break; |
357 | case 1: |
358 | int_array = seg_mgr->template construct_it<int>(unique_instance, std::nothrow)[10](&int_array_values[0]); |
359 | break; |
360 | case 2: |
361 | int_array = seg_mgr->template find_or_construct_it<int>(unique_instance)[10](&int_array_values[0]); |
362 | break; |
363 | case 3: |
364 | int_array = seg_mgr->template find_or_construct_it<int>(unique_instance, std::nothrow)[10](&int_array_values[0]); |
365 | break; |
366 | } |
367 | std::pair<int*, std::size_t> find_ret2 = seg_mgr->template find<int>(unique_instance); |
368 | if(int_array != find_ret2.first) |
369 | return false; |
370 | if(10 != find_ret2.second) |
371 | return false; |
372 | if(10 != seg_mgr->get_instance_length(int_array)) |
373 | return false; |
374 | if(unique_type != seg_mgr->get_instance_type(int_array)) |
375 | return false; |
376 | if(std::strcmp(s1: typeid(int).name(), s2: seg_mgr->get_instance_name(int_array))) |
377 | return false; |
378 | if(seg_mgr->get_num_unique_objects() != 2) |
379 | return false; |
380 | typename SegmentManager::const_unique_iterator nb(seg_mgr->unique_begin()); |
381 | typename SegmentManager::const_unique_iterator ne(seg_mgr->unique_end()); |
382 | for(std::size_t j = 0, imax = seg_mgr->get_num_unique_objects(); j != imax; ++j){ ++nb; } |
383 | if(nb != ne) |
384 | return false; |
385 | seg_mgr->destroy_ptr(uint_object); |
386 | seg_mgr->template destroy<int>(unique_instance); |
387 | } |
388 | BOOST_TRY{ seg_mgr->template construct<unsigned int>(unique_instance)[ShmSizeSize](); }BOOST_CATCH(interprocess_exception&){} BOOST_CATCH_END |
389 | if(seg_mgr->template construct<int>(unique_instance, std::nothrow)[ShmSizeSize]()) |
390 | BOOST_TRY{ seg_mgr->template construct_it<long int>(unique_instance)[ShmSizeSize](&int_array_values[0]); }BOOST_CATCH(interprocess_exception&){} BOOST_CATCH_END |
391 | if(seg_mgr->template construct_it<int>(unique_instance, std::nothrow)[ShmSizeSize](&int_array_values[0])) |
392 | return false; |
393 | seg_mgr->shrink_to_fit_indexes(); |
394 | if(seg_mgr->get_free_memory() != free_mem_before) |
395 | return false; |
396 | if(!seg_mgr->all_memory_deallocated()) |
397 | return false; |
398 | seg_mgr->reserve_unique_objects(1); |
399 | //In indexes with no capacity() memory won't be allocated so don't check anything was allocated. |
400 | //if(seg_mgr->all_memory_deallocated()) return false; |
401 | seg_mgr->shrink_to_fit_indexes(); |
402 | if(!seg_mgr->all_memory_deallocated()) |
403 | return false; |
404 | } |
405 | {//test allocator/deleter |
406 | if(!seg_mgr->all_memory_deallocated()) |
407 | return false; |
408 | typedef typename SegmentManager::template allocator<float>::type allocator_t; |
409 | |
410 | allocator_t alloc(seg_mgr->template get_allocator<float>()); |
411 | |
412 | if(!seg_mgr->all_memory_deallocated()) |
413 | return false; |
414 | offset_ptr<float> f = alloc.allocate(50); |
415 | if(seg_mgr->all_memory_deallocated()) |
416 | return false; |
417 | alloc.deallocate(f, 50); |
418 | if(!seg_mgr->all_memory_deallocated()) |
419 | return false; |
420 | typedef typename SegmentManager::template deleter<float>::type deleter_t; |
421 | deleter_t delet(seg_mgr->template get_deleter<float>()); |
422 | delet(seg_mgr->template construct<float>(anonymous_instance)()); |
423 | if(!seg_mgr->all_memory_deallocated()) |
424 | return false; |
425 | } |
426 | {//test allocator/deleter |
427 | if(!seg_mgr->all_memory_deallocated()) |
428 | return false; |
429 | int *int_object = seg_mgr->template construct<int>("atomic_func_find_object" )(); |
430 | atomic_func_test<SegmentManager> func(*seg_mgr); |
431 | seg_mgr->atomic_func(func); |
432 | if(int_object != func.object) |
433 | return 1; |
434 | seg_mgr->destroy_ptr(int_object); |
435 | seg_mgr->shrink_to_fit_indexes(); |
436 | if(!seg_mgr->all_memory_deallocated()) |
437 | return false; |
438 | } |
439 | {//test get_memory_algorithm |
440 | { |
441 | typename SegmentManager::memory_algorithm & mem_algo = |
442 | seg_mgr->get_memory_algorithm(); |
443 | const typename SegmentManager::memory_algorithm & const_mem_algo = |
444 | const_cast<const SegmentManager*>(seg_mgr)->get_memory_algorithm(); |
445 | if (&mem_algo != &const_mem_algo) |
446 | return false; |
447 | } |
448 | } |
449 | return true; |
450 | } |
451 | |
452 | template<class MemoryAlgorithm> |
453 | bool test_each_algo() |
454 | { |
455 | { |
456 | typedef segment_manager< char, MemoryAlgorithm, flat_map_index > segment_manager_t; |
457 | if(!test_segment_manager<segment_manager_t>()) |
458 | return false; |
459 | } |
460 | { |
461 | typedef segment_manager< char, MemoryAlgorithm, map_index > segment_manager_t; |
462 | if(!test_segment_manager<segment_manager_t>()) |
463 | return false; |
464 | } |
465 | { |
466 | typedef segment_manager< char, MemoryAlgorithm, iset_index > segment_manager_t; |
467 | if(!test_segment_manager<segment_manager_t>()) |
468 | return false; |
469 | } |
470 | { |
471 | typedef segment_manager< char, MemoryAlgorithm, iunordered_set_index > segment_manager_t; |
472 | if(!test_segment_manager<segment_manager_t>()) |
473 | return false; |
474 | } |
475 | return true; |
476 | } |
477 | |
478 | int main() |
479 | { |
480 | if(!test_each_algo< simple_seq_fit< null_mutex_family > >()) |
481 | return 1; |
482 | if(!test_each_algo< rbtree_best_fit< null_mutex_family > >()) |
483 | return 1; |
484 | |
485 | return 0; |
486 | } |
487 | |