concurrent_node_set.hpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. /* Fast open-addressing, node-based concurrent hashset.
  2. *
  3. * Copyright 2023 Christian Mazakas.
  4. * Copyright 2023-2024 Joaquin M Lopez Munoz.
  5. * Distributed under the Boost Software License, Version 1.0.
  6. * (See accompanying file LICENSE_1_0.txt or copy at
  7. * http://www.boost.org/LICENSE_1_0.txt)
  8. *
  9. * See https://www.boost.org/libs/unordered for library home page.
  10. */
  11. #ifndef BOOST_UNORDERED_CONCURRENT_NODE_SET_HPP
  12. #define BOOST_UNORDERED_CONCURRENT_NODE_SET_HPP
  13. #include <boost/unordered/concurrent_node_set_fwd.hpp>
  14. #include <boost/unordered/detail/concurrent_static_asserts.hpp>
  15. #include <boost/unordered/detail/foa/concurrent_table.hpp>
  16. #include <boost/unordered/detail/foa/element_type.hpp>
  17. #include <boost/unordered/detail/foa/node_set_handle.hpp>
  18. #include <boost/unordered/detail/foa/node_set_types.hpp>
  19. #include <boost/unordered/detail/type_traits.hpp>
  20. #include <boost/unordered/unordered_node_set_fwd.hpp>
  21. #include <boost/container_hash/hash.hpp>
  22. #include <boost/core/allocator_access.hpp>
  23. #include <boost/core/serialization.hpp>
  24. #include <utility>
  25. namespace boost {
  26. namespace unordered {
  27. template <class Key, class Hash, class Pred, class Allocator>
  28. class concurrent_node_set
  29. {
  30. private:
  31. template <class Key2, class Hash2, class Pred2, class Allocator2>
  32. friend class concurrent_node_set;
  33. template <class Key2, class Hash2, class Pred2, class Allocator2>
  34. friend class unordered_node_set;
  35. using type_policy = detail::foa::node_set_types<Key,
  36. typename boost::allocator_void_pointer<Allocator>::type>;
  37. using table_type =
  38. detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator>;
  39. table_type table_;
  40. template <class K, class H, class KE, class A>
  41. bool friend operator==(concurrent_node_set<K, H, KE, A> const& lhs,
  42. concurrent_node_set<K, H, KE, A> const& rhs);
  43. template <class K, class H, class KE, class A, class Predicate>
  44. friend typename concurrent_node_set<K, H, KE, A>::size_type erase_if(
  45. concurrent_node_set<K, H, KE, A>& set, Predicate pred);
  46. template<class Archive, class K, class H, class KE, class A>
  47. friend void serialize(
  48. Archive& ar, concurrent_node_set<K, H, KE, A>& c,
  49. unsigned int version);
  50. public:
  51. using key_type = Key;
  52. using value_type = typename type_policy::value_type;
  53. using init_type = typename type_policy::init_type;
  54. using size_type = std::size_t;
  55. using difference_type = std::ptrdiff_t;
  56. using hasher = typename boost::unordered::detail::type_identity<Hash>::type;
  57. using key_equal = typename boost::unordered::detail::type_identity<Pred>::type;
  58. using allocator_type = typename boost::unordered::detail::type_identity<Allocator>::type;
  59. using reference = value_type&;
  60. using const_reference = value_type const&;
  61. using pointer = typename boost::allocator_pointer<allocator_type>::type;
  62. using const_pointer =
  63. typename boost::allocator_const_pointer<allocator_type>::type;
  64. using node_type = detail::foa::node_set_handle<type_policy,
  65. typename boost::allocator_rebind<Allocator,
  66. typename type_policy::value_type>::type>;
  67. using insert_return_type =
  68. detail::foa::iteratorless_insert_return_type<node_type>;
  69. static constexpr size_type bulk_visit_size = table_type::bulk_visit_size;
  70. #if defined(BOOST_UNORDERED_ENABLE_STATS)
  71. using stats = typename table_type::stats;
  72. #endif
  73. concurrent_node_set()
  74. : concurrent_node_set(detail::foa::default_bucket_count)
  75. {
  76. }
  77. explicit concurrent_node_set(size_type n, const hasher& hf = hasher(),
  78. const key_equal& eql = key_equal(),
  79. const allocator_type& a = allocator_type())
  80. : table_(n, hf, eql, a)
  81. {
  82. }
  83. template <class InputIterator>
  84. concurrent_node_set(InputIterator f, InputIterator l,
  85. size_type n = detail::foa::default_bucket_count,
  86. const hasher& hf = hasher(), const key_equal& eql = key_equal(),
  87. const allocator_type& a = allocator_type())
  88. : table_(n, hf, eql, a)
  89. {
  90. this->insert(f, l);
  91. }
  92. concurrent_node_set(concurrent_node_set const& rhs)
  93. : table_(rhs.table_,
  94. boost::allocator_select_on_container_copy_construction(
  95. rhs.get_allocator()))
  96. {
  97. }
  98. concurrent_node_set(concurrent_node_set&& rhs)
  99. : table_(std::move(rhs.table_))
  100. {
  101. }
  102. template <class InputIterator>
  103. concurrent_node_set(
  104. InputIterator f, InputIterator l, allocator_type const& a)
  105. : concurrent_node_set(f, l, 0, hasher(), key_equal(), a)
  106. {
  107. }
  108. explicit concurrent_node_set(allocator_type const& a)
  109. : table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
  110. {
  111. }
  112. concurrent_node_set(
  113. concurrent_node_set const& rhs, allocator_type const& a)
  114. : table_(rhs.table_, a)
  115. {
  116. }
  117. concurrent_node_set(concurrent_node_set&& rhs, allocator_type const& a)
  118. : table_(std::move(rhs.table_), a)
  119. {
  120. }
  121. concurrent_node_set(std::initializer_list<value_type> il,
  122. size_type n = detail::foa::default_bucket_count,
  123. const hasher& hf = hasher(), const key_equal& eql = key_equal(),
  124. const allocator_type& a = allocator_type())
  125. : concurrent_node_set(n, hf, eql, a)
  126. {
  127. this->insert(il.begin(), il.end());
  128. }
  129. concurrent_node_set(size_type n, const allocator_type& a)
  130. : concurrent_node_set(n, hasher(), key_equal(), a)
  131. {
  132. }
  133. concurrent_node_set(
  134. size_type n, const hasher& hf, const allocator_type& a)
  135. : concurrent_node_set(n, hf, key_equal(), a)
  136. {
  137. }
  138. template <typename InputIterator>
  139. concurrent_node_set(
  140. InputIterator f, InputIterator l, size_type n, const allocator_type& a)
  141. : concurrent_node_set(f, l, n, hasher(), key_equal(), a)
  142. {
  143. }
  144. template <typename InputIterator>
  145. concurrent_node_set(InputIterator f, InputIterator l, size_type n,
  146. const hasher& hf, const allocator_type& a)
  147. : concurrent_node_set(f, l, n, hf, key_equal(), a)
  148. {
  149. }
  150. concurrent_node_set(
  151. std::initializer_list<value_type> il, const allocator_type& a)
  152. : concurrent_node_set(
  153. il, detail::foa::default_bucket_count, hasher(), key_equal(), a)
  154. {
  155. }
  156. concurrent_node_set(std::initializer_list<value_type> il, size_type n,
  157. const allocator_type& a)
  158. : concurrent_node_set(il, n, hasher(), key_equal(), a)
  159. {
  160. }
  161. concurrent_node_set(std::initializer_list<value_type> il, size_type n,
  162. const hasher& hf, const allocator_type& a)
  163. : concurrent_node_set(il, n, hf, key_equal(), a)
  164. {
  165. }
  166. template <bool avoid_explicit_instantiation = true>
  167. concurrent_node_set(
  168. unordered_node_set<Key, Hash, Pred, Allocator>&& other)
  169. : table_(std::move(other.table_))
  170. {
  171. }
  172. ~concurrent_node_set() = default;
  173. concurrent_node_set& operator=(concurrent_node_set const& rhs)
  174. {
  175. table_ = rhs.table_;
  176. return *this;
  177. }
  178. concurrent_node_set& operator=(concurrent_node_set&& rhs)
  179. noexcept(boost::allocator_is_always_equal<Allocator>::type::value ||
  180. boost::allocator_propagate_on_container_move_assignment<
  181. Allocator>::type::value)
  182. {
  183. table_ = std::move(rhs.table_);
  184. return *this;
  185. }
  186. concurrent_node_set& operator=(std::initializer_list<value_type> ilist)
  187. {
  188. table_ = ilist;
  189. return *this;
  190. }
  191. /// Capacity
  192. ///
  193. size_type size() const noexcept { return table_.size(); }
  194. size_type max_size() const noexcept { return table_.max_size(); }
  195. BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
  196. {
  197. return size() == 0;
  198. }
  199. template <class F>
  200. BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
  201. {
  202. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  203. return table_.visit(k, f);
  204. }
  205. template <class F>
  206. BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
  207. {
  208. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  209. return table_.visit(k, f);
  210. }
  211. template <class F>
  212. BOOST_FORCEINLINE size_type cvisit(key_type const& k, F f) const
  213. {
  214. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  215. return table_.visit(k, f);
  216. }
  217. template <class K, class F>
  218. BOOST_FORCEINLINE typename std::enable_if<
  219. detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
  220. visit(K&& k, F f)
  221. {
  222. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  223. return table_.visit(std::forward<K>(k), f);
  224. }
  225. template <class K, class F>
  226. BOOST_FORCEINLINE typename std::enable_if<
  227. detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
  228. visit(K&& k, F f) const
  229. {
  230. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  231. return table_.visit(std::forward<K>(k), f);
  232. }
  233. template <class K, class F>
  234. BOOST_FORCEINLINE typename std::enable_if<
  235. detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
  236. cvisit(K&& k, F f) const
  237. {
  238. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  239. return table_.visit(std::forward<K>(k), f);
  240. }
  241. template<class FwdIterator, class F>
  242. BOOST_FORCEINLINE
  243. size_t visit(FwdIterator first, FwdIterator last, F f)
  244. {
  245. BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
  246. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  247. return table_.visit(first, last, f);
  248. }
  249. template<class FwdIterator, class F>
  250. BOOST_FORCEINLINE
  251. size_t visit(FwdIterator first, FwdIterator last, F f) const
  252. {
  253. BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
  254. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  255. return table_.visit(first, last, f);
  256. }
  257. template<class FwdIterator, class F>
  258. BOOST_FORCEINLINE
  259. size_t cvisit(FwdIterator first, FwdIterator last, F f) const
  260. {
  261. BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
  262. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  263. return table_.visit(first, last, f);
  264. }
  265. template <class F> size_type visit_all(F f)
  266. {
  267. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  268. return table_.visit_all(f);
  269. }
  270. template <class F> size_type visit_all(F f) const
  271. {
  272. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  273. return table_.visit_all(f);
  274. }
  275. template <class F> size_type cvisit_all(F f) const
  276. {
  277. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  278. return table_.cvisit_all(f);
  279. }
  280. #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
  281. template <class ExecPolicy, class F>
  282. typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
  283. void>::type
  284. visit_all(ExecPolicy&& p, F f)
  285. {
  286. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  287. BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
  288. table_.visit_all(p, f);
  289. }
  290. template <class ExecPolicy, class F>
  291. typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
  292. void>::type
  293. visit_all(ExecPolicy&& p, F f) const
  294. {
  295. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  296. BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
  297. table_.visit_all(p, f);
  298. }
  299. template <class ExecPolicy, class F>
  300. typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
  301. void>::type
  302. cvisit_all(ExecPolicy&& p, F f) const
  303. {
  304. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  305. BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
  306. table_.cvisit_all(p, f);
  307. }
  308. #endif
  309. template <class F> bool visit_while(F f)
  310. {
  311. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  312. return table_.visit_while(f);
  313. }
  314. template <class F> bool visit_while(F f) const
  315. {
  316. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  317. return table_.visit_while(f);
  318. }
  319. template <class F> bool cvisit_while(F f) const
  320. {
  321. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  322. return table_.cvisit_while(f);
  323. }
  324. #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
  325. template <class ExecPolicy, class F>
  326. typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
  327. bool>::type
  328. visit_while(ExecPolicy&& p, F f)
  329. {
  330. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  331. BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
  332. return table_.visit_while(p, f);
  333. }
  334. template <class ExecPolicy, class F>
  335. typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
  336. bool>::type
  337. visit_while(ExecPolicy&& p, F f) const
  338. {
  339. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  340. BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
  341. return table_.visit_while(p, f);
  342. }
  343. template <class ExecPolicy, class F>
  344. typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
  345. bool>::type
  346. cvisit_while(ExecPolicy&& p, F f) const
  347. {
  348. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  349. BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
  350. return table_.cvisit_while(p, f);
  351. }
  352. #endif
  353. /// Modifiers
  354. ///
  355. BOOST_FORCEINLINE bool insert(value_type const& obj)
  356. {
  357. return table_.insert(obj);
  358. }
  359. BOOST_FORCEINLINE bool insert(value_type&& obj)
  360. {
  361. return table_.insert(std::move(obj));
  362. }
  363. template <class K>
  364. BOOST_FORCEINLINE typename std::enable_if<
  365. detail::are_transparent<K, hasher, key_equal>::value,
  366. bool >::type
  367. insert(K&& k)
  368. {
  369. return table_.try_emplace(std::forward<K>(k));
  370. }
  371. template <class InputIterator>
  372. size_type insert(InputIterator begin, InputIterator end)
  373. {
  374. size_type count_elements = 0;
  375. for (auto pos = begin; pos != end; ++pos, ++count_elements) {
  376. table_.emplace(*pos);
  377. }
  378. return count_elements;
  379. }
  380. size_type insert(std::initializer_list<value_type> ilist)
  381. {
  382. return this->insert(ilist.begin(), ilist.end());
  383. }
  384. insert_return_type insert(node_type&& nh)
  385. {
  386. using access = detail::foa::node_handle_access;
  387. if (nh.empty()) {
  388. return {false, node_type{}};
  389. }
  390. // Caveat: get_allocator() incurs synchronization (not cheap)
  391. BOOST_ASSERT(get_allocator() == nh.get_allocator());
  392. if (table_.insert(std::move(access::element(nh)))) {
  393. access::reset(nh);
  394. return {true, node_type{}};
  395. } else {
  396. return {false, std::move(nh)};
  397. }
  398. }
  399. template <class F>
  400. BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
  401. {
  402. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  403. return table_.insert_or_visit(obj, f);
  404. }
  405. template <class F>
  406. BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
  407. {
  408. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  409. return table_.insert_or_visit(std::move(obj), f);
  410. }
  411. template <class K, class F>
  412. BOOST_FORCEINLINE typename std::enable_if<
  413. detail::are_transparent<K, hasher, key_equal>::value,
  414. bool >::type
  415. insert_or_visit(K&& k, F f)
  416. {
  417. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  418. return table_.try_emplace_or_visit(std::forward<K>(k), f);
  419. }
  420. template <class InputIterator, class F>
  421. size_type insert_or_visit(InputIterator first, InputIterator last, F f)
  422. {
  423. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  424. size_type count_elements = 0;
  425. for (; first != last; ++first, ++count_elements) {
  426. table_.emplace_or_visit(*first, f);
  427. }
  428. return count_elements;
  429. }
  430. template <class F>
  431. size_type insert_or_visit(std::initializer_list<value_type> ilist, F f)
  432. {
  433. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  434. return this->insert_or_visit(ilist.begin(), ilist.end(), std::ref(f));
  435. }
  436. template <class F>
  437. insert_return_type insert_or_visit(node_type&& nh, F f)
  438. {
  439. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  440. using access = detail::foa::node_handle_access;
  441. if (nh.empty()) {
  442. return {false, node_type{}};
  443. }
  444. // Caveat: get_allocator() incurs synchronization (not cheap)
  445. BOOST_ASSERT(get_allocator() == nh.get_allocator());
  446. if (table_.insert_or_visit(std::move(access::element(nh)), f)) {
  447. access::reset(nh);
  448. return {true, node_type{}};
  449. } else {
  450. return {false, std::move(nh)};
  451. }
  452. }
  453. template <class F>
  454. BOOST_FORCEINLINE bool insert_or_cvisit(value_type const& obj, F f)
  455. {
  456. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  457. return table_.insert_or_cvisit(obj, f);
  458. }
  459. template <class F>
  460. BOOST_FORCEINLINE bool insert_or_cvisit(value_type&& obj, F f)
  461. {
  462. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  463. return table_.insert_or_cvisit(std::move(obj), f);
  464. }
  465. template <class K, class F>
  466. BOOST_FORCEINLINE typename std::enable_if<
  467. detail::are_transparent<K, hasher, key_equal>::value,
  468. bool >::type
  469. insert_or_cvisit(K&& k, F f)
  470. {
  471. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  472. return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
  473. }
  474. template <class InputIterator, class F>
  475. size_type insert_or_cvisit(InputIterator first, InputIterator last, F f)
  476. {
  477. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  478. size_type count_elements = 0;
  479. for (; first != last; ++first, ++count_elements) {
  480. table_.emplace_or_cvisit(*first, f);
  481. }
  482. return count_elements;
  483. }
  484. template <class F>
  485. size_type insert_or_cvisit(std::initializer_list<value_type> ilist, F f)
  486. {
  487. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  488. return this->insert_or_cvisit(ilist.begin(), ilist.end(), std::ref(f));
  489. }
  490. template <class F>
  491. insert_return_type insert_or_cvisit(node_type&& nh, F f)
  492. {
  493. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
  494. using access = detail::foa::node_handle_access;
  495. if (nh.empty()) {
  496. return {false, node_type{}};
  497. }
  498. // Caveat: get_allocator() incurs synchronization (not cheap)
  499. BOOST_ASSERT(get_allocator() == nh.get_allocator());
  500. if (table_.insert_or_cvisit(std::move(access::element(nh)), f)) {
  501. access::reset(nh);
  502. return {true, node_type{}};
  503. } else {
  504. return {false, std::move(nh)};
  505. }
  506. }
  507. template <class F1, class F2>
  508. BOOST_FORCEINLINE bool insert_and_visit(
  509. value_type const& obj, F1 f1, F2 f2)
  510. {
  511. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  512. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  513. return table_.insert_and_visit(obj, f1, f2);
  514. }
  515. template <class F1, class F2>
  516. BOOST_FORCEINLINE bool insert_and_visit(value_type&& obj, F1 f1, F2 f2)
  517. {
  518. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  519. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  520. return table_.insert_and_visit(std::move(obj), f1, f2);
  521. }
  522. template <class K, class F1, class F2>
  523. BOOST_FORCEINLINE typename std::enable_if<
  524. detail::are_transparent<K, hasher, key_equal>::value,
  525. bool >::type
  526. insert_and_visit(K&& k, F1 f1, F2 f2)
  527. {
  528. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  529. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  530. return table_.try_emplace_and_visit(std::forward<K>(k), f1, f2);
  531. }
  532. template <class InputIterator, class F1, class F2>
  533. size_type insert_and_visit(
  534. InputIterator first, InputIterator last, F1 f1, F2 f2)
  535. {
  536. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  537. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  538. size_type count_elements = 0;
  539. for (; first != last; ++first, ++count_elements) {
  540. table_.emplace_and_visit(*first, f1, f2);
  541. }
  542. return count_elements;
  543. }
  544. template <class F1, class F2>
  545. size_type insert_and_visit(
  546. std::initializer_list<value_type> ilist, F1 f1, F2 f2)
  547. {
  548. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  549. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  550. return this->insert_and_visit(
  551. ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
  552. }
  553. template <class F1, class F2>
  554. insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2)
  555. {
  556. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  557. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  558. using access = detail::foa::node_handle_access;
  559. if (nh.empty()) {
  560. return {false, node_type{}};
  561. }
  562. // Caveat: get_allocator() incurs synchronization (not cheap)
  563. BOOST_ASSERT(get_allocator() == nh.get_allocator());
  564. if (table_.insert_and_visit(std::move(access::element(nh)), f1, f2)) {
  565. access::reset(nh);
  566. return {true, node_type{}};
  567. } else {
  568. return {false, std::move(nh)};
  569. }
  570. }
  571. template <class F1, class F2>
  572. BOOST_FORCEINLINE bool insert_and_cvisit(
  573. value_type const& obj, F1 f1, F2 f2)
  574. {
  575. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  576. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  577. return table_.insert_and_cvisit(obj, f1, f2);
  578. }
  579. template <class F1, class F2>
  580. BOOST_FORCEINLINE bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2)
  581. {
  582. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  583. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  584. return table_.insert_and_cvisit(std::move(obj), f1, f2);
  585. }
  586. template <class K, class F1, class F2>
  587. BOOST_FORCEINLINE typename std::enable_if<
  588. detail::are_transparent<K, hasher, key_equal>::value,
  589. bool >::type
  590. insert_and_cvisit(K&& k, F1 f1, F2 f2)
  591. {
  592. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  593. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  594. return table_.try_emplace_and_cvisit(std::forward<K>(k), f1, f2);
  595. }
  596. template <class InputIterator, class F1, class F2>
  597. size_type insert_and_cvisit(
  598. InputIterator first, InputIterator last, F1 f1, F2 f2)
  599. {
  600. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  601. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  602. size_type count_elements = 0;
  603. for (; first != last; ++first, ++count_elements) {
  604. table_.emplace_and_cvisit(*first, f1, f2);
  605. }
  606. return count_elements;
  607. }
  608. template <class F1, class F2>
  609. size_type insert_and_cvisit(
  610. std::initializer_list<value_type> ilist, F1 f1, F2 f2)
  611. {
  612. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  613. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  614. return this->insert_and_cvisit(
  615. ilist.begin(), ilist.end(), std::ref(f1), std::ref(f2));
  616. }
  617. template <class F1, class F2>
  618. insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2)
  619. {
  620. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
  621. BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
  622. using access = detail::foa::node_handle_access;
  623. if (nh.empty()) {
  624. return {false, node_type{}};
  625. }
  626. // Caveat: get_allocator() incurs synchronization (not cheap)
  627. BOOST_ASSERT(get_allocator() == nh.get_allocator());
  628. if (table_.insert_and_cvisit(std::move(access::element(nh)), f1, f2)) {
  629. access::reset(nh);
  630. return {true, node_type{}};
  631. } else {
  632. return {false, std::move(nh)};
  633. }
  634. }
  635. template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
  636. {
  637. return table_.emplace(std::forward<Args>(args)...);
  638. }
  639. template <class Arg, class... Args>
  640. BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
  641. {
  642. BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
  643. return table_.emplace_or_visit(
  644. std::forward<Arg>(arg), std::forward<Args>(args)...);
  645. }
  646. template <class Arg, class... Args>
  647. BOOST_FORCEINLINE bool emplace_or_cvisit(Arg&& arg, Args&&... args)
  648. {
  649. BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
  650. return table_.emplace_or_cvisit(
  651. std::forward<Arg>(arg), std::forward<Args>(args)...);
  652. }
  653. template <class Arg1, class Arg2, class... Args>
  654. BOOST_FORCEINLINE bool emplace_and_visit(
  655. Arg1&& arg1, Arg2&& arg2, Args&&... args)
  656. {
  657. BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
  658. Arg1, Arg2, Args...)
  659. BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
  660. return table_.emplace_and_visit(
  661. std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
  662. std::forward<Args>(args)...);
  663. }
  664. template <class Arg1, class Arg2, class... Args>
  665. BOOST_FORCEINLINE bool emplace_and_cvisit(
  666. Arg1&& arg1, Arg2&& arg2, Args&&... args)
  667. {
  668. BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
  669. Arg1, Arg2, Args...)
  670. BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
  671. return table_.emplace_and_cvisit(
  672. std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
  673. std::forward<Args>(args)...);
  674. }
  675. BOOST_FORCEINLINE size_type erase(key_type const& k)
  676. {
  677. return table_.erase(k);
  678. }
  679. template <class K>
  680. BOOST_FORCEINLINE typename std::enable_if<
  681. detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
  682. erase(K&& k)
  683. {
  684. return table_.erase(std::forward<K>(k));
  685. }
  686. template <class F>
  687. BOOST_FORCEINLINE size_type erase_if(key_type const& k, F f)
  688. {
  689. return table_.erase_if(k, f);
  690. }
  691. template <class K, class F>
  692. BOOST_FORCEINLINE typename std::enable_if<
  693. detail::are_transparent<K, hasher, key_equal>::value &&
  694. !detail::is_execution_policy<K>::value,
  695. size_type>::type
  696. erase_if(K&& k, F f)
  697. {
  698. return table_.erase_if(std::forward<K>(k), f);
  699. }
  700. #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
  701. template <class ExecPolicy, class F>
  702. typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
  703. void>::type
  704. erase_if(ExecPolicy&& p, F f)
  705. {
  706. BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
  707. table_.erase_if(p, f);
  708. }
  709. #endif
  710. template <class F> size_type erase_if(F f) { return table_.erase_if(f); }
  711. void swap(concurrent_node_set& other) noexcept(
  712. boost::allocator_is_always_equal<Allocator>::type::value ||
  713. boost::allocator_propagate_on_container_swap<Allocator>::type::value)
  714. {
  715. return table_.swap(other.table_);
  716. }
  717. node_type extract(key_type const& key)
  718. {
  719. node_type nh;
  720. table_.extract(key, detail::foa::node_handle_emplacer(nh));
  721. return nh;
  722. }
  723. template <class K>
  724. typename std::enable_if<
  725. detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
  726. extract(K const& key)
  727. {
  728. node_type nh;
  729. table_.extract(key, detail::foa::node_handle_emplacer(nh));
  730. return nh;
  731. }
  732. template <class F>
  733. node_type extract_if(key_type const& key, F f)
  734. {
  735. node_type nh;
  736. table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
  737. return nh;
  738. }
  739. template <class K, class F>
  740. typename std::enable_if<
  741. detail::are_transparent<K, hasher, key_equal>::value, node_type>::type
  742. extract_if(K const& key, F f)
  743. {
  744. node_type nh;
  745. table_.extract_if(key, f, detail::foa::node_handle_emplacer(nh));
  746. return nh;
  747. }
  748. void clear() noexcept { table_.clear(); }
  749. template <typename H2, typename P2>
  750. size_type merge(concurrent_node_set<Key, H2, P2, Allocator>& x)
  751. {
  752. BOOST_ASSERT(get_allocator() == x.get_allocator());
  753. return table_.merge(x.table_);
  754. }
  755. template <typename H2, typename P2>
  756. size_type merge(concurrent_node_set<Key, H2, P2, Allocator>&& x)
  757. {
  758. return merge(x);
  759. }
  760. BOOST_FORCEINLINE size_type count(key_type const& k) const
  761. {
  762. return table_.count(k);
  763. }
  764. template <class K>
  765. BOOST_FORCEINLINE typename std::enable_if<
  766. detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
  767. count(K const& k)
  768. {
  769. return table_.count(k);
  770. }
  771. BOOST_FORCEINLINE bool contains(key_type const& k) const
  772. {
  773. return table_.contains(k);
  774. }
  775. template <class K>
  776. BOOST_FORCEINLINE typename std::enable_if<
  777. detail::are_transparent<K, hasher, key_equal>::value, bool>::type
  778. contains(K const& k) const
  779. {
  780. return table_.contains(k);
  781. }
  782. /// Hash Policy
  783. ///
  784. size_type bucket_count() const noexcept { return table_.capacity(); }
  785. float load_factor() const noexcept { return table_.load_factor(); }
  786. float max_load_factor() const noexcept
  787. {
  788. return table_.max_load_factor();
  789. }
  790. void max_load_factor(float) {}
  791. size_type max_load() const noexcept { return table_.max_load(); }
  792. void rehash(size_type n) { table_.rehash(n); }
  793. void reserve(size_type n) { table_.reserve(n); }
  794. #if defined(BOOST_UNORDERED_ENABLE_STATS)
  795. /// Stats
  796. ///
  797. stats get_stats() const { return table_.get_stats(); }
  798. void reset_stats() noexcept { table_.reset_stats(); }
  799. #endif
  800. /// Observers
  801. ///
  802. allocator_type get_allocator() const noexcept
  803. {
  804. return table_.get_allocator();
  805. }
  806. hasher hash_function() const { return table_.hash_function(); }
  807. key_equal key_eq() const { return table_.key_eq(); }
  808. };
  809. template <class Key, class Hash, class KeyEqual, class Allocator>
  810. bool operator==(
  811. concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
  812. concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
  813. {
  814. return lhs.table_ == rhs.table_;
  815. }
  816. template <class Key, class Hash, class KeyEqual, class Allocator>
  817. bool operator!=(
  818. concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& lhs,
  819. concurrent_node_set<Key, Hash, KeyEqual, Allocator> const& rhs)
  820. {
  821. return !(lhs == rhs);
  822. }
  823. template <class Key, class Hash, class Pred, class Alloc>
  824. void swap(concurrent_node_set<Key, Hash, Pred, Alloc>& x,
  825. concurrent_node_set<Key, Hash, Pred, Alloc>& y)
  826. noexcept(noexcept(x.swap(y)))
  827. {
  828. x.swap(y);
  829. }
  830. template <class K, class H, class P, class A, class Predicate>
  831. typename concurrent_node_set<K, H, P, A>::size_type erase_if(
  832. concurrent_node_set<K, H, P, A>& c, Predicate pred)
  833. {
  834. return c.table_.erase_if(pred);
  835. }
  836. template<class Archive, class K, class H, class KE, class A>
  837. void serialize(
  838. Archive& ar, concurrent_node_set<K, H, KE, A>& c, unsigned int)
  839. {
  840. ar & core::make_nvp("table",c.table_);
  841. }
  842. #if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
  843. template <class InputIterator,
  844. class Hash =
  845. boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
  846. class Pred =
  847. std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
  848. class Allocator = std::allocator<
  849. typename std::iterator_traits<InputIterator>::value_type>,
  850. class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
  851. class = std::enable_if_t<detail::is_hash_v<Hash> >,
  852. class = std::enable_if_t<detail::is_pred_v<Pred> >,
  853. class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
  854. concurrent_node_set(InputIterator, InputIterator,
  855. std::size_t = boost::unordered::detail::foa::default_bucket_count,
  856. Hash = Hash(), Pred = Pred(), Allocator = Allocator())
  857. -> concurrent_node_set<
  858. typename std::iterator_traits<InputIterator>::value_type, Hash, Pred,
  859. Allocator>;
  860. template <class T, class Hash = boost::hash<T>,
  861. class Pred = std::equal_to<T>, class Allocator = std::allocator<T>,
  862. class = std::enable_if_t<detail::is_hash_v<Hash> >,
  863. class = std::enable_if_t<detail::is_pred_v<Pred> >,
  864. class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
  865. concurrent_node_set(std::initializer_list<T>,
  866. std::size_t = boost::unordered::detail::foa::default_bucket_count,
  867. Hash = Hash(), Pred = Pred(), Allocator = Allocator())
  868. -> concurrent_node_set< T, Hash, Pred, Allocator>;
  869. template <class InputIterator, class Allocator,
  870. class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
  871. class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
  872. concurrent_node_set(InputIterator, InputIterator, std::size_t, Allocator)
  873. -> concurrent_node_set<
  874. typename std::iterator_traits<InputIterator>::value_type,
  875. boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
  876. std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
  877. Allocator>;
  878. template <class InputIterator, class Allocator,
  879. class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
  880. class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
  881. concurrent_node_set(InputIterator, InputIterator, Allocator)
  882. -> concurrent_node_set<
  883. typename std::iterator_traits<InputIterator>::value_type,
  884. boost::hash<typename std::iterator_traits<InputIterator>::value_type>,
  885. std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
  886. Allocator>;
  887. template <class InputIterator, class Hash, class Allocator,
  888. class = std::enable_if_t<detail::is_hash_v<Hash> >,
  889. class = std::enable_if_t<detail::is_input_iterator_v<InputIterator> >,
  890. class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
  891. concurrent_node_set(
  892. InputIterator, InputIterator, std::size_t, Hash, Allocator)
  893. -> concurrent_node_set<
  894. typename std::iterator_traits<InputIterator>::value_type, Hash,
  895. std::equal_to<typename std::iterator_traits<InputIterator>::value_type>,
  896. Allocator>;
  897. template <class T, class Allocator,
  898. class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
  899. concurrent_node_set(std::initializer_list<T>, std::size_t, Allocator)
  900. -> concurrent_node_set<T, boost::hash<T>,std::equal_to<T>, Allocator>;
  901. template <class T, class Allocator,
  902. class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
  903. concurrent_node_set(std::initializer_list<T >, Allocator)
  904. -> concurrent_node_set<T, boost::hash<T>, std::equal_to<T>, Allocator>;
  905. template <class T, class Hash, class Allocator,
  906. class = std::enable_if_t<detail::is_hash_v<Hash> >,
  907. class = std::enable_if_t<detail::is_allocator_v<Allocator> > >
  908. concurrent_node_set(std::initializer_list<T >, std::size_t,Hash, Allocator)
  909. -> concurrent_node_set<T, Hash, std::equal_to<T>, Allocator>;
  910. #endif
  911. } // namespace unordered
  912. } // namespace boost
  913. #endif // BOOST_UNORDERED_CONCURRENT_NODE_SET_HPP