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

Copyright © 2005-2018 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.