Intel(R) Threading Building Blocks Doxygen Documentation  version 4.2.3
_concurrent_unordered_impl.h
Go to the documentation of this file.
1 /*
2  Copyright (c) 2005-2019 Intel Corporation
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 
16 
17 
18 
19 */
20 
21 /* Container implementations in this header are based on PPL implementations
22  provided by Microsoft. */
23 
24 #ifndef __TBB__concurrent_unordered_impl_H
25 #define __TBB__concurrent_unordered_impl_H
26 #if !defined(__TBB_concurrent_unordered_map_H) && !defined(__TBB_concurrent_unordered_set_H) && !defined(__TBB_concurrent_hash_map_H)
27 #error Do not #include this internal file directly; use public TBB headers instead.
28 #endif
29 
30 #include "../tbb_stddef.h"
31 
32 #include <iterator>
33 #include <utility> // Need std::pair
34 #include <functional> // Need std::equal_to (in ../concurrent_unordered_*.h)
35 #include <string> // For tbb_hasher
36 #include <cstring> // Need std::memset
37 #include __TBB_STD_SWAP_HEADER
38 
39 #include "../atomic.h"
40 #include "../tbb_exception.h"
41 #include "../tbb_allocator.h"
42 
43 #if __TBB_INITIALIZER_LISTS_PRESENT
44  #include <initializer_list>
45 #endif
46 
47 #include "_allocator_traits.h"
48 #include "_tbb_hash_compare_impl.h"
49 
50 namespace tbb {
51 namespace interface5 {
53 namespace internal {
54 
55 template <typename T, typename Allocator>
57 template <typename Traits>
59 
60 // Forward list iterators (without skipping dummy elements)
61 template<class Solist, typename Value>
62 class flist_iterator : public std::iterator<std::forward_iterator_tag, Value>
63 {
64  template <typename T, typename Allocator>
65  friend class split_ordered_list;
66  template <typename Traits>
68  template<class M, typename V>
69  friend class flist_iterator;
70 
71  typedef typename Solist::nodeptr_t nodeptr_t;
72 public:
73  typedef typename Solist::value_type value_type;
74  typedef typename Solist::difference_type difference_type;
75  typedef typename Solist::pointer pointer;
76  typedef typename Solist::reference reference;
77 
80  : my_node_ptr(other.my_node_ptr) {}
81 
82  reference operator*() const { return my_node_ptr->my_element; }
83  pointer operator->() const { return &**this; }
84 
86  my_node_ptr = my_node_ptr->my_next;
87  return *this;
88  }
89 
91  flist_iterator tmp = *this;
92  ++*this;
93  return tmp;
94  }
95 
96 protected:
97  flist_iterator(nodeptr_t pnode) : my_node_ptr(pnode) {}
98  nodeptr_t get_node_ptr() const { return my_node_ptr; }
99 
100  nodeptr_t my_node_ptr;
101 
102  template<typename M, typename T, typename U>
103  friend bool operator==( const flist_iterator<M,T> &i, const flist_iterator<M,U> &j );
104  template<typename M, typename T, typename U>
105  friend bool operator!=( const flist_iterator<M,T>& i, const flist_iterator<M,U>& j );
106 };
107 
108 template<typename Solist, typename T, typename U>
110  return i.my_node_ptr == j.my_node_ptr;
111 }
112 template<typename Solist, typename T, typename U>
114  return i.my_node_ptr != j.my_node_ptr;
115 }
116 
117 // Split-order list iterators, needed to skip dummy elements
118 template<class Solist, typename Value>
119 class solist_iterator : public flist_iterator<Solist, Value>
120 {
122  typedef typename Solist::nodeptr_t nodeptr_t;
123  using base_type::get_node_ptr;
124  template <typename T, typename Allocator>
125  friend class split_ordered_list;
126  template<class M, typename V>
127  friend class solist_iterator;
128  template<typename M, typename T, typename U>
129  friend bool operator==( const solist_iterator<M,T> &i, const solist_iterator<M,U> &j );
130  template<typename M, typename T, typename U>
131  friend bool operator!=( const solist_iterator<M,T>& i, const solist_iterator<M,U>& j );
132 
133  const Solist *my_list_ptr;
134  solist_iterator(nodeptr_t pnode, const Solist *plist) : base_type(pnode), my_list_ptr(plist) {}
135 
136 public:
137  typedef typename Solist::value_type value_type;
138  typedef typename Solist::difference_type difference_type;
139  typedef typename Solist::pointer pointer;
140  typedef typename Solist::reference reference;
141 
144  : base_type(other), my_list_ptr(other.my_list_ptr) {}
145 
146  reference operator*() const {
147  return this->base_type::operator*();
148  }
149 
150  pointer operator->() const {
151  return (&**this);
152  }
153 
155  do ++(*(base_type *)this);
156  while (get_node_ptr() != NULL && get_node_ptr()->is_dummy());
157 
158  return (*this);
159  }
160 
162  solist_iterator tmp = *this;
163  do ++*this;
164  while (get_node_ptr() != NULL && get_node_ptr()->is_dummy());
165 
166  return (tmp);
167  }
168 };
169 
170 template<typename Solist, typename T, typename U>
172  return i.my_node_ptr == j.my_node_ptr && i.my_list_ptr == j.my_list_ptr;
173 }
174 template<typename Solist, typename T, typename U>
176  return i.my_node_ptr != j.my_node_ptr || i.my_list_ptr != j.my_list_ptr;
177 }
178 
179 // Forward type and class definitions
180 typedef size_t sokey_t;
181 
182 
183 // Forward list in which elements are sorted in a split-order
184 template <typename T, typename Allocator>
185 class split_ordered_list
186 {
187 public:
189 
191 
192  struct node;
193  typedef node *nodeptr_t;
194 
200  // No support for reference/const_reference in allocator traits
201  typedef value_type& reference;
202  typedef const value_type& const_reference;
203 
208 
209  // Node that holds the element in a split-ordered list
211  {
212  private:
213  // for compilers that try to generate default constructors though they are not needed.
214  node(); // VS 2008, 2010, 2012
215  public:
216  // Initialize the node with the given order key
217  void init(sokey_t order_key) {
218  my_order_key = order_key;
219  my_next = NULL;
220  }
221 
222  // Return the order key (needed for hashing)
223  sokey_t get_order_key() const { // TODO: remove
224  return my_order_key;
225  }
226 
227  // Inserts the new element in the list in an atomic fashion
228  nodeptr_t atomic_set_next(nodeptr_t new_node, nodeptr_t current_node)
229  {
230  // Try to change the next pointer on the current element to a new element, only if it still points to the cached next
231  nodeptr_t exchange_node = tbb::internal::as_atomic(my_next).compare_and_swap(new_node, current_node);
232 
233  if (exchange_node == current_node) // TODO: why this branch?
234  {
235  // Operation succeeded, return the new node
236  return new_node;
237  }
238  else
239  {
240  // Operation failed, return the "interfering" node
241  return exchange_node;
242  }
243  }
244 
245  // Checks if this element in the list is a dummy, order enforcing node. Dummy nodes are used by buckets
246  // in the hash table to quickly index into the right subsection of the split-ordered list.
247  bool is_dummy() const {
248  return (my_order_key & 0x1) == 0;
249  }
250 
251 
252  nodeptr_t my_next; // Next element in the list
253  value_type my_element; // Element storage
254  sokey_t my_order_key; // Order key for this element
255  };
256 
257  // Allocate a new node with the given order key; used to allocate dummy nodes
258  nodeptr_t create_node(sokey_t order_key) {
259  nodeptr_t pnode = my_node_allocator.allocate(1);
260  pnode->init(order_key);
261  return (pnode);
262  }
263 
264  // Allocate a new node with the given order key and value
265  template<typename Arg>
266  nodeptr_t create_node(sokey_t order_key, __TBB_FORWARDING_REF(Arg) t,
268  nodeptr_t pnode = my_node_allocator.allocate(1);
269 
270  //TODO: use RAII scoped guard instead of explicit catch
271  __TBB_TRY {
272  new(static_cast<void*>(&pnode->my_element)) T(tbb::internal::forward<Arg>(t));
273  pnode->init(order_key);
274  } __TBB_CATCH(...) {
275  my_node_allocator.deallocate(pnode, 1);
276  __TBB_RETHROW();
277  }
278 
279  return (pnode);
280  }
281 
282  // A helper to avoid excessive requiremens in internal_insert
283  template<typename Arg>
284  nodeptr_t create_node(sokey_t, __TBB_FORWARDING_REF(Arg),
285  /*AllowCreate=*/tbb::internal::false_type){
286  __TBB_ASSERT(false, "This compile-time helper should never get called");
287  return nodeptr_t();
288  }
289 
290  // Allocate a new node with the given parameters for constructing value
291  template<typename __TBB_PARAMETER_PACK Args>
293  nodeptr_t pnode = my_node_allocator.allocate(1);
294 
295  //TODO: use RAII scoped guard instead of explicit catch
296  __TBB_TRY {
297  new(static_cast<void*>(&pnode->my_element)) T(__TBB_PACK_EXPANSION(tbb::internal::forward<Args>(args)));
298  } __TBB_CATCH(...) {
299  my_node_allocator.deallocate(pnode, 1);
300  __TBB_RETHROW();
301  }
302 
303  return (pnode);
304  }
305 
306  split_ordered_list(allocator_type a = allocator_type())
307  : my_node_allocator(a), my_element_count(0)
308  {
309  // Immediately allocate a dummy node with order key of 0. This node
310  // will always be the head of the list.
311  my_head = create_node(sokey_t(0));
312  }
313 
315  {
316  // Clear the list
317  clear();
318 
319  // Remove the head element which is not cleared by clear()
320  nodeptr_t pnode = my_head;
321  my_head = NULL;
322 
323  __TBB_ASSERT(pnode != NULL && pnode->my_next == NULL, "Invalid head list node");
324 
325  destroy_node(pnode);
326  }
327 
328  // Common forward list functions
329 
330  allocator_type get_allocator() const {
331  return (my_node_allocator);
332  }
333 
334  void clear() {
335  nodeptr_t pnext;
336  nodeptr_t pnode = my_head;
337 
338  __TBB_ASSERT(my_head != NULL, "Invalid head list node");
339  pnext = pnode->my_next;
340  pnode->my_next = NULL;
341  pnode = pnext;
342 
343  while (pnode != NULL)
344  {
345  pnext = pnode->my_next;
346  destroy_node(pnode);
347  pnode = pnext;
348  }
349 
350  my_element_count = 0;
351  }
352 
353  // Returns a first non-dummy element in the SOL
354  iterator begin() {
355  return first_real_iterator(raw_begin());
356  }
357 
358  // Returns a first non-dummy element in the SOL
359  const_iterator begin() const {
360  return first_real_iterator(raw_begin());
361  }
362 
363  iterator end() {
364  return (iterator(0, this));
365  }
366 
367  const_iterator end() const {
368  return (const_iterator(0, this));
369  }
370 
371  const_iterator cbegin() const {
372  return (((const self_type *)this)->begin());
373  }
374 
375  const_iterator cend() const {
376  return (((const self_type *)this)->end());
377  }
378 
379  // Checks if the number of elements (non-dummy) is 0
380  bool empty() const {
381  return (my_element_count == 0);
382  }
383 
384  // Returns the number of non-dummy elements in the list
385  size_type size() const {
386  return my_element_count;
387  }
388 
389  // Returns the maximum size of the list, determined by the allocator
390  size_type max_size() const {
391  return my_node_allocator.max_size();
392  }
393 
394  // Swaps 'this' list with the passed in one
395  void swap(self_type& other)
396  {
397  if (this == &other)
398  {
399  // Nothing to do
400  return;
401  }
402 
403  std::swap(my_element_count, other.my_element_count);
404  std::swap(my_head, other.my_head);
405  }
406 
407  // Split-order list functions
408 
409  // Returns a first element in the SOL, which is always a dummy
410  raw_iterator raw_begin() {
411  return raw_iterator(my_head);
412  }
413 
414  // Returns a first element in the SOL, which is always a dummy
415  raw_const_iterator raw_begin() const {
416  return raw_const_iterator(my_head);
417  }
418 
419  raw_iterator raw_end() {
420  return raw_iterator(0);
421  }
422 
423  raw_const_iterator raw_end() const {
424  return raw_const_iterator(0);
425  }
426 
427  static sokey_t get_order_key(const raw_const_iterator& it) {
428  return it.get_node_ptr()->get_order_key();
429  }
430 
431  static sokey_t get_safe_order_key(const raw_const_iterator& it) {
432  if( !it.get_node_ptr() ) return ~sokey_t(0);
433  return it.get_node_ptr()->get_order_key();
434  }
435 
436  // Returns a public iterator version of the internal iterator. Public iterator must not
437  // be a dummy private iterator.
438  iterator get_iterator(raw_iterator it) {
439  __TBB_ASSERT(it.get_node_ptr() == NULL || !it.get_node_ptr()->is_dummy(), "Invalid user node (dummy)");
440  return iterator(it.get_node_ptr(), this);
441  }
442 
443  // Returns a public iterator version of the internal iterator. Public iterator must not
444  // be a dummy private iterator.
445  const_iterator get_iterator(raw_const_iterator it) const {
446  __TBB_ASSERT(it.get_node_ptr() == NULL || !it.get_node_ptr()->is_dummy(), "Invalid user node (dummy)");
447  return const_iterator(it.get_node_ptr(), this);
448  }
449 
450  // Returns a non-const version of the raw_iterator
451  raw_iterator get_iterator(raw_const_iterator it) {
452  return raw_iterator(it.get_node_ptr());
453  }
454 
455  // Returns a non-const version of the iterator
456  static iterator get_iterator(const_iterator it) {
457  return iterator(it.my_node_ptr, it.my_list_ptr);
458  }
459 
460  // Returns a public iterator version of a first non-dummy internal iterator at or after
461  // the passed in internal iterator.
462  iterator first_real_iterator(raw_iterator it)
463  {
464  // Skip all dummy, internal only iterators
465  while (it != raw_end() && it.get_node_ptr()->is_dummy())
466  ++it;
467 
468  return iterator(it.get_node_ptr(), this);
469  }
470 
471  // Returns a public iterator version of a first non-dummy internal iterator at or after
472  // the passed in internal iterator.
473  const_iterator first_real_iterator(raw_const_iterator it) const
474  {
475  // Skip all dummy, internal only iterators
476  while (it != raw_end() && it.get_node_ptr()->is_dummy())
477  ++it;
478 
479  return const_iterator(it.get_node_ptr(), this);
480  }
481 
482  // Erase an element using the allocator
483  void destroy_node(nodeptr_t pnode) {
484  if (!pnode->is_dummy()) my_node_allocator.destroy(pnode);
485  my_node_allocator.deallocate(pnode, 1);
486  }
487 
488  // Try to insert a new element in the list.
489  // If insert fails, return the node that was inserted instead.
490  static nodeptr_t try_insert_atomic(nodeptr_t previous, nodeptr_t new_node, nodeptr_t current_node) {
491  new_node->my_next = current_node;
492  return previous->atomic_set_next(new_node, current_node);
493  }
494 
495  // Insert a new element between passed in iterators
496  std::pair<iterator, bool> try_insert(raw_iterator it, raw_iterator next, nodeptr_t pnode, size_type *new_count)
497  {
498  nodeptr_t inserted_node = try_insert_atomic(it.get_node_ptr(), pnode, next.get_node_ptr());
499 
500  if (inserted_node == pnode)
501  {
502  // If the insert succeeded, check that the order is correct and increment the element count
503  check_range(it, next);
504  *new_count = tbb::internal::as_atomic(my_element_count).fetch_and_increment();
505  return std::pair<iterator, bool>(iterator(pnode, this), true);
506  }
507  else
508  {
509  return std::pair<iterator, bool>(end(), false);
510  }
511  }
512 
513  // Insert a new dummy element, starting search at a parent dummy element
514  raw_iterator insert_dummy(raw_iterator it, sokey_t order_key)
515  {
516  raw_iterator last = raw_end();
517  raw_iterator where = it;
518 
519  __TBB_ASSERT(where != last, "Invalid head node");
520 
521  ++where;
522 
523  // Create a dummy element up front, even though it may be discarded (due to concurrent insertion)
524  nodeptr_t dummy_node = create_node(order_key);
525 
526  for (;;)
527  {
528  __TBB_ASSERT(it != last, "Invalid head list node");
529 
530  // If the head iterator is at the end of the list, or past the point where this dummy
531  // node needs to be inserted, then try to insert it.
532  if (where == last || get_order_key(where) > order_key)
533  {
534  __TBB_ASSERT(get_order_key(it) < order_key, "Invalid node order in the list");
535 
536  // Try to insert it in the right place
537  nodeptr_t inserted_node = try_insert_atomic(it.get_node_ptr(), dummy_node, where.get_node_ptr());
538 
539  if (inserted_node == dummy_node)
540  {
541  // Insertion succeeded, check the list for order violations
542  check_range(it, where);
543  return raw_iterator(dummy_node);
544  }
545  else
546  {
547  // Insertion failed: either dummy node was inserted by another thread, or
548  // a real element was inserted at exactly the same place as dummy node.
549  // Proceed with the search from the previous location where order key was
550  // known to be larger (note: this is legal only because there is no safe
551  // concurrent erase operation supported).
552  where = it;
553  ++where;
554  continue;
555  }
556  }
557  else if (get_order_key(where) == order_key)
558  {
559  // Another dummy node with the same value found, discard the new one.
560  destroy_node(dummy_node);
561  return where;
562  }
563 
564  // Move the iterator forward
565  it = where;
566  ++where;
567  }
568 
569  }
570 
571  // This erase function can handle both real and dummy nodes
572  void erase_node(raw_iterator previous, raw_const_iterator& where)
573  {
574  nodeptr_t pnode = (where++).get_node_ptr();
575  nodeptr_t prevnode = previous.get_node_ptr();
576  __TBB_ASSERT(prevnode->my_next == pnode, "Erase must take consecutive iterators");
577  prevnode->my_next = pnode->my_next;
578 
579  destroy_node(pnode);
580  }
581 
582  // Erase the element (previous node needs to be passed because this is a forward only list)
583  iterator erase_node(raw_iterator previous, const_iterator where)
584  {
585  raw_const_iterator it = where;
586  erase_node(previous, it);
587  my_element_count--;
588 
589  return get_iterator(first_real_iterator(it));
590  }
591 
592  // Move all elements from the passed in split-ordered list to this one
593  void move_all(self_type& source)
594  {
595  raw_const_iterator first = source.raw_begin();
596  raw_const_iterator last = source.raw_end();
597 
598  if (first == last)
599  return;
600 
601  nodeptr_t previous_node = my_head;
602  raw_const_iterator begin_iterator = first++;
603 
604  // Move all elements one by one, including dummy ones
605  for (raw_const_iterator it = first; it != last;)
606  {
607  nodeptr_t pnode = it.get_node_ptr();
608 
609  nodeptr_t dummy_node = pnode->is_dummy() ? create_node(pnode->get_order_key()) : create_node(pnode->get_order_key(), pnode->my_element);
610  previous_node = try_insert_atomic(previous_node, dummy_node, NULL);
611  __TBB_ASSERT(previous_node != NULL, "Insertion must succeed");
612  raw_const_iterator where = it++;
613  source.erase_node(get_iterator(begin_iterator), where);
614  }
615  check_range();
616  }
617 
618 
619 private:
620  //Need to setup private fields of split_ordered_list in move constructor and assignment of concurrent_unordered_base
621  template <typename Traits>
623 
624  // Check the list for order violations
625  void check_range( raw_iterator first, raw_iterator last )
626  {
627 #if TBB_USE_ASSERT
628  for (raw_iterator it = first; it != last; ++it)
629  {
630  raw_iterator next = it;
631  ++next;
632 
633  __TBB_ASSERT(next == raw_end() || get_order_key(next) >= get_order_key(it), "!!! List order inconsistency !!!");
634  }
635 #else
637 #endif
638  }
639  void check_range()
640  {
641 #if TBB_USE_ASSERT
642  check_range( raw_begin(), raw_end() );
643 #endif
644  }
645 
647  size_type my_element_count; // Total item count, not counting dummy nodes
648  nodeptr_t my_head; // pointer to head node
649 };
650 
651 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
652 #pragma warning(push)
653 #pragma warning(disable: 4127) // warning C4127: conditional expression is constant
654 #endif
655 
656 template <typename Traits>
657 class concurrent_unordered_base : public Traits
658 {
659 protected:
660  // Type definitions
662  typedef typename Traits::value_type value_type;
663  typedef typename Traits::key_type key_type;
664  typedef typename Traits::hash_compare hash_compare;
665  typedef typename Traits::allocator_type allocator_type;
666  typedef typename hash_compare::hasher hasher;
668 
673  // No support for reference/const_reference in allocator
676 
678  typedef typename solist_t::nodeptr_t nodeptr_t;
679  // Iterators that walk the entire split-order list, including dummy nodes
682  typedef typename solist_t::iterator iterator; // TODO: restore const iterator for unordered_sets
684  typedef iterator local_iterator;
685  typedef const_iterator const_local_iterator;
686  using Traits::my_hash_compare;
687  using Traits::get_key;
688  using Traits::allow_multimapping;
689 
690  static const size_type initial_bucket_number = 8; // Initial number of buckets
691 private:
692  typedef std::pair<iterator, iterator> pairii_t;
693  typedef std::pair<const_iterator, const_iterator> paircc_t;
694 
695  static size_type const pointers_per_table = sizeof(size_type) * 8; // One bucket segment per bit
696  static const size_type initial_bucket_load = 4; // Initial maximum number of elements per bucket
697 
701  void dismiss(){ my_instance = NULL;}
703  if (my_instance){
704  my_instance->internal_clear();
705  }
706  }
707  };
708 protected:
709  // Constructors/Destructors
710  concurrent_unordered_base(size_type n_of_buckets = initial_bucket_number,
711  const hash_compare& hc = hash_compare(), const allocator_type& a = allocator_type())
712  : Traits(hc), my_solist(a),
713  my_allocator(a), my_maximum_bucket_size((float) initial_bucket_load)
714  {
715  if( n_of_buckets == 0) ++n_of_buckets;
716  my_number_of_buckets = size_type(1)<<__TBB_Log2((uintptr_t)n_of_buckets*2-1); // round up to power of 2
717  internal_init();
718  }
719 
720  concurrent_unordered_base(const concurrent_unordered_base& right, const allocator_type& a)
721  : Traits(right.my_hash_compare), my_solist(a), my_allocator(a)
722  {
723  internal_init();
724  internal_copy(right);
725  }
726 
728  : Traits(right.my_hash_compare), my_solist(right.get_allocator()), my_allocator(right.get_allocator())
729  {
730  //FIXME:exception safety seems to be broken here
731  internal_init();
732  internal_copy(right);
733  }
734 
735 #if __TBB_CPP11_RVALUE_REF_PRESENT
737  : Traits(right.my_hash_compare), my_solist(right.get_allocator()), my_allocator(right.get_allocator()),
738  my_maximum_bucket_size(float(initial_bucket_load))
739  {
740  my_number_of_buckets = initial_bucket_number;
741  internal_init();
742  swap(right);
743  }
744 
745  concurrent_unordered_base(concurrent_unordered_base&& right, const allocator_type& a)
746  : Traits(right.my_hash_compare), my_solist(a), my_allocator(a)
747  {
748  call_internal_clear_on_exit clear_buckets_on_exception(this);
749 
750  internal_init();
751  if (a == right.get_allocator()){
752  my_number_of_buckets = initial_bucket_number;
753  my_maximum_bucket_size = float(initial_bucket_load);
754  this->swap(right);
755  }else{
756  my_maximum_bucket_size = right.my_maximum_bucket_size;
757  my_number_of_buckets = right.my_number_of_buckets;
758  my_solist.my_element_count = right.my_solist.my_element_count;
759 
760  if (! right.my_solist.empty()){
761  nodeptr_t previous_node = my_solist.my_head;
762 
763  // Move all elements one by one, including dummy ones
764  for (raw_const_iterator it = ++(right.my_solist.raw_begin()), last = right.my_solist.raw_end(); it != last; ++it)
765  {
766  const nodeptr_t pnode = it.get_node_ptr();
767  nodeptr_t node;
768  if (pnode->is_dummy()) {
769  node = my_solist.create_node(pnode->get_order_key());
770  size_type bucket = __TBB_ReverseBits(pnode->get_order_key()) % my_number_of_buckets;
771  set_bucket(bucket, node);
772  }else{
773  node = my_solist.create_node(pnode->get_order_key(), std::move(pnode->my_element));
774  }
775 
776  previous_node = my_solist.try_insert_atomic(previous_node, node, NULL);
777  __TBB_ASSERT(previous_node != NULL, "Insertion of node failed. Concurrent inserts in constructor ?");
778  }
779  my_solist.check_range();
780  }
781  }
782 
783  clear_buckets_on_exception.dismiss();
784  }
785 
786 #endif // __TBB_CPP11_RVALUE_REF_PRESENT
787 
789  if (this != &right)
790  internal_copy(right);
791  return (*this);
792  }
793 
794 #if __TBB_CPP11_RVALUE_REF_PRESENT
796  {
797  if(this != &other){
799  if(pocma_t::value || this->my_allocator == other.my_allocator) {
800  concurrent_unordered_base trash (std::move(*this));
801  swap(other);
802  if (pocma_t::value) {
803  using std::swap;
804  //TODO: swapping allocators here may be a problem, replace with single direction moving
805  swap(this->my_solist.my_node_allocator, other.my_solist.my_node_allocator);
806  swap(this->my_allocator, other.my_allocator);
807  }
808  } else {
809  concurrent_unordered_base moved_copy(std::move(other),this->my_allocator);
810  this->swap(moved_copy);
811  }
812  }
813  return *this;
814  }
815 
816 #endif // __TBB_CPP11_RVALUE_REF_PRESENT
817 
818 #if __TBB_INITIALIZER_LISTS_PRESENT
819  concurrent_unordered_base& operator=(std::initializer_list<value_type> il)
821  {
822  this->clear();
823  this->insert(il.begin(),il.end());
824  return (*this);
825  }
826 #endif // __TBB_INITIALIZER_LISTS_PRESENT
827 
828 
830  // Delete all node segments
831  internal_clear();
832  }
833 
834 public:
835  allocator_type get_allocator() const {
836  return my_solist.get_allocator();
837  }
838 
839  // Size and capacity function
840  bool empty() const {
841  return my_solist.empty();
842  }
843 
844  size_type size() const {
845  return my_solist.size();
846  }
847 
848  size_type max_size() const {
849  return my_solist.max_size();
850  }
851 
852  // Iterators
853  iterator begin() {
854  return my_solist.begin();
855  }
856 
857  const_iterator begin() const {
858  return my_solist.begin();
859  }
860 
861  iterator end() {
862  return my_solist.end();
863  }
864 
865  const_iterator end() const {
866  return my_solist.end();
867  }
868 
869  const_iterator cbegin() const {
870  return my_solist.cbegin();
871  }
872 
873  const_iterator cend() const {
874  return my_solist.cend();
875  }
876 
877  // Parallel traversal support
880  raw_const_iterator my_begin_node;
881  raw_const_iterator my_end_node;
882  mutable raw_const_iterator my_midpoint_node;
883  public:
890 
892  bool empty() const {return my_begin_node == my_end_node;}
893 
895  bool is_divisible() const {
896  return my_midpoint_node != my_end_node;
897  }
900  my_table(r.my_table), my_end_node(r.my_end_node)
901  {
902  r.my_end_node = my_begin_node = r.my_midpoint_node;
903  __TBB_ASSERT( !empty(), "Splitting despite the range is not divisible" );
904  __TBB_ASSERT( !r.empty(), "Splitting despite the range is not divisible" );
905  set_midpoint();
906  r.set_midpoint();
907  }
910  my_table(a_table), my_begin_node(a_table.my_solist.begin()),
911  my_end_node(a_table.my_solist.end())
912  {
913  set_midpoint();
914  }
915  iterator begin() const { return my_table.my_solist.get_iterator(my_begin_node); }
916  iterator end() const { return my_table.my_solist.get_iterator(my_end_node); }
918  size_type grainsize() const { return 1; }
919 
921  void set_midpoint() const {
922  if( my_begin_node == my_end_node ) // not divisible
923  my_midpoint_node = my_end_node;
924  else {
925  sokey_t begin_key = solist_t::get_safe_order_key(my_begin_node);
926  sokey_t end_key = solist_t::get_safe_order_key(my_end_node);
927  size_t mid_bucket = __TBB_ReverseBits( begin_key + (end_key-begin_key)/2 ) % my_table.my_number_of_buckets;
928  while ( !my_table.is_initialized(mid_bucket) ) mid_bucket = my_table.get_parent(mid_bucket);
929  if(__TBB_ReverseBits(mid_bucket) > begin_key) {
930  // found a dummy_node between begin and end
931  my_midpoint_node = my_table.my_solist.first_real_iterator(my_table.get_bucket( mid_bucket ));
932  }
933  else {
934  // didn't find a dummy node between begin and end.
935  my_midpoint_node = my_end_node;
936  }
937 #if TBB_USE_ASSERT
938  {
939  sokey_t mid_key = solist_t::get_safe_order_key(my_midpoint_node);
940  __TBB_ASSERT( begin_key < mid_key, "my_begin_node is after my_midpoint_node" );
941  __TBB_ASSERT( mid_key <= end_key, "my_midpoint_node is after my_end_node" );
942  }
943 #endif // TBB_USE_ASSERT
944  }
945  }
946  };
947 
948  class range_type : public const_range_type {
949  public:
954  range_type( const concurrent_unordered_base &a_table ) : const_range_type(a_table) {}
955 
956  iterator begin() const { return solist_t::get_iterator( const_range_type::begin() ); }
957  iterator end() const { return solist_t::get_iterator( const_range_type::end() ); }
958  };
959 
960  range_type range() {
961  return range_type( *this );
962  }
963 
964  const_range_type range() const {
965  return const_range_type( *this );
966  }
967 
968  // Modifiers
969  std::pair<iterator, bool> insert(const value_type& value) {
970  return internal_insert</*AllowCreate=*/tbb::internal::true_type>(value);
971  }
972 
973  iterator insert(const_iterator, const value_type& value) {
974  // Ignore hint
975  return insert(value).first;
976  }
977 
978 #if __TBB_CPP11_RVALUE_REF_PRESENT
979  std::pair<iterator, bool> insert(value_type&& value) {
980  return internal_insert</*AllowCreate=*/tbb::internal::true_type>(std::move(value));
981  }
982 
983  iterator insert(const_iterator, value_type&& value) {
984  // Ignore hint
985  return insert(std::move(value)).first;
986  }
987 
988 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
989  template<typename... Args>
990  std::pair<iterator, bool> emplace(Args&&... args) {
991  nodeptr_t pnode = my_solist.create_node_v(tbb::internal::forward<Args>(args)...);
992  const sokey_t hashed_element_key = (sokey_t) my_hash_compare(get_key(pnode->my_element));
993  const sokey_t order_key = split_order_key_regular(hashed_element_key);
994  pnode->init(order_key);
995 
996  return internal_insert</*AllowCreate=*/tbb::internal::false_type>(pnode->my_element, pnode);
997  }
998 
999  template<typename... Args>
1000  iterator emplace_hint(const_iterator, Args&&... args) {
1001  // Ignore hint
1002  return emplace(tbb::internal::forward<Args>(args)...).first;
1003  }
1004 
1005 #endif // __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1006 #endif // __TBB_CPP11_RVALUE_REF_PRESENT
1007 
1008  template<class Iterator>
1009  void insert(Iterator first, Iterator last) {
1010  for (Iterator it = first; it != last; ++it)
1011  insert(*it);
1012  }
1013 
1014 #if __TBB_INITIALIZER_LISTS_PRESENT
1015  void insert(std::initializer_list<value_type> il) {
1017  insert(il.begin(), il.end());
1018  }
1019 #endif
1020 
1021  iterator unsafe_erase(const_iterator where) {
1022  return internal_erase(where);
1023  }
1024 
1025  iterator unsafe_erase(const_iterator first, const_iterator last) {
1026  while (first != last)
1027  unsafe_erase(first++);
1028  return my_solist.get_iterator(first);
1029  }
1030 
1031  size_type unsafe_erase(const key_type& key) {
1032  pairii_t where = equal_range(key);
1033  size_type item_count = internal_distance(where.first, where.second);
1034  unsafe_erase(where.first, where.second);
1035  return item_count;
1036  }
1037 
1039  if (this != &right) {
1040  std::swap(my_hash_compare, right.my_hash_compare); // TODO: check what ADL meant here
1041  my_solist.swap(right.my_solist);
1042  internal_swap_buckets(right);
1043  std::swap(my_number_of_buckets, right.my_number_of_buckets);
1044  std::swap(my_maximum_bucket_size, right.my_maximum_bucket_size);
1045  }
1046  }
1047 
1048  // Observers
1049  hasher hash_function() const {
1050  return my_hash_compare.my_hash_object;
1051  }
1052 
1053  key_equal key_eq() const {
1054  return my_hash_compare.my_key_compare_object;
1055  }
1056 
1057  void clear() {
1058  // Clear list
1059  my_solist.clear();
1060 
1061  // Clear buckets
1062  internal_clear();
1063 
1064  // Initialize bucket 0
1065  __TBB_ASSERT(my_buckets[0] == NULL, NULL);
1066  raw_iterator dummy_node = my_solist.raw_begin();
1067  set_bucket(0, dummy_node);
1068  }
1069 
1070  // Lookup
1071  iterator find(const key_type& key) {
1072  return internal_find(key);
1073  }
1074 
1075  const_iterator find(const key_type& key) const {
1076  return const_cast<self_type*>(this)->internal_find(key);
1077  }
1078 
1079  size_type count(const key_type& key) const {
1080  if(allow_multimapping) {
1081  paircc_t answer = equal_range(key);
1082  size_type item_count = internal_distance(answer.first, answer.second);
1083  return item_count;
1084  } else {
1085  return const_cast<self_type*>(this)->internal_find(key) == end()?0:1;
1086  }
1087  }
1088 
1089  std::pair<iterator, iterator> equal_range(const key_type& key) {
1090  return internal_equal_range(key);
1091  }
1092 
1093  std::pair<const_iterator, const_iterator> equal_range(const key_type& key) const {
1094  return const_cast<self_type*>(this)->internal_equal_range(key);
1095  }
1096 
1097  // Bucket interface - for debugging
1098  size_type unsafe_bucket_count() const {
1099  return my_number_of_buckets;
1100  }
1101 
1102  size_type unsafe_max_bucket_count() const {
1103  return segment_size(pointers_per_table-1);
1104  }
1105 
1106  size_type unsafe_bucket_size(size_type bucket) {
1107  size_type item_count = 0;
1108  if (is_initialized(bucket)) {
1109  raw_iterator it = get_bucket(bucket);
1110  ++it;
1111  for (; it != my_solist.raw_end() && !it.get_node_ptr()->is_dummy(); ++it)
1112  ++item_count;
1113  }
1114  return item_count;
1115  }
1116 
1117  size_type unsafe_bucket(const key_type& key) const {
1118  sokey_t order_key = (sokey_t) my_hash_compare(key);
1119  size_type bucket = order_key % my_number_of_buckets;
1120  return bucket;
1121  }
1122 
1123  // If the bucket is initialized, return a first non-dummy element in it
1124  local_iterator unsafe_begin(size_type bucket) {
1125  if (!is_initialized(bucket))
1126  return end();
1127 
1128  raw_iterator it = get_bucket(bucket);
1129  return my_solist.first_real_iterator(it);
1130  }
1131 
1132  // If the bucket is initialized, return a first non-dummy element in it
1133  const_local_iterator unsafe_begin(size_type bucket) const
1134  {
1135  if (!is_initialized(bucket))
1136  return end();
1137 
1138  raw_const_iterator it = get_bucket(bucket);
1139  return my_solist.first_real_iterator(it);
1140  }
1141 
1142  // @REVIEW: Takes O(n)
1143  // Returns the iterator after the last non-dummy element in the bucket
1144  local_iterator unsafe_end(size_type bucket)
1145  {
1146  if (!is_initialized(bucket))
1147  return end();
1148 
1149  raw_iterator it = get_bucket(bucket);
1150 
1151  // Find the end of the bucket, denoted by the dummy element
1152  do ++it;
1153  while(it != my_solist.raw_end() && !it.get_node_ptr()->is_dummy());
1154 
1155  // Return the first real element past the end of the bucket
1156  return my_solist.first_real_iterator(it);
1157  }
1158 
1159  // @REVIEW: Takes O(n)
1160  // Returns the iterator after the last non-dummy element in the bucket
1161  const_local_iterator unsafe_end(size_type bucket) const
1162  {
1163  if (!is_initialized(bucket))
1164  return end();
1165 
1166  raw_const_iterator it = get_bucket(bucket);
1167 
1168  // Find the end of the bucket, denoted by the dummy element
1169  do ++it;
1170  while(it != my_solist.raw_end() && !it.get_node_ptr()->is_dummy());
1171 
1172  // Return the first real element past the end of the bucket
1173  return my_solist.first_real_iterator(it);
1174  }
1175 
1176  const_local_iterator unsafe_cbegin(size_type bucket) const {
1177  return ((const self_type *) this)->unsafe_begin(bucket);
1178  }
1179 
1180  const_local_iterator unsafe_cend(size_type bucket) const {
1181  return ((const self_type *) this)->unsafe_end(bucket);
1182  }
1183 
1184  // Hash policy
1185  float load_factor() const {
1186  return (float) size() / (float) unsafe_bucket_count();
1187  }
1188 
1189  float max_load_factor() const {
1190  return my_maximum_bucket_size;
1191  }
1192 
1193  void max_load_factor(float newmax) {
1194  if (newmax != newmax || newmax < 0)
1196  my_maximum_bucket_size = newmax;
1197  }
1198 
1199  // This function is a noop, because the underlying split-ordered list
1200  // is already sorted, so an increase in the bucket number will be
1201  // reflected next time this bucket is touched.
1202  void rehash(size_type buckets) {
1203  size_type current_buckets = my_number_of_buckets;
1204  if (current_buckets >= buckets)
1205  return;
1206  my_number_of_buckets = size_type(1)<<__TBB_Log2((uintptr_t)buckets*2-1); // round up to power of 2
1207  }
1208 
1209 private:
1210 
1211  // Initialize the hash and keep the first bucket open
1212  void internal_init() {
1213  // Initialize the array of segment pointers
1214  memset(my_buckets, 0, sizeof(my_buckets));
1215 
1216  // Initialize bucket 0
1217  raw_iterator dummy_node = my_solist.raw_begin();
1218  set_bucket(0, dummy_node);
1219  }
1220 
1222  for (size_type index = 0; index < pointers_per_table; ++index) {
1223  if (my_buckets[index] != NULL) {
1224  size_type sz = segment_size(index);
1225  for (size_type index2 = 0; index2 < sz; ++index2)
1226  my_allocator.destroy(&my_buckets[index][index2]);
1227  my_allocator.deallocate(my_buckets[index], sz);
1228  my_buckets[index] = 0;
1229  }
1230  }
1231  }
1232 
1233  void internal_copy(const self_type& right) {
1234  clear();
1235 
1236  my_maximum_bucket_size = right.my_maximum_bucket_size;
1237  my_number_of_buckets = right.my_number_of_buckets;
1238 
1239  __TBB_TRY {
1240  insert(right.begin(), right.end());
1241  my_hash_compare = right.my_hash_compare;
1242  } __TBB_CATCH(...) {
1243  my_solist.clear();
1244  __TBB_RETHROW();
1245  }
1246  }
1247 
1249  {
1250  // Swap all node segments
1251  for (size_type index = 0; index < pointers_per_table; ++index)
1252  {
1253  raw_iterator * iterator_pointer = my_buckets[index];
1254  my_buckets[index] = right.my_buckets[index];
1255  right.my_buckets[index] = iterator_pointer;
1256  }
1257  }
1258 
1259  //TODO: why not use std::distance?
1260  // Hash APIs
1261  static size_type internal_distance(const_iterator first, const_iterator last)
1262  {
1263  size_type num = 0;
1264 
1265  for (const_iterator it = first; it != last; ++it)
1266  ++num;
1267 
1268  return num;
1269  }
1270 
1271  // Insert an element in the hash given its value
1272  template<typename AllowCreate, typename ValueType>
1273  std::pair<iterator, bool> internal_insert(__TBB_FORWARDING_REF(ValueType) value, nodeptr_t pnode = NULL)
1274  {
1275  const key_type *pkey = &get_key(value);
1276  sokey_t hash_key = (sokey_t) my_hash_compare(*pkey);
1277  size_type new_count = 0;
1278  sokey_t order_key = split_order_key_regular(hash_key);
1279  raw_iterator previous = prepare_bucket(hash_key);
1280  raw_iterator last = my_solist.raw_end();
1281  __TBB_ASSERT(previous != last, "Invalid head node");
1282 
1283  // First node is a dummy node
1284  for (raw_iterator where = previous;;)
1285  {
1286  ++where;
1287  if (where == last || solist_t::get_order_key(where) > order_key ||
1288  // if multimapped, stop at the first item equal to us.
1289  (allow_multimapping && solist_t::get_order_key(where) == order_key &&
1290  !my_hash_compare(get_key(*where), *pkey))) // TODO: fix negation
1291  {
1292  if (!pnode) {
1293  pnode = my_solist.create_node(order_key, tbb::internal::forward<ValueType>(value), AllowCreate());
1294  // If the value was moved, the known reference to key might be invalid
1295  pkey = &get_key(pnode->my_element);
1296  }
1297 
1298  // Try to insert 'pnode' between 'previous' and 'where'
1299  std::pair<iterator, bool> result = my_solist.try_insert(previous, where, pnode, &new_count);
1300 
1301  if (result.second)
1302  {
1303  // Insertion succeeded, adjust the table size, if needed
1304  adjust_table_size(new_count, my_number_of_buckets);
1305  return result;
1306  }
1307  else
1308  {
1309  // Insertion failed: either the same node was inserted by another thread, or
1310  // another element was inserted at exactly the same place as this node.
1311  // Proceed with the search from the previous location where order key was
1312  // known to be larger (note: this is legal only because there is no safe
1313  // concurrent erase operation supported).
1314  where = previous;
1315  continue;
1316  }
1317  }
1318  else if (!allow_multimapping && solist_t::get_order_key(where) == order_key &&
1319  !my_hash_compare(get_key(*where), *pkey)) // TODO: fix negation
1320  { // Element already in the list, return it
1321  if (pnode)
1322  my_solist.destroy_node(pnode);
1323  return std::pair<iterator, bool>(my_solist.get_iterator(where), false);
1324  }
1325  // Move the iterator forward
1326  previous = where;
1327  }
1328  }
1329 
1330  // Find the element in the split-ordered list
1331  iterator internal_find(const key_type& key)
1332  {
1333  sokey_t hash_key = (sokey_t) my_hash_compare(key);
1334  sokey_t order_key = split_order_key_regular(hash_key);
1335  raw_iterator last = my_solist.raw_end();
1336 
1337  for (raw_iterator it = prepare_bucket(hash_key); it != last; ++it)
1338  {
1339  if (solist_t::get_order_key(it) > order_key)
1340  {
1341  // If the order key is smaller than the current order key, the element
1342  // is not in the hash.
1343  return end();
1344  }
1345  else if (solist_t::get_order_key(it) == order_key)
1346  {
1347  // The fact that order keys match does not mean that the element is found.
1348  // Key function comparison has to be performed to check whether this is the
1349  // right element. If not, keep searching while order key is the same.
1350  if (!my_hash_compare(get_key(*it), key)) // TODO: fix negation
1351  return my_solist.get_iterator(it);
1352  }
1353  }
1354 
1355  return end();
1356  }
1357 
1358  // Erase an element from the list. This is not a concurrency safe function.
1359  iterator internal_erase(const_iterator it)
1360  {
1361  sokey_t hash_key = (sokey_t) my_hash_compare(get_key(*it));
1362  raw_iterator previous = prepare_bucket(hash_key);
1363  raw_iterator last = my_solist.raw_end();
1364  __TBB_ASSERT(previous != last, "Invalid head node");
1365 
1366  // First node is a dummy node
1367  for (raw_iterator where = previous; ; previous = where) {
1368  ++where;
1369  if (where == last)
1370  return end();
1371  else if (my_solist.get_iterator(where) == it)
1372  return my_solist.erase_node(previous, it);
1373  }
1374  }
1375 
1376  // Return the [begin, end) pair of iterators with the same key values.
1377  // This operation makes sense only if mapping is many-to-one.
1378  pairii_t internal_equal_range(const key_type& key)
1379  {
1380  sokey_t hash_key = (sokey_t) my_hash_compare(key);
1381  sokey_t order_key = split_order_key_regular(hash_key);
1382  raw_iterator end_it = my_solist.raw_end();
1383 
1384  for (raw_iterator it = prepare_bucket(hash_key); it != end_it; ++it)
1385  {
1386  if (solist_t::get_order_key(it) > order_key)
1387  {
1388  // There is no element with the given key
1389  return pairii_t(end(), end());
1390  }
1391  else if (solist_t::get_order_key(it) == order_key &&
1392  !my_hash_compare(get_key(*it), key)) // TODO: fix negation; also below
1393  {
1394  iterator first = my_solist.get_iterator(it);
1395  iterator last = first;
1396  do ++last; while( allow_multimapping && last != end() && !my_hash_compare(get_key(*last), key) );
1397  return pairii_t(first, last);
1398  }
1399  }
1400 
1401  return pairii_t(end(), end());
1402  }
1403 
1404  // Bucket APIs
1405  void init_bucket(size_type bucket)
1406  {
1407  // Bucket 0 has no parent.
1408  __TBB_ASSERT( bucket != 0, "The first bucket must always be initialized");
1409 
1410  size_type parent_bucket = get_parent(bucket);
1411 
1412  // All parent_bucket buckets have to be initialized before this bucket is
1413  if (!is_initialized(parent_bucket))
1414  init_bucket(parent_bucket);
1415 
1416  raw_iterator parent = get_bucket(parent_bucket);
1417 
1418  // Create a dummy first node in this bucket
1419  raw_iterator dummy_node = my_solist.insert_dummy(parent, split_order_key_dummy(bucket));
1420  set_bucket(bucket, dummy_node);
1421  }
1422 
1423  void adjust_table_size(size_type total_elements, size_type current_size)
1424  {
1425  // Grow the table by a factor of 2 if possible and needed
1426  if ( ((float) total_elements / (float) current_size) > my_maximum_bucket_size )
1427  {
1428  // Double the size of the hash only if size has not changed in between loads
1429  my_number_of_buckets.compare_and_swap(2u*current_size, current_size);
1430  //Simple "my_number_of_buckets.compare_and_swap( current_size<<1, current_size );" does not work for VC8
1431  //due to overzealous compiler warnings in /Wp64 mode
1432  }
1433  }
1434 
1435  size_type get_parent(size_type bucket) const
1436  {
1437  // Unsets bucket's most significant turned-on bit
1438  size_type msb = __TBB_Log2((uintptr_t)bucket);
1439  return bucket & ~(size_type(1) << msb);
1440  }
1441 
1442 
1443  // Dynamic sized array (segments)
1445  static size_type segment_index_of( size_type index ) {
1446  return size_type( __TBB_Log2( uintptr_t(index|1) ) );
1447  }
1448 
1450  static size_type segment_base( size_type k ) {
1451  return (size_type(1)<<k & ~size_type(1));
1452  }
1453 
1455  static size_type segment_size( size_type k ) {
1456  return k? size_type(1)<<k : 2;
1457  }
1458 
1459  raw_iterator get_bucket(size_type bucket) const {
1460  size_type segment = segment_index_of(bucket);
1461  bucket -= segment_base(segment);
1462  __TBB_ASSERT( my_buckets[segment], "bucket must be in an allocated segment" );
1463  return my_buckets[segment][bucket];
1464  }
1465 
1466  raw_iterator prepare_bucket(sokey_t hash_key) {
1467  size_type bucket = hash_key % my_number_of_buckets;
1468  size_type segment = segment_index_of(bucket);
1469  size_type index = bucket - segment_base(segment);
1470  if (my_buckets[segment] == NULL || my_buckets[segment][index].get_node_ptr() == NULL)
1471  init_bucket(bucket);
1472  return my_buckets[segment][index];
1473  }
1474 
1475  void set_bucket(size_type bucket, raw_iterator dummy_head) {
1476  size_type segment = segment_index_of(bucket);
1477  bucket -= segment_base(segment);
1478 
1479  if (my_buckets[segment] == NULL) {
1480  size_type sz = segment_size(segment);
1481  raw_iterator * new_segment = my_allocator.allocate(sz);
1482  std::memset(static_cast<void*>(new_segment), 0, sz*sizeof(raw_iterator));
1483 
1484  if (my_buckets[segment].compare_and_swap( new_segment, NULL) != NULL)
1485  my_allocator.deallocate(new_segment, sz);
1486  }
1487 
1488  my_buckets[segment][bucket] = dummy_head;
1489  }
1490 
1491  bool is_initialized(size_type bucket) const {
1492  size_type segment = segment_index_of(bucket);
1493  bucket -= segment_base(segment);
1494 
1495  if (my_buckets[segment] == NULL)
1496  return false;
1497 
1498  raw_iterator it = my_buckets[segment][bucket];
1499  return (it.get_node_ptr() != NULL);
1500  }
1501 
1502  // Utilities for keys
1503 
1504  // A regular order key has its original hash value reversed and the last bit set
1505  sokey_t split_order_key_regular(sokey_t order_key) const {
1506  return __TBB_ReverseBits(order_key) | 0x1;
1507  }
1508 
1509  // A dummy order key has its original hash value reversed and the last bit unset
1510  sokey_t split_order_key_dummy(sokey_t order_key) const {
1511  return __TBB_ReverseBits(order_key) & ~sokey_t(0x1);
1512  }
1513 
1514  // Shared variables
1516  solist_t my_solist; // List where all the elements are kept
1518  float my_maximum_bucket_size; // Maximum size of the bucket
1519  atomic<raw_iterator*> my_buckets[pointers_per_table]; // The segment table
1520 };
1521 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
1522 #pragma warning(pop) // warning 4127 is back
1523 #endif
1524 
1525 } // namespace internal
1527 } // namespace interface5
1528 } // namespace tbb
1529 #endif // __TBB__concurrent_unordered_impl_H
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp end
concurrent_unordered_base(const concurrent_unordered_base &right)
static sokey_t get_order_key(const raw_const_iterator &it)
void adjust_table_size(size_type total_elements, size_type current_size)
allocator_type::difference_type difference_type
split_ordered_list< value_type, typename Traits::allocator_type > solist_t
iterator emplace_hint(const_iterator, Args &&... args)
tbb::internal::allocator_rebind< allocator_type, raw_iterator >::type my_allocator
iterator erase_node(raw_iterator previous, const_iterator where)
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
Definition: tbb_stddef.h:169
tbb::internal::allocator_traits< allocator_type >::pointer pointer
static sokey_t get_safe_order_key(const raw_const_iterator &it)
const_range_type(const concurrent_unordered_base &a_table)
Init range with container and grainsize specified.
auto first(Container &c) -> decltype(begin(c))
Base class for types that should not be assigned.
Definition: tbb_stddef.h:324
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp begin
allocator_type::const_pointer const_pointer
void suppress_unused_warning(const T1 &)
Utility template function to prevent "unused" warnings by various compilers.
Definition: tbb_stddef.h:381
range_type(const concurrent_unordered_base &a_table)
Init range with container and grainsize specified.
const_local_iterator unsafe_begin(size_type bucket) const
tbb::internal::allocator_rebind< Allocator, T >::type allocator_type
const_iterator first_real_iterator(raw_const_iterator it) const
std::pair< iterator, bool > insert(value_type &&value)
void swap(atomic< T > &lhs, atomic< T > &rhs)
Definition: atomic.h:539
raw_iterator insert_dummy(raw_iterator it, sokey_t order_key)
#define __TBB_TRY
Definition: tbb_stddef.h:287
#define __TBB_PARAMETER_PACK
Definition: tbb_stddef.h:507
allocator_type::pointer pointer
void internal_swap_buckets(concurrent_unordered_base &right)
tbb::internal::allocator_traits< allocator_type >::const_pointer const_pointer
split_ordered_list(allocator_type a=allocator_type())
iterator insert(const_iterator, const value_type &value)
concurrent_unordered_base(const concurrent_unordered_base &right, const allocator_type &a)
std::pair< iterator, bool > insert(const value_type &value)
const_local_iterator unsafe_cend(size_type bucket) const
const_local_iterator unsafe_cbegin(size_type bucket) const
T __TBB_ReverseBits(T src)
Definition: tbb_machine.h:974
allocator_type::value_type value_type
const_iterator get_iterator(raw_const_iterator it) const
std::pair< const_iterator, const_iterator > equal_range(const key_type &key) const
void throw_exception(exception_id eid)
Versionless convenience wrapper for throw_exception_v4()
iterator unsafe_erase(const_iterator first, const_iterator last)
atomic< raw_iterator * > my_buckets[pointers_per_table]
tbb::internal::allocator_traits< allocator_type >::value_type value_type
auto last(Container &c) -> decltype(begin(c))
intptr_t __TBB_Log2(uintptr_t x)
Definition: tbb_machine.h:867
tbb::internal::allocator_traits< allocator_type >::difference_type difference_type
concurrent_unordered_base & operator=(const concurrent_unordered_base &right)
#define __TBB_CATCH(e)
Definition: tbb_stddef.h:288
concurrent_unordered_base(concurrent_unordered_base &&right, const allocator_type &a)
concurrent_unordered_base & operator=(concurrent_unordered_base &&other)
#define __TBB_FORWARDING_REF(A)
Definition: tbb_stddef.h:500
solist_iterator(const solist_iterator< Solist, typename Solist::value_type > &other)
iterator insert(const_iterator, value_type &&value)
The graph class.
void swap(concurrent_hash_map< Key, T, HashCompare, A > &a, concurrent_hash_map< Key, T, HashCompare, A > &b)
friend bool operator!=(const flist_iterator< M, T > &i, const flist_iterator< M, U > &j)
std::pair< iterator, bool > emplace(Args &&... args)
std::pair< iterator, bool > internal_insert(__TBB_FORWARDING_REF(ValueType) value, nodeptr_t pnode=NULL)
solist_iterator< self_type, value_type > iterator
#define __TBB_PACK_EXPANSION(A)
Definition: tbb_stddef.h:508
flist_iterator< self_type, const value_type > raw_const_iterator
void set_bucket(size_type bucket, raw_iterator dummy_head)
static nodeptr_t try_insert_atomic(nodeptr_t previous, nodeptr_t new_node, nodeptr_t current_node)
void set_midpoint() const
Set my_midpoint_node to point approximately half way between my_begin_node and my_end_node.
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long value
const_local_iterator unsafe_end(size_type bucket) const
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id parent
friend bool operator==(const flist_iterator< M, T > &i, const flist_iterator< M, U > &j)
tbb::internal::allocator_traits< allocator_type >::pointer pointer
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle * key
solist_iterator(nodeptr_t pnode, const Solist *plist)
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance * instance
flist_iterator(const flist_iterator< Solist, typename Solist::value_type > &other)
tbb::internal::allocator_rebind< allocator_type, node >::type my_node_allocator
void check_range(raw_iterator first, raw_iterator last)
allocator_traits< Alloc >::template rebind_alloc< T >::other type
bool is_divisible() const
True if range can be partitioned into two subranges.
tbb::internal::allocator_traits< allocator_type >::size_type size_type
void move(tbb_thread &t1, tbb_thread &t2)
Definition: tbb_thread.h:309
solist_iterator< self_type, const value_type > const_iterator
tbb::internal::allocator_traits< allocator_type >::difference_type difference_type
nodeptr_t create_node_v(__TBB_FORWARDING_REF(Args) __TBB_PARAMETER_PACK args)
flist_iterator< self_type, value_type > raw_iterator
nodeptr_t create_node(sokey_t, __TBB_FORWARDING_REF(Arg), tbb::internal::false_type)
concurrent_unordered_base::size_type size_type
Type for size of a range.
tbb::internal::allocator_traits< allocator_type >::size_type size_type
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t size
tbb::internal::allocator_traits< allocator_type >::const_pointer const_pointer
std::pair< iterator, bool > try_insert(raw_iterator it, raw_iterator next, nodeptr_t pnode, size_type *new_count)
nodeptr_t atomic_set_next(nodeptr_t new_node, nodeptr_t current_node)
std::pair< iterator, iterator > equal_range(const key_type &key)
allocator_type::size_type size_type
static size_type internal_distance(const_iterator first, const_iterator last)
concurrent_unordered_base(size_type n_of_buckets=initial_bucket_number, const hash_compare &hc=hash_compare(), const allocator_type &a=allocator_type())
Dummy type that distinguishes splitting constructor from copy constructor.
Definition: tbb_stddef.h:399
std::pair< const_iterator, const_iterator > paircc_t
nodeptr_t create_node(sokey_t order_key, __TBB_FORWARDING_REF(Arg) t, tbb::internal::true_type=tbb::internal::true_type())
atomic< T > & as_atomic(T &t)
Definition: atomic.h:547
#define __TBB_RETHROW()
Definition: tbb_stddef.h:290
void erase_node(raw_iterator previous, raw_const_iterator &where)

Copyright © 2005-2019 Intel Corporation. All Rights Reserved.

Intel, Pentium, Intel Xeon, Itanium, Intel XScale and VTune are registered trademarks or trademarks of Intel Corporation or its subsidiaries in the United States and other countries.

* Other names and brands may be claimed as the property of others.