Chris@16
|
1 //////////////////////////////////////////////////////////////////////////////
|
Chris@16
|
2 //
|
Chris@16
|
3 // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
|
Chris@16
|
4 // Software License, Version 1.0. (See accompanying file
|
Chris@16
|
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
6 //
|
Chris@16
|
7 // See http://www.boost.org/libs/interprocess for documentation.
|
Chris@16
|
8 //
|
Chris@16
|
9 //////////////////////////////////////////////////////////////////////////////
|
Chris@16
|
10
|
Chris@16
|
11 #ifndef BOOST_INTERPROCESS_IUNORDERED_SET_INDEX_HPP
|
Chris@16
|
12 #define BOOST_INTERPROCESS_IUNORDERED_SET_INDEX_HPP
|
Chris@16
|
13
|
Chris@101
|
14 #ifndef BOOST_CONFIG_HPP
|
Chris@101
|
15 # include <boost/config.hpp>
|
Chris@101
|
16 #endif
|
Chris@101
|
17 #
|
Chris@101
|
18 #if defined(BOOST_HAS_PRAGMA_ONCE)
|
Chris@101
|
19 # pragma once
|
Chris@101
|
20 #endif
|
Chris@101
|
21
|
Chris@16
|
22 #include <boost/interprocess/detail/config_begin.hpp>
|
Chris@16
|
23 #include <boost/interprocess/detail/workaround.hpp>
|
Chris@16
|
24
|
Chris@16
|
25 #include <boost/interprocess/detail/utilities.hpp>
|
Chris@101
|
26 #include <boost/interprocess/allocators/allocator.hpp>
|
Chris@16
|
27 #include <boost/interprocess/containers/vector.hpp>
|
Chris@16
|
28 #include <boost/intrusive/unordered_set.hpp>
|
Chris@101
|
29 #include <boost/intrusive/detail/minimal_pair_header.hpp>
|
Chris@101
|
30 #include <boost/intrusive/detail/minimal_less_equal_header.hpp> //std::less
|
Chris@101
|
31 #include <boost/container/detail/minimal_char_traits_header.hpp> //std::char_traits
|
Chris@101
|
32 #include <boost/container/detail/placement_new.hpp>
|
Chris@16
|
33
|
Chris@16
|
34 //!\file
|
Chris@16
|
35 //!Describes index adaptor of boost::intrusive::unordered_set container, to use it
|
Chris@16
|
36 //!as name/shared memory index
|
Chris@16
|
37
|
Chris@16
|
38 namespace boost { namespace interprocess {
|
Chris@16
|
39
|
Chris@101
|
40 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
|
Chris@16
|
41
|
Chris@16
|
42 //!Helper class to define typedefs
|
Chris@16
|
43 //!from IndexTraits
|
Chris@16
|
44 template <class MapConfig>
|
Chris@16
|
45 struct iunordered_set_index_aux
|
Chris@16
|
46 {
|
Chris@16
|
47 typedef typename
|
Chris@16
|
48 MapConfig::segment_manager_base segment_manager_base;
|
Chris@16
|
49
|
Chris@16
|
50 typedef typename
|
Chris@16
|
51 segment_manager_base::void_pointer void_pointer;
|
Chris@16
|
52
|
Chris@16
|
53 typedef typename bi::make_unordered_set_base_hook
|
Chris@16
|
54 < bi::void_pointer<void_pointer>
|
Chris@16
|
55 >::type derivation_hook;
|
Chris@16
|
56
|
Chris@16
|
57 typedef typename MapConfig::template
|
Chris@16
|
58 intrusive_value_type<derivation_hook>::type value_type;
|
Chris@16
|
59
|
Chris@16
|
60 typedef typename MapConfig::
|
Chris@16
|
61 intrusive_compare_key_type intrusive_compare_key_type;
|
Chris@16
|
62
|
Chris@16
|
63 typedef std::equal_to<value_type> value_equal;
|
Chris@16
|
64
|
Chris@16
|
65 typedef typename MapConfig::char_type char_type;
|
Chris@16
|
66
|
Chris@16
|
67 struct equal_function
|
Chris@16
|
68 {
|
Chris@16
|
69 bool operator()(const intrusive_compare_key_type &i, const value_type &b) const
|
Chris@16
|
70 {
|
Chris@16
|
71 return (i.m_len == b.name_length()) &&
|
Chris@16
|
72 (std::char_traits<char_type>::compare
|
Chris@16
|
73 (i.mp_str, b.name(), i.m_len) == 0);
|
Chris@16
|
74 }
|
Chris@16
|
75
|
Chris@16
|
76 bool operator()(const value_type &b, const intrusive_compare_key_type &i) const
|
Chris@16
|
77 {
|
Chris@16
|
78 return (i.m_len == b.name_length()) &&
|
Chris@16
|
79 (std::char_traits<char_type>::compare
|
Chris@16
|
80 (i.mp_str, b.name(), i.m_len) == 0);
|
Chris@16
|
81 }
|
Chris@16
|
82
|
Chris@16
|
83 bool operator()(const value_type &b1, const value_type &b2) const
|
Chris@16
|
84 {
|
Chris@16
|
85 return (b1.name_length() == b2.name_length()) &&
|
Chris@16
|
86 (std::char_traits<char_type>::compare
|
Chris@16
|
87 (b1.name(), b2.name(), b1.name_length()) == 0);
|
Chris@16
|
88 }
|
Chris@16
|
89 };
|
Chris@16
|
90
|
Chris@16
|
91 struct hash_function
|
Chris@16
|
92 : std::unary_function<value_type, std::size_t>
|
Chris@16
|
93 {
|
Chris@16
|
94 std::size_t operator()(const value_type &val) const
|
Chris@16
|
95 {
|
Chris@16
|
96 const char_type *beg = ipcdetail::to_raw_pointer(val.name()),
|
Chris@16
|
97 *end = beg + val.name_length();
|
Chris@16
|
98 return boost::hash_range(beg, end);
|
Chris@16
|
99 }
|
Chris@16
|
100
|
Chris@16
|
101 std::size_t operator()(const intrusive_compare_key_type &i) const
|
Chris@16
|
102 {
|
Chris@16
|
103 const char_type *beg = i.mp_str,
|
Chris@16
|
104 *end = beg + i.m_len;
|
Chris@16
|
105 return boost::hash_range(beg, end);
|
Chris@16
|
106 }
|
Chris@16
|
107 };
|
Chris@16
|
108
|
Chris@16
|
109 typedef typename bi::make_unordered_set
|
Chris@16
|
110 < value_type
|
Chris@16
|
111 , bi::hash<hash_function>
|
Chris@16
|
112 , bi::equal<equal_function>
|
Chris@16
|
113 , bi::size_type<typename segment_manager_base::size_type>
|
Chris@16
|
114 >::type index_t;
|
Chris@16
|
115 typedef typename index_t::bucket_type bucket_type;
|
Chris@16
|
116 typedef allocator
|
Chris@16
|
117 <bucket_type, segment_manager_base> allocator_type;
|
Chris@16
|
118
|
Chris@16
|
119 struct allocator_holder
|
Chris@16
|
120 {
|
Chris@16
|
121 allocator_holder(segment_manager_base *mngr)
|
Chris@16
|
122 : alloc(mngr)
|
Chris@16
|
123 {}
|
Chris@16
|
124 allocator_type alloc;
|
Chris@16
|
125 bucket_type init_bucket;
|
Chris@16
|
126 };
|
Chris@16
|
127 };
|
Chris@101
|
128 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
|
Chris@16
|
129
|
Chris@16
|
130 //!Index type based in boost::intrusive::set.
|
Chris@16
|
131 //!Just derives from boost::intrusive::set
|
Chris@16
|
132 //!and defines the interface needed by managed memory segments
|
Chris@16
|
133 template <class MapConfig>
|
Chris@16
|
134 class iunordered_set_index
|
Chris@16
|
135 //Derive class from map specialization
|
Chris@16
|
136 : private iunordered_set_index_aux<MapConfig>::allocator_holder
|
Chris@16
|
137 , public iunordered_set_index_aux<MapConfig>::index_t
|
Chris@16
|
138 {
|
Chris@101
|
139 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
|
Chris@16
|
140 typedef iunordered_set_index_aux<MapConfig> index_aux;
|
Chris@16
|
141 typedef typename index_aux::index_t index_type;
|
Chris@16
|
142 typedef typename MapConfig::
|
Chris@16
|
143 intrusive_compare_key_type intrusive_compare_key_type;
|
Chris@16
|
144 typedef typename index_aux::equal_function equal_function;
|
Chris@16
|
145 typedef typename index_aux::hash_function hash_function;
|
Chris@16
|
146 typedef typename MapConfig::char_type char_type;
|
Chris@16
|
147 typedef typename
|
Chris@16
|
148 iunordered_set_index_aux<MapConfig>::allocator_type allocator_type;
|
Chris@16
|
149 typedef typename
|
Chris@16
|
150 iunordered_set_index_aux<MapConfig>::allocator_holder allocator_holder;
|
Chris@101
|
151 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
|
Chris@16
|
152
|
Chris@16
|
153 public:
|
Chris@16
|
154 typedef typename index_type::iterator iterator;
|
Chris@16
|
155 typedef typename index_type::const_iterator const_iterator;
|
Chris@16
|
156 typedef typename index_type::insert_commit_data insert_commit_data;
|
Chris@16
|
157 typedef typename index_type::value_type value_type;
|
Chris@16
|
158 typedef typename index_type::bucket_ptr bucket_ptr;
|
Chris@16
|
159 typedef typename index_type::bucket_type bucket_type;
|
Chris@16
|
160 typedef typename index_type::bucket_traits bucket_traits;
|
Chris@16
|
161 typedef typename index_type::size_type size_type;
|
Chris@16
|
162
|
Chris@101
|
163 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
|
Chris@16
|
164 private:
|
Chris@16
|
165 typedef typename index_aux::
|
Chris@16
|
166 segment_manager_base segment_manager_base;
|
Chris@16
|
167
|
Chris@16
|
168 static const std::size_t InitBufferSize = 64;
|
Chris@16
|
169
|
Chris@16
|
170 static bucket_ptr create_buckets(allocator_type &alloc, size_type num)
|
Chris@16
|
171 {
|
Chris@16
|
172 num = index_type::suggested_upper_bucket_count(num);
|
Chris@16
|
173 bucket_ptr buckets = alloc.allocate(num);
|
Chris@16
|
174 bucket_ptr buckets_init = buckets;
|
Chris@16
|
175 for(size_type i = 0; i < num; ++i){
|
Chris@101
|
176 ::new(to_raw_pointer(buckets_init++), boost_container_new_t())bucket_type();
|
Chris@16
|
177 }
|
Chris@16
|
178 return buckets;
|
Chris@16
|
179 }
|
Chris@16
|
180
|
Chris@16
|
181 static size_type shrink_buckets
|
Chris@16
|
182 ( bucket_ptr buckets, size_type old_size
|
Chris@16
|
183 , allocator_type &alloc, size_type new_size)
|
Chris@16
|
184 {
|
Chris@16
|
185 if(old_size <= new_size )
|
Chris@16
|
186 return old_size;
|
Chris@101
|
187 size_type received_size = new_size;
|
Chris@16
|
188 if(!alloc.allocation_command
|
Chris@101
|
189 (boost::interprocess::try_shrink_in_place | boost::interprocess::nothrow_allocation, old_size, received_size, buckets)){
|
Chris@16
|
190 return old_size;
|
Chris@16
|
191 }
|
Chris@16
|
192
|
Chris@16
|
193 for( bucket_type *p = ipcdetail::to_raw_pointer(buckets) + received_size
|
Chris@16
|
194 , *pend = ipcdetail::to_raw_pointer(buckets) + old_size
|
Chris@16
|
195 ; p != pend
|
Chris@16
|
196 ; ++p){
|
Chris@16
|
197 p->~bucket_type();
|
Chris@16
|
198 }
|
Chris@16
|
199
|
Chris@16
|
200 bucket_ptr shunk_p = alloc.allocation_command
|
Chris@101
|
201 (boost::interprocess::shrink_in_place | boost::interprocess::nothrow_allocation, received_size, received_size, buckets);
|
Chris@16
|
202 BOOST_ASSERT(buckets == shunk_p); (void)shunk_p;
|
Chris@16
|
203
|
Chris@16
|
204 bucket_ptr buckets_init = buckets + received_size;
|
Chris@16
|
205 for(size_type i = 0; i < (old_size - received_size); ++i){
|
Chris@16
|
206 to_raw_pointer(buckets_init++)->~bucket_type();
|
Chris@16
|
207 }
|
Chris@16
|
208 return received_size;
|
Chris@16
|
209 }
|
Chris@16
|
210
|
Chris@16
|
211 static bucket_ptr expand_or_create_buckets
|
Chris@16
|
212 ( bucket_ptr old_buckets, const size_type old_num
|
Chris@16
|
213 , allocator_type &alloc, const size_type new_num)
|
Chris@16
|
214 {
|
Chris@101
|
215 size_type received_size = new_num;
|
Chris@101
|
216 bucket_ptr reuse(old_buckets);
|
Chris@101
|
217 bucket_ptr ret = alloc.allocation_command
|
Chris@101
|
218 (boost::interprocess::expand_fwd | boost::interprocess::allocate_new, new_num, received_size, reuse);
|
Chris@101
|
219 if(ret == old_buckets){
|
Chris@16
|
220 bucket_ptr buckets_init = old_buckets + old_num;
|
Chris@16
|
221 for(size_type i = 0; i < (new_num - old_num); ++i){
|
Chris@101
|
222 ::new(to_raw_pointer(buckets_init++), boost_container_new_t())bucket_type();
|
Chris@16
|
223 }
|
Chris@16
|
224 }
|
Chris@16
|
225 else{
|
Chris@101
|
226 bucket_ptr buckets_init = ret;
|
Chris@16
|
227 for(size_type i = 0; i < new_num; ++i){
|
Chris@101
|
228 ::new(to_raw_pointer(buckets_init++), boost_container_new_t())bucket_type();
|
Chris@16
|
229 }
|
Chris@16
|
230 }
|
Chris@101
|
231 return ret;
|
Chris@16
|
232 }
|
Chris@16
|
233
|
Chris@16
|
234 static void destroy_buckets
|
Chris@16
|
235 (allocator_type &alloc, bucket_ptr buckets, size_type num)
|
Chris@16
|
236 {
|
Chris@16
|
237 bucket_ptr buckets_destroy = buckets;
|
Chris@16
|
238 for(size_type i = 0; i < num; ++i){
|
Chris@16
|
239 to_raw_pointer(buckets_destroy++)->~bucket_type();
|
Chris@16
|
240 }
|
Chris@16
|
241 alloc.deallocate(buckets, num);
|
Chris@16
|
242 }
|
Chris@16
|
243
|
Chris@16
|
244 iunordered_set_index<MapConfig>* get_this_pointer()
|
Chris@16
|
245 { return this; }
|
Chris@16
|
246
|
Chris@101
|
247 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
|
Chris@16
|
248
|
Chris@16
|
249 public:
|
Chris@16
|
250 //!Constructor. Takes a pointer to the
|
Chris@16
|
251 //!segment manager. Can throw
|
Chris@16
|
252 iunordered_set_index(segment_manager_base *mngr)
|
Chris@16
|
253 : allocator_holder(mngr)
|
Chris@16
|
254 , index_type(bucket_traits(&get_this_pointer()->init_bucket, 1))
|
Chris@16
|
255 {}
|
Chris@16
|
256
|
Chris@16
|
257 ~iunordered_set_index()
|
Chris@16
|
258 {
|
Chris@16
|
259 index_type::clear();
|
Chris@16
|
260 if(index_type::bucket_pointer() != bucket_ptr(&this->init_bucket)){
|
Chris@16
|
261 destroy_buckets(this->alloc, index_type::bucket_pointer(), index_type::bucket_count());
|
Chris@16
|
262 }
|
Chris@16
|
263 }
|
Chris@16
|
264
|
Chris@16
|
265 //!This reserves memory to optimize the insertion of n
|
Chris@16
|
266 //!elements in the index
|
Chris@16
|
267 void reserve(size_type new_n)
|
Chris@16
|
268 {
|
Chris@16
|
269 //Let's maintain a 1.0f load factor
|
Chris@16
|
270 size_type old_n = this->bucket_count();
|
Chris@16
|
271 if(new_n <= old_n)
|
Chris@16
|
272 return;
|
Chris@16
|
273 bucket_ptr old_p = this->bucket_pointer();
|
Chris@16
|
274 new_n = index_type::suggested_upper_bucket_count(new_n);
|
Chris@16
|
275 bucket_ptr new_p;
|
Chris@16
|
276 //This can throw
|
Chris@16
|
277 try{
|
Chris@16
|
278 if(old_p != bucket_ptr(&this->init_bucket))
|
Chris@16
|
279 new_p = expand_or_create_buckets(old_p, old_n, this->alloc, new_n);
|
Chris@16
|
280 else
|
Chris@16
|
281 new_p = create_buckets(this->alloc, new_n);
|
Chris@16
|
282 }
|
Chris@16
|
283 catch(...){
|
Chris@16
|
284 return;
|
Chris@16
|
285 }
|
Chris@16
|
286 //Rehashing does not throw, since neither the hash nor the
|
Chris@16
|
287 //comparison function can throw
|
Chris@16
|
288 this->rehash(bucket_traits(new_p, new_n));
|
Chris@16
|
289 if(new_p != old_p && old_p != bucket_ptr(&this->init_bucket)){
|
Chris@16
|
290 destroy_buckets(this->alloc, old_p, old_n);
|
Chris@16
|
291 }
|
Chris@16
|
292 }
|
Chris@16
|
293
|
Chris@16
|
294 //!This tries to free unused memory
|
Chris@16
|
295 //!previously allocated.
|
Chris@16
|
296 void shrink_to_fit()
|
Chris@16
|
297 {
|
Chris@16
|
298 size_type cur_size = this->size();
|
Chris@16
|
299 size_type cur_count = this->bucket_count();
|
Chris@16
|
300 bucket_ptr old_p = this->bucket_pointer();
|
Chris@16
|
301
|
Chris@16
|
302 if(!this->size() && old_p != bucket_ptr(&this->init_bucket)){
|
Chris@16
|
303 this->rehash(bucket_traits(bucket_ptr(&this->init_bucket), 1));
|
Chris@16
|
304 destroy_buckets(this->alloc, old_p, cur_count);
|
Chris@16
|
305 }
|
Chris@16
|
306 else{
|
Chris@16
|
307 size_type sug_count = 0; //gcc warning
|
Chris@16
|
308 sug_count = index_type::suggested_upper_bucket_count(cur_size);
|
Chris@16
|
309
|
Chris@16
|
310 if(sug_count >= cur_count)
|
Chris@16
|
311 return;
|
Chris@16
|
312
|
Chris@16
|
313 try{
|
Chris@16
|
314 shrink_buckets(old_p, cur_count, this->alloc, sug_count);
|
Chris@16
|
315 }
|
Chris@16
|
316 catch(...){
|
Chris@16
|
317 return;
|
Chris@16
|
318 }
|
Chris@16
|
319
|
Chris@16
|
320 //Rehashing does not throw, since neither the hash nor the
|
Chris@16
|
321 //comparison function can throw
|
Chris@16
|
322 this->rehash(bucket_traits(old_p, sug_count));
|
Chris@16
|
323 }
|
Chris@16
|
324 }
|
Chris@16
|
325
|
Chris@16
|
326 iterator find(const intrusive_compare_key_type &key)
|
Chris@16
|
327 { return index_type::find(key, hash_function(), equal_function()); }
|
Chris@16
|
328
|
Chris@16
|
329 const_iterator find(const intrusive_compare_key_type &key) const
|
Chris@16
|
330 { return index_type::find(key, hash_function(), equal_function()); }
|
Chris@16
|
331
|
Chris@16
|
332 std::pair<iterator, bool>insert_check
|
Chris@16
|
333 (const intrusive_compare_key_type &key, insert_commit_data &commit_data)
|
Chris@16
|
334 { return index_type::insert_check(key, hash_function(), equal_function(), commit_data); }
|
Chris@16
|
335
|
Chris@16
|
336 iterator insert_commit(value_type &val, insert_commit_data &commit_data)
|
Chris@16
|
337 {
|
Chris@16
|
338 iterator it = index_type::insert_commit(val, commit_data);
|
Chris@16
|
339 size_type cur_size = this->size();
|
Chris@16
|
340 if(cur_size > this->bucket_count()){
|
Chris@16
|
341 try{
|
Chris@16
|
342 this->reserve(cur_size);
|
Chris@16
|
343 }
|
Chris@16
|
344 catch(...){
|
Chris@16
|
345 //Strong guarantee: if something goes wrong
|
Chris@16
|
346 //we should remove the insertion.
|
Chris@16
|
347 //
|
Chris@16
|
348 //We can use the iterator because the hash function
|
Chris@16
|
349 //can't throw and this means that "reserve" will
|
Chris@16
|
350 //throw only because of the memory allocation:
|
Chris@16
|
351 //the iterator has not been invalidated.
|
Chris@16
|
352 index_type::erase(it);
|
Chris@16
|
353 throw;
|
Chris@16
|
354 }
|
Chris@16
|
355 }
|
Chris@16
|
356 return it;
|
Chris@16
|
357 }
|
Chris@16
|
358 };
|
Chris@16
|
359
|
Chris@101
|
360 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
|
Chris@16
|
361
|
Chris@16
|
362 //!Trait class to detect if an index is an intrusive
|
Chris@16
|
363 //!index
|
Chris@16
|
364 template<class MapConfig>
|
Chris@16
|
365 struct is_intrusive_index
|
Chris@16
|
366 <boost::interprocess::iunordered_set_index<MapConfig> >
|
Chris@16
|
367 {
|
Chris@16
|
368 static const bool value = true;
|
Chris@16
|
369 };
|
Chris@101
|
370 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
|
Chris@16
|
371
|
Chris@16
|
372 }} //namespace boost { namespace interprocess {
|
Chris@16
|
373
|
Chris@16
|
374 #include <boost/interprocess/detail/config_end.hpp>
|
Chris@16
|
375
|
Chris@16
|
376 #endif //#ifndef BOOST_INTERPROCESS_IUNORDERED_SET_INDEX_HPP
|