2 Copyright 2005-2010 Intel Corporation. All Rights Reserved.
4 This file is part of Threading Building Blocks.
6 Threading Building Blocks is free software; you can redistribute it
7 and/or modify it under the terms of the GNU General Public License
8 version 2 as published by the Free Software Foundation.
10 Threading Building Blocks is distributed in the hope that it will be
11 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with Threading Building Blocks; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 As a special exception, you may use this file as part of a free software
20 library without restriction. Specifically, if other files instantiate
21 templates or use macros or inline functions from this file, or you compile
22 this file and link it with other files to produce an executable, this
23 file does not by itself cause the resulting executable to be covered by
24 the GNU General Public License. This exception does not however
25 invalidate any other reasons why the executable file might be covered by
26 the GNU General Public License.
29 #ifndef __TBB_enumerable_thread_specific_H
30 #define __TBB_enumerable_thread_specific_H
32 #include "concurrent_vector.h"
33 #include "tbb_thread.h"
34 #include "cache_aligned_allocator.h"
36 #include <string.h> // for memcpy
47 //! enum for selecting between single key and key-per-instance versions
48 enum ets_key_usage_type { ets_key_per_instance, ets_no_key };
50 namespace interface5 {
55 template<ets_key_usage_type ETS_key_type>
56 class ets_base: tbb::internal::no_copy {
59 typedef DWORD key_type;
61 typedef pthread_t key_type;
63 #if __TBB_GCC_3_3_PROTECTED_BROKEN
71 slot& at( size_t k ) {
72 return ((slot*)(void*)(this+1))[k];
74 size_t size() const {return (size_t)1<<lg_size;}
75 size_t mask() const {return size()-1;}
76 size_t start( size_t h ) const {
77 return h>>(8*sizeof(size_t)-lg_size);
83 bool empty() const {return !key;}
84 bool match( key_type k ) const {return key==k;}
85 bool claim( key_type k ) {
86 __TBB_ASSERT(sizeof(tbb::atomic<key_type>)==sizeof(key_type), NULL);
87 __TBB_ASSERT(sizeof(void*)==sizeof(tbb::atomic<key_type>*), NULL);
88 union { void* space; tbb::atomic<key_type>* key_atomic; } helper;
90 return helper.key_atomic->compare_and_swap(k,0)==0;
93 #if __TBB_GCC_3_3_PROTECTED_BROKEN
97 static key_type key_of_current_thread() {
98 tbb::tbb_thread::id id = tbb::this_tbb_thread::get_id();
100 memcpy( &k, &id, sizeof(k) );
104 //! Root of linked list of arrays of decreasing size.
105 /** NULL if and only if my_count==0.
106 Each array in the list is half the size of its predecessor. */
107 atomic<array*> my_root;
108 atomic<size_t> my_count;
109 virtual void* create_local() = 0;
110 virtual void* create_array(size_t _size) = 0; // _size in bytes
111 virtual void free_array(void* ptr, size_t _size) = 0; // _size in bytes
112 array* allocate( size_t lg_size ) {
113 size_t n = 1<<lg_size;
114 array* a = static_cast<array*>(create_array( sizeof(array)+n*sizeof(slot) ));
115 a->lg_size = lg_size;
116 std::memset( a+1, 0, n*sizeof(slot) );
119 void free(array* a) {
120 size_t n = 1<<(a->lg_size);
121 free_array( (void *)a, size_t(sizeof(array)+n*sizeof(slot)) );
123 static size_t hash( key_type k ) {
124 // Multiplicative hashing. Client should use *upper* bits.
125 // casts required for Mac gcc4.* compiler
126 #if __TBB_WORDSIZE == 4
127 return uintptr_t(k)*0x9E3779B9;
129 return uintptr_t(k)*0x9E3779B97F4A7C15;
133 ets_base() {my_root=NULL; my_count=0;}
134 virtual ~ets_base(); // g++ complains if this is not virtual...
135 void* table_lookup( bool& exists );
137 slot& table_find( key_type k ) {
140 size_t mask = r->mask();
141 for(size_t i = r->start(h);;i=(i+1)&mask) {
143 if( s.empty() || s.match(k) )
147 void table_reserve_for_copy( const ets_base& other ) {
148 __TBB_ASSERT(!my_root,NULL);
149 __TBB_ASSERT(!my_count,NULL);
150 if( other.my_root ) {
151 array* a = allocate(other.my_root->lg_size);
154 my_count = other.my_count;
159 template<ets_key_usage_type ETS_key_type>
160 ets_base<ETS_key_type>::~ets_base() {
161 __TBB_ASSERT(!my_root, NULL);
164 template<ets_key_usage_type ETS_key_type>
165 void ets_base<ETS_key_type>::table_clear() {
166 while( array* r = my_root ) {
173 template<ets_key_usage_type ETS_key_type>
174 void* ets_base<ETS_key_type>::table_lookup( bool& exists ) {
175 const key_type k = key_of_current_thread();
177 __TBB_ASSERT(k!=0,NULL);
180 for( array* r=my_root; r; r=r->next ) {
181 size_t mask=r->mask();
182 for(size_t i = r->start(h); ;i=(i+1)&mask) {
184 if( s.empty() ) break;
187 // Success at top level
191 // Success at some other level. Need to insert at top level.
199 // Key does not yet exist
201 found = create_local();
203 size_t c = ++my_count;
205 if( !r || c>r->size()/2 ) {
206 size_t s = r ? r->lg_size : 2;
207 while( c>size_t(1)<<(s-1) ) ++s;
208 array* a = allocate(s);
211 array* new_r = my_root.compare_and_swap(a,r);
212 if( new_r==r ) break;
213 if( new_r->lg_size>=s ) {
214 // Another thread inserted an equal or bigger array, so our array is superfluous.
223 // Guaranteed to be room for it, and it is not present, so search for empty slot and grab it.
225 size_t mask = ir->mask();
226 for(size_t i = ir->start(h);;i=(i+1)&mask) {
237 //! Specialization that exploits native TLS
239 class ets_base<ets_key_per_instance>: protected ets_base<ets_no_key> {
240 typedef ets_base<ets_no_key> super;
242 typedef DWORD tls_key_t;
243 void create_key() { my_key = TlsAlloc(); }
244 void destroy_key() { TlsFree(my_key); }
245 void set_tls(void * value) { TlsSetValue(my_key, (LPVOID)value); }
246 void* get_tls() { return (void *)TlsGetValue(my_key); }
248 typedef pthread_key_t tls_key_t;
249 void create_key() { pthread_key_create(&my_key, NULL); }
250 void destroy_key() { pthread_key_delete(my_key); }
251 void set_tls( void * value ) const { pthread_setspecific(my_key, value); }
252 void* get_tls() const { return pthread_getspecific(my_key); }
255 virtual void* create_local() = 0;
256 virtual void* create_array(size_t _size) = 0; // _size in bytes
257 virtual void free_array(void* ptr, size_t _size) = 0; // size in bytes
259 ets_base() {create_key();}
260 ~ets_base() {destroy_key();}
261 void* table_lookup( bool& exists ) {
262 void* found = get_tls();
266 found = super::table_lookup(exists);
274 super::table_clear();
278 //! Random access iterator for traversing the thread local copies.
279 template< typename Container, typename Value >
280 class enumerable_thread_specific_iterator
281 #if defined(_WIN64) && defined(_MSC_VER)
282 // Ensure that Microsoft's internal template function _Val_type works correctly.
283 : public std::iterator<std::random_access_iterator_tag,Value>
284 #endif /* defined(_WIN64) && defined(_MSC_VER) */
286 //! current position in the concurrent_vector
288 Container *my_container;
289 typename Container::size_type my_index;
290 mutable Value *my_value;
292 template<typename C, typename T>
293 friend enumerable_thread_specific_iterator<C,T> operator+( ptrdiff_t offset,
294 const enumerable_thread_specific_iterator<C,T>& v );
296 template<typename C, typename T, typename U>
297 friend bool operator==( const enumerable_thread_specific_iterator<C,T>& i,
298 const enumerable_thread_specific_iterator<C,U>& j );
300 template<typename C, typename T, typename U>
301 friend bool operator<( const enumerable_thread_specific_iterator<C,T>& i,
302 const enumerable_thread_specific_iterator<C,U>& j );
304 template<typename C, typename T, typename U>
305 friend ptrdiff_t operator-( const enumerable_thread_specific_iterator<C,T>& i, const enumerable_thread_specific_iterator<C,U>& j );
307 template<typename C, typename U>
308 friend class enumerable_thread_specific_iterator;
312 enumerable_thread_specific_iterator( const Container &container, typename Container::size_type index ) :
313 my_container(&const_cast<Container &>(container)), my_index(index), my_value(NULL) {}
315 //! Default constructor
316 enumerable_thread_specific_iterator() : my_container(NULL), my_index(0), my_value(NULL) {}
319 enumerable_thread_specific_iterator( const enumerable_thread_specific_iterator<Container, U>& other ) :
320 my_container( other.my_container ), my_index( other.my_index), my_value( const_cast<Value *>(other.my_value) ) {}
322 enumerable_thread_specific_iterator operator+( ptrdiff_t offset ) const {
323 return enumerable_thread_specific_iterator(*my_container, my_index + offset);
326 enumerable_thread_specific_iterator &operator+=( ptrdiff_t offset ) {
332 enumerable_thread_specific_iterator operator-( ptrdiff_t offset ) const {
333 return enumerable_thread_specific_iterator( *my_container, my_index-offset );
336 enumerable_thread_specific_iterator &operator-=( ptrdiff_t offset ) {
342 Value& operator*() const {
343 Value* value = my_value;
345 value = my_value = reinterpret_cast<Value *>(&(*my_container)[my_index].value);
347 __TBB_ASSERT( value==reinterpret_cast<Value *>(&(*my_container)[my_index].value), "corrupt cache" );
351 Value& operator[]( ptrdiff_t k ) const {
352 return (*my_container)[my_index + k].value;
355 Value* operator->() const {return &operator*();}
357 enumerable_thread_specific_iterator& operator++() {
363 enumerable_thread_specific_iterator& operator--() {
370 enumerable_thread_specific_iterator operator++(int) {
371 enumerable_thread_specific_iterator result = *this;
378 enumerable_thread_specific_iterator operator--(int) {
379 enumerable_thread_specific_iterator result = *this;
386 typedef ptrdiff_t difference_type;
387 typedef Value value_type;
388 typedef Value* pointer;
389 typedef Value& reference;
390 typedef std::random_access_iterator_tag iterator_category;
393 template<typename Container, typename T>
394 enumerable_thread_specific_iterator<Container,T> operator+( ptrdiff_t offset,
395 const enumerable_thread_specific_iterator<Container,T>& v ) {
396 return enumerable_thread_specific_iterator<Container,T>( v.my_container, v.my_index + offset );
399 template<typename Container, typename T, typename U>
400 bool operator==( const enumerable_thread_specific_iterator<Container,T>& i,
401 const enumerable_thread_specific_iterator<Container,U>& j ) {
402 return i.my_index==j.my_index && i.my_container == j.my_container;
405 template<typename Container, typename T, typename U>
406 bool operator!=( const enumerable_thread_specific_iterator<Container,T>& i,
407 const enumerable_thread_specific_iterator<Container,U>& j ) {
411 template<typename Container, typename T, typename U>
412 bool operator<( const enumerable_thread_specific_iterator<Container,T>& i,
413 const enumerable_thread_specific_iterator<Container,U>& j ) {
414 return i.my_index<j.my_index;
417 template<typename Container, typename T, typename U>
418 bool operator>( const enumerable_thread_specific_iterator<Container,T>& i,
419 const enumerable_thread_specific_iterator<Container,U>& j ) {
423 template<typename Container, typename T, typename U>
424 bool operator>=( const enumerable_thread_specific_iterator<Container,T>& i,
425 const enumerable_thread_specific_iterator<Container,U>& j ) {
429 template<typename Container, typename T, typename U>
430 bool operator<=( const enumerable_thread_specific_iterator<Container,T>& i,
431 const enumerable_thread_specific_iterator<Container,U>& j ) {
435 template<typename Container, typename T, typename U>
436 ptrdiff_t operator-( const enumerable_thread_specific_iterator<Container,T>& i,
437 const enumerable_thread_specific_iterator<Container,U>& j ) {
438 return i.my_index-j.my_index;
441 template<typename SegmentedContainer, typename Value >
442 class segmented_iterator
443 #if defined(_WIN64) && defined(_MSC_VER)
444 : public std::iterator<std::input_iterator_tag, Value>
447 template<typename C, typename T, typename U>
448 friend bool operator==(const segmented_iterator<C,T>& i, const segmented_iterator<C,U>& j);
450 template<typename C, typename T, typename U>
451 friend bool operator!=(const segmented_iterator<C,T>& i, const segmented_iterator<C,U>& j);
453 template<typename C, typename U>
454 friend class segmented_iterator;
458 segmented_iterator() {my_segcont = NULL;}
460 segmented_iterator( const SegmentedContainer& _segmented_container ) :
461 my_segcont(const_cast<SegmentedContainer*>(&_segmented_container)),
462 outer_iter(my_segcont->end()) { }
464 ~segmented_iterator() {}
466 typedef typename SegmentedContainer::iterator outer_iterator;
467 typedef typename SegmentedContainer::value_type InnerContainer;
468 typedef typename InnerContainer::iterator inner_iterator;
471 typedef ptrdiff_t difference_type;
472 typedef Value value_type;
473 typedef typename SegmentedContainer::size_type size_type;
474 typedef Value* pointer;
475 typedef Value& reference;
476 typedef std::input_iterator_tag iterator_category;
480 segmented_iterator(const segmented_iterator<SegmentedContainer, U>& other) :
481 my_segcont(other.my_segcont),
482 outer_iter(other.outer_iter),
483 // can we assign a default-constructed iterator to inner if we're at the end?
484 inner_iter(other.inner_iter)
489 segmented_iterator& operator=( const segmented_iterator<SegmentedContainer, U>& other) {
491 my_segcont = other.my_segcont;
492 outer_iter = other.outer_iter;
493 if(outer_iter != my_segcont->end()) inner_iter = other.inner_iter;
498 // allow assignment of outer iterator to segmented iterator. Once it is
499 // assigned, move forward until a non-empty inner container is found or
500 // the end of the outer container is reached.
501 segmented_iterator& operator=(const outer_iterator& new_outer_iter) {
502 __TBB_ASSERT(my_segcont != NULL, NULL);
503 // check that this iterator points to something inside the segmented container
504 for(outer_iter = new_outer_iter ;outer_iter!=my_segcont->end(); ++outer_iter) {
505 if( !outer_iter->empty() ) {
506 inner_iter = outer_iter->begin();
514 segmented_iterator& operator++() {
520 segmented_iterator operator++(int) {
521 segmented_iterator tmp = *this;
526 bool operator==(const outer_iterator& other_outer) const {
527 __TBB_ASSERT(my_segcont != NULL, NULL);
528 return (outer_iter == other_outer &&
529 (outer_iter == my_segcont->end() || inner_iter == outer_iter->begin()));
532 bool operator!=(const outer_iterator& other_outer) const {
533 return !operator==(other_outer);
538 reference operator*() const {
539 __TBB_ASSERT(my_segcont != NULL, NULL);
540 __TBB_ASSERT(outer_iter != my_segcont->end(), "Dereferencing a pointer at end of container");
541 __TBB_ASSERT(inner_iter != outer_iter->end(), NULL); // should never happen
546 pointer operator->() const { return &operator*();}
549 SegmentedContainer* my_segcont;
550 outer_iterator outer_iter;
551 inner_iterator inner_iter;
554 __TBB_ASSERT(my_segcont != NULL, NULL);
555 __TBB_ASSERT(outer_iter != my_segcont->end(), NULL); // not true if there are no inner containers
556 __TBB_ASSERT(inner_iter != outer_iter->end(), NULL); // not true if the inner containers are all empty.
558 while(inner_iter == outer_iter->end() && ++outer_iter != my_segcont->end()) {
559 inner_iter = outer_iter->begin();
562 }; // segmented_iterator
564 template<typename SegmentedContainer, typename T, typename U>
565 bool operator==( const segmented_iterator<SegmentedContainer,T>& i,
566 const segmented_iterator<SegmentedContainer,U>& j ) {
567 if(i.my_segcont != j.my_segcont) return false;
568 if(i.my_segcont == NULL) return true;
569 if(i.outer_iter != j.outer_iter) return false;
570 if(i.outer_iter == i.my_segcont->end()) return true;
571 return i.inner_iter == j.inner_iter;
575 template<typename SegmentedContainer, typename T, typename U>
576 bool operator!=( const segmented_iterator<SegmentedContainer,T>& i,
577 const segmented_iterator<SegmentedContainer,U>& j ) {
581 // storage for initialization function pointer
583 struct callback_base {
584 virtual T apply( ) = 0;
585 virtual void destroy( ) = 0;
586 // need to be able to create copies of callback_base for copy constructor
587 virtual callback_base* make_copy() = 0;
588 // need virtual destructor to satisfy GCC compiler warning
589 virtual ~callback_base() { }
592 template <typename T, typename Functor>
593 struct callback_leaf : public callback_base<T>, public tbb::internal::no_copy {
594 typedef Functor my_callback_type;
595 typedef callback_leaf<T,Functor> my_type;
596 typedef my_type* callback_pointer;
597 typedef typename tbb::tbb_allocator<my_type> my_allocator_type;
599 callback_leaf( const Functor& f_) : f(f_) {
602 static callback_pointer new_callback(const Functor& f_ ) {
603 void* new_void = my_allocator_type().allocate(1);
604 callback_pointer new_cb = new (new_void) callback_leaf<T,Functor>(f_); // placement new
608 /* override */ callback_pointer make_copy() {
609 return new_callback( f );
612 /* override */ void destroy( ) {
613 callback_pointer my_ptr = this;
614 my_allocator_type().destroy(my_ptr);
615 my_allocator_type().deallocate(my_ptr,1);
617 /* override */ T apply() { return f(); } // does copy construction of returned value.
621 //! Template for adding padding in order to avoid false sharing
622 /** ModularSize should be sizeof(U) modulo the cache line size.
623 All maintenance of the space will be done explicitly on push_back,
624 and all thread local copies must be destroyed before the concurrent
627 template<typename U, size_t ModularSize>
629 char value[sizeof(U) + tbb::internal::NFS_MaxLineSize-ModularSize];
631 // "reinterpret_cast<U*>(&value)->~U();" causes type-punning warning with gcc 4.4,
632 // "U* u = reinterpret_cast<U*>(&value); u->~U();" causes unused variable warning with VS2010.
633 // Thus another "casting via union" hack.
634 __TBB_ASSERT(sizeof(void*)==sizeof(U*),NULL);
635 union { void* space; U* val; } helper;
636 helper.space = &value;
641 //! Partial specialization for case where no padding is needed.
643 struct ets_element<U,0> {
644 char value[sizeof(U)];
645 void unconstruct() { // Same implementation as in general case
646 __TBB_ASSERT(sizeof(void*)==sizeof(U*),NULL);
647 union { void* space; U* val; } helper;
648 helper.space = &value;
653 } // namespace internal
656 //! The enumerable_thread_specific container
657 /** enumerable_thread_specific has the following properties:
658 - thread-local copies are lazily created, with default, exemplar or function initialization.
659 - thread-local copies do not move (during lifetime, and excepting clear()) so the address of a copy is invariant.
660 - the contained objects need not have operator=() defined if combine is not used.
661 - enumerable_thread_specific containers may be copy-constructed or assigned.
662 - thread-local copies can be managed by hash-table, or can be accessed via TLS storage for speed.
663 - outside of parallel contexts, the contents of all thread-local copies are accessible by iterator or using combine or combine_each methods
665 @par Segmented iterator
666 When the thread-local objects are containers with input_iterators defined, a segmented iterator may
667 be used to iterate over all the elements of all thread-local copies.
669 @par combine and combine_each
670 - Both methods are defined for enumerable_thread_specific.
671 - combine() requires the the type T have operator=() defined.
672 - neither method modifies the contents of the object (though there is no guarantee that the applied methods do not modify the object.)
673 - Both are evaluated in serial context (the methods are assumed to be non-benign.)
675 @ingroup containers */
676 template <typename T,
677 typename Allocator=cache_aligned_allocator<T>,
678 ets_key_usage_type ETS_key_type=ets_no_key >
679 class enumerable_thread_specific: internal::ets_base<ETS_key_type> {
681 template<typename U, typename A, ets_key_usage_type C> friend class enumerable_thread_specific;
683 typedef internal::ets_element<T,sizeof(T)%tbb::internal::NFS_MaxLineSize> padded_element;
685 //! A generic range, used to create range objects from the iterators
687 class generic_range_type: public blocked_range<I> {
689 typedef T value_type;
690 typedef T& reference;
691 typedef const T& const_reference;
693 typedef ptrdiff_t difference_type;
694 generic_range_type( I begin_, I end_, size_t grainsize_ = 1) : blocked_range<I>(begin_,end_,grainsize_) {}
696 generic_range_type( const generic_range_type<U>& r) : blocked_range<I>(r.begin(),r.end(),r.grainsize()) {}
697 generic_range_type( generic_range_type& r, split ) : blocked_range<I>(r,split()) {}
700 typedef typename Allocator::template rebind< padded_element >::other padded_allocator_type;
701 typedef tbb::concurrent_vector< padded_element, padded_allocator_type > internal_collection_type;
703 internal::callback_base<T> *my_finit_callback;
705 // need to use a pointed-to exemplar because T may not be assignable.
706 // using tbb_allocator instead of padded_element_allocator because we may be
707 // copying an exemplar from one instantiation of ETS to another with a different
709 typedef typename tbb::tbb_allocator<padded_element > exemplar_allocator_type;
710 static padded_element * create_exemplar(const T& my_value) {
711 padded_element *new_exemplar = reinterpret_cast<padded_element *>(exemplar_allocator_type().allocate(1));
712 new(new_exemplar->value) T(my_value);
716 static padded_element *create_exemplar( ) {
717 padded_element *new_exemplar = reinterpret_cast<padded_element *>(exemplar_allocator_type().allocate(1));
718 new(new_exemplar->value) T( );
722 static void free_exemplar(padded_element *my_ptr) {
723 my_ptr->unconstruct();
724 exemplar_allocator_type().destroy(my_ptr);
725 exemplar_allocator_type().deallocate(my_ptr,1);
728 padded_element* my_exemplar_ptr;
730 internal_collection_type my_locals;
732 /*override*/ void* create_local() {
734 void* lref = &my_locals[my_locals.push_back(padded_element())];
736 void* lref = &*my_locals.push_back(padded_element());
738 if(my_finit_callback) {
739 new(lref) T(my_finit_callback->apply());
740 } else if(my_exemplar_ptr) {
741 pointer t_exemp = reinterpret_cast<T *>(&(my_exemplar_ptr->value));
742 new(lref) T(*t_exemp);
749 void unconstruct_locals() {
750 for(typename internal_collection_type::iterator cvi = my_locals.begin(); cvi != my_locals.end(); ++cvi) {
755 typedef typename Allocator::template rebind< uintptr_t >::other array_allocator_type;
758 /*override*/ void* create_array(size_t _size) {
759 size_t nelements = (_size + sizeof(uintptr_t) -1) / sizeof(uintptr_t);
760 return array_allocator_type().allocate(nelements);
763 /*override*/ void free_array( void* _ptr, size_t _size) {
764 size_t nelements = (_size + sizeof(uintptr_t) -1) / sizeof(uintptr_t);
765 array_allocator_type().deallocate( reinterpret_cast<uintptr_t *>(_ptr),nelements);
771 typedef Allocator allocator_type;
772 typedef T value_type;
773 typedef T& reference;
774 typedef const T& const_reference;
776 typedef const T* const_pointer;
777 typedef typename internal_collection_type::size_type size_type;
778 typedef typename internal_collection_type::difference_type difference_type;
781 typedef typename internal::enumerable_thread_specific_iterator< internal_collection_type, value_type > iterator;
782 typedef typename internal::enumerable_thread_specific_iterator< internal_collection_type, const value_type > const_iterator;
784 // Parallel range types
785 typedef generic_range_type< iterator > range_type;
786 typedef generic_range_type< const_iterator > const_range_type;
788 //! Default constructor, which leads to default construction of local copies
789 enumerable_thread_specific() : my_finit_callback(0) {
793 //! construction with initializer method
794 // Finit should be a function taking 0 parameters and returning a T
795 template <typename Finit>
796 enumerable_thread_specific( Finit _finit )
798 my_finit_callback = internal::callback_leaf<T,Finit>::new_callback( _finit );
799 my_exemplar_ptr = 0; // don't need exemplar if function is provided
802 //! Constuction with exemplar, which leads to copy construction of local copies
803 enumerable_thread_specific(const T &_exemplar) : my_finit_callback(0) {
804 my_exemplar_ptr = create_exemplar(_exemplar);
808 ~enumerable_thread_specific() {
809 if(my_finit_callback) {
810 my_finit_callback->destroy();
812 if(my_exemplar_ptr) {
813 free_exemplar(my_exemplar_ptr);
815 this->clear(); // deallocation before the derived class is finished destructing
816 // So free(array *) is still accessible
819 //! returns reference to local, discarding exists
822 return local(exists);
825 //! Returns reference to calling thread's local copy, creating one if necessary
826 reference local(bool& exists) {
827 __TBB_ASSERT(ETS_key_type==ets_no_key,"ets_key_per_instance not yet implemented");
828 void* ptr = this->table_lookup(exists);
832 //! Get the number of local copies
833 size_type size() const { return my_locals.size(); }
835 //! true if there have been no local copies created
836 bool empty() const { return my_locals.empty(); }
839 iterator begin() { return iterator( my_locals, 0 ); }
841 iterator end() { return iterator(my_locals, my_locals.size() ); }
843 //! begin const iterator
844 const_iterator begin() const { return const_iterator(my_locals, 0); }
846 //! end const iterator
847 const_iterator end() const { return const_iterator(my_locals, my_locals.size()); }
849 //! Get range for parallel algorithms
850 range_type range( size_t grainsize=1 ) { return range_type( begin(), end(), grainsize ); }
852 //! Get const range for parallel algorithms
853 const_range_type range( size_t grainsize=1 ) const { return const_range_type( begin(), end(), grainsize ); }
855 //! Destroys local copies
857 unconstruct_locals();
860 // callback is not destroyed
861 // exemplar is not destroyed
866 template<typename U, typename A2, ets_key_usage_type C2>
867 void internal_copy( const enumerable_thread_specific<U, A2, C2>& other);
871 template<typename U, typename Alloc, ets_key_usage_type Cachetype>
872 enumerable_thread_specific( const enumerable_thread_specific<U, Alloc, Cachetype>& other ) : internal::ets_base<ETS_key_type> ()
874 internal_copy(other);
877 enumerable_thread_specific( const enumerable_thread_specific& other ) : internal::ets_base<ETS_key_type> ()
879 internal_copy(other);
884 template<typename U, typename A2, ets_key_usage_type C2>
885 enumerable_thread_specific &
886 internal_assign(const enumerable_thread_specific<U, A2, C2>& other) {
887 if(static_cast<void *>( this ) != static_cast<const void *>( &other )) {
889 if(my_finit_callback) {
890 my_finit_callback->destroy();
891 my_finit_callback = 0;
893 if(my_exemplar_ptr) {
894 free_exemplar(my_exemplar_ptr);
897 internal_copy( other );
905 enumerable_thread_specific& operator=(const enumerable_thread_specific& other) {
906 return internal_assign(other);
909 template<typename U, typename Alloc, ets_key_usage_type Cachetype>
910 enumerable_thread_specific& operator=(const enumerable_thread_specific<U, Alloc, Cachetype>& other)
912 return internal_assign(other);
915 // combine_func_t has signature T(T,T) or T(const T&, const T&)
916 template <typename combine_func_t>
917 T combine(combine_func_t f_combine) {
918 if(begin() == end()) {
919 if(my_finit_callback) {
920 return my_finit_callback->apply();
922 pointer local_ref = reinterpret_cast<T*>((my_exemplar_ptr->value));
923 return T(*local_ref);
925 const_iterator ci = begin();
928 my_result = f_combine( my_result, *ci );
932 // combine_func_t has signature void(T) or void(const T&)
933 template <typename combine_func_t>
934 void combine_each(combine_func_t f_combine) {
935 for(const_iterator ci = begin(); ci != end(); ++ci) {
940 }; // enumerable_thread_specific
943 template <typename T, typename Allocator, ets_key_usage_type ETS_key_type>
944 template<typename U, typename A2, ets_key_usage_type C2>
945 void enumerable_thread_specific<T,Allocator,ETS_key_type>::internal_copy( const enumerable_thread_specific<U, A2, C2>& other) {
946 typedef internal::ets_base<ets_no_key> base;
947 __TBB_ASSERT(my_locals.size()==0,NULL);
948 this->table_reserve_for_copy( other );
949 for( base::array* r=other.my_root; r; r=r->next ) {
950 for( size_t i=0; i<r->size(); ++i ) {
951 base::slot& s1 = r->at(i);
953 base::slot& s2 = this->table_find(s1.key);
956 void* lref = &my_locals[my_locals.push_back(padded_element())];
958 void* lref = &*my_locals.push_back(padded_element());
960 s2.ptr = new(lref) T(*(U*)s1.ptr);
963 // Skip the duplicate
968 if(other.my_finit_callback) {
969 my_finit_callback = other.my_finit_callback->make_copy();
971 my_finit_callback = 0;
973 if(other.my_exemplar_ptr) {
974 pointer local_ref = reinterpret_cast<U*>(other.my_exemplar_ptr->value);
975 my_exemplar_ptr = create_exemplar(*local_ref);
981 template< typename Container >
984 // This intermediate typedef is to address issues with VC7.1 compilers
985 typedef typename Container::value_type conval_type;
990 typedef typename conval_type::size_type size_type;
991 typedef typename conval_type::difference_type difference_type;
992 typedef typename conval_type::allocator_type allocator_type;
993 typedef typename conval_type::value_type value_type;
994 typedef typename conval_type::reference reference;
995 typedef typename conval_type::const_reference const_reference;
996 typedef typename conval_type::pointer pointer;
997 typedef typename conval_type::const_pointer const_pointer;
999 typedef typename internal::segmented_iterator<Container, value_type> iterator;
1000 typedef typename internal::segmented_iterator<Container, const value_type> const_iterator;
1002 flattened2d( const Container &c, typename Container::const_iterator b, typename Container::const_iterator e ) :
1003 my_container(const_cast<Container*>(&c)), my_begin(b), my_end(e) { }
1005 flattened2d( const Container &c ) :
1006 my_container(const_cast<Container*>(&c)), my_begin(c.begin()), my_end(c.end()) { }
1008 iterator begin() { return iterator(*my_container) = my_begin; }
1009 iterator end() { return iterator(*my_container) = my_end; }
1010 const_iterator begin() const { return const_iterator(*my_container) = my_begin; }
1011 const_iterator end() const { return const_iterator(*my_container) = my_end; }
1013 size_type size() const {
1014 size_type tot_size = 0;
1015 for(typename Container::const_iterator i = my_begin; i != my_end; ++i) {
1016 tot_size += i->size();
1023 Container *my_container;
1024 typename Container::const_iterator my_begin;
1025 typename Container::const_iterator my_end;
1029 template <typename Container>
1030 flattened2d<Container> flatten2d(const Container &c, const typename Container::const_iterator b, const typename Container::const_iterator e) {
1031 return flattened2d<Container>(c, b, e);
1034 template <typename Container>
1035 flattened2d<Container> flatten2d(const Container &c) {
1036 return flattened2d<Container>(c);
1041 namespace internal {
1042 using interface5::internal::segmented_iterator;
1045 using interface5::enumerable_thread_specific;
1046 using interface5::flattened2d;
1047 using interface5::flatten2d;