iunordered_set_index.hpp 12 KB

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