149e08aacStbbdev /* 2*54a22a62SAlexandr-Konovalov Copyright (c) 2005-2024 Intel Corporation 349e08aacStbbdev 449e08aacStbbdev Licensed under the Apache License, Version 2.0 (the "License"); 549e08aacStbbdev you may not use this file except in compliance with the License. 649e08aacStbbdev You may obtain a copy of the License at 749e08aacStbbdev 849e08aacStbbdev http://www.apache.org/licenses/LICENSE-2.0 949e08aacStbbdev 1049e08aacStbbdev Unless required by applicable law or agreed to in writing, software 1149e08aacStbbdev distributed under the License is distributed on an "AS IS" BASIS, 1249e08aacStbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1349e08aacStbbdev See the License for the specific language governing permissions and 1449e08aacStbbdev limitations under the License. 1549e08aacStbbdev */ 1649e08aacStbbdev 1749e08aacStbbdev #ifndef __TBB_enumerable_thread_specific_H 1849e08aacStbbdev #define __TBB_enumerable_thread_specific_H 1949e08aacStbbdev 2049e08aacStbbdev #include "detail/_config.h" 2149e08aacStbbdev #include "detail/_namespace_injection.h" 2249e08aacStbbdev #include "detail/_assert.h" 2349e08aacStbbdev #include "detail/_template_helpers.h" 2449e08aacStbbdev #include "detail/_aligned_space.h" 2549e08aacStbbdev 2649e08aacStbbdev #include "concurrent_vector.h" 2749e08aacStbbdev #include "tbb_allocator.h" 2849e08aacStbbdev #include "cache_aligned_allocator.h" 2949e08aacStbbdev #include "profiling.h" 3049e08aacStbbdev 3149e08aacStbbdev #include <atomic> 3249e08aacStbbdev #include <thread> 3349e08aacStbbdev #include <cstring> // memcpy 3449e08aacStbbdev #include <cstddef> // std::ptrdiff_t 3549e08aacStbbdev 3649e08aacStbbdev #include "task.h" // for task::suspend_point 3749e08aacStbbdev 3849e08aacStbbdev #if _WIN32 || _WIN64 39*54a22a62SAlexandr-Konovalov #ifndef NOMINMAX 40*54a22a62SAlexandr-Konovalov #define NOMINMAX 41*54a22a62SAlexandr-Konovalov #define __TBB_DEFINED_NOMINMAX 1 42*54a22a62SAlexandr-Konovalov #endif 4349e08aacStbbdev #include <windows.h> 44*54a22a62SAlexandr-Konovalov #if __TBB_DEFINED_NOMINMAX 45*54a22a62SAlexandr-Konovalov #undef NOMINMAX 46*54a22a62SAlexandr-Konovalov #undef __TBB_DEFINED_NOMINMAX 47*54a22a62SAlexandr-Konovalov #endif 4849e08aacStbbdev #else 4949e08aacStbbdev #include <pthread.h> 5049e08aacStbbdev #endif 5149e08aacStbbdev 5249e08aacStbbdev namespace tbb { 5349e08aacStbbdev namespace detail { 5449e08aacStbbdev namespace d1 { 5549e08aacStbbdev 5649e08aacStbbdev //! enum for selecting between single key and key-per-instance versions 5749e08aacStbbdev enum ets_key_usage_type { 5849e08aacStbbdev ets_key_per_instance 5949e08aacStbbdev , ets_no_key 6049e08aacStbbdev #if __TBB_RESUMABLE_TASKS 6149e08aacStbbdev , ets_suspend_aware 6249e08aacStbbdev #endif 6349e08aacStbbdev }; 6449e08aacStbbdev 6549e08aacStbbdev // Forward declaration to use in internal classes 6649e08aacStbbdev template <typename T, typename Allocator, ets_key_usage_type ETS_key_type> 6749e08aacStbbdev class enumerable_thread_specific; 6849e08aacStbbdev 6949e08aacStbbdev template <std::size_t ThreadIDSize> 7049e08aacStbbdev struct internal_ets_key_selector { 7149e08aacStbbdev using key_type = std::thread::id; current_keyinternal_ets_key_selector7249e08aacStbbdev static key_type current_key() { 7349e08aacStbbdev return std::this_thread::get_id(); 7449e08aacStbbdev } 7549e08aacStbbdev }; 7649e08aacStbbdev 7749e08aacStbbdev // Intel Compiler on OSX cannot create atomics objects that instantiated from non-fundamental types 7849e08aacStbbdev #if __INTEL_COMPILER && __APPLE__ 7949e08aacStbbdev template<> 8049e08aacStbbdev struct internal_ets_key_selector<sizeof(std::size_t)> { 8149e08aacStbbdev using key_type = std::size_t; 8249e08aacStbbdev static key_type current_key() { 8349e08aacStbbdev auto id = std::this_thread::get_id(); 8449e08aacStbbdev return reinterpret_cast<key_type&>(id); 8549e08aacStbbdev } 8649e08aacStbbdev }; 8749e08aacStbbdev #endif 8849e08aacStbbdev 8949e08aacStbbdev template <ets_key_usage_type ETS_key_type> 9049e08aacStbbdev struct ets_key_selector : internal_ets_key_selector<sizeof(std::thread::id)> {}; 9149e08aacStbbdev 9249e08aacStbbdev #if __TBB_RESUMABLE_TASKS 9349e08aacStbbdev template <> 9449e08aacStbbdev struct ets_key_selector<ets_suspend_aware> { 9549e08aacStbbdev using key_type = suspend_point; 9649e08aacStbbdev static key_type current_key() { 9749e08aacStbbdev return r1::current_suspend_point(); 9849e08aacStbbdev } 9949e08aacStbbdev }; 10049e08aacStbbdev #endif 10149e08aacStbbdev 10249e08aacStbbdev template<ets_key_usage_type ETS_key_type> 10349e08aacStbbdev class ets_base : detail::no_copy { 10449e08aacStbbdev protected: 10549e08aacStbbdev using key_type = typename ets_key_selector<ETS_key_type>::key_type; 10649e08aacStbbdev 10749e08aacStbbdev public: 10849e08aacStbbdev struct slot; 10949e08aacStbbdev struct array { 11049e08aacStbbdev array* next; 11149e08aacStbbdev std::size_t lg_size; 11249e08aacStbbdev slot& at( std::size_t k ) { 11349e08aacStbbdev return (reinterpret_cast<slot*>(reinterpret_cast<void*>(this+1)))[k]; 11449e08aacStbbdev } 11549e08aacStbbdev std::size_t size() const { return std::size_t(1) << lg_size; } 11649e08aacStbbdev std::size_t mask() const { return size() - 1; } 11749e08aacStbbdev std::size_t start( std::size_t h ) const { 11849e08aacStbbdev return h >> (8 * sizeof(std::size_t) - lg_size); 11949e08aacStbbdev } 12049e08aacStbbdev }; 12149e08aacStbbdev struct slot { 12249e08aacStbbdev std::atomic<key_type> key; 12349e08aacStbbdev void* ptr; 12449e08aacStbbdev bool empty() const { return key.load(std::memory_order_relaxed) == key_type(); } 12549e08aacStbbdev bool match( key_type k ) const { return key.load(std::memory_order_relaxed) == k; } 12649e08aacStbbdev bool claim( key_type k ) { 12749e08aacStbbdev // TODO: maybe claim ptr, because key_type is not guaranteed to fit into word size 12849e08aacStbbdev key_type expected = key_type(); 12949e08aacStbbdev return key.compare_exchange_strong(expected, k); 13049e08aacStbbdev } 13149e08aacStbbdev }; 13249e08aacStbbdev 13349e08aacStbbdev protected: 13449e08aacStbbdev //! Root of linked list of arrays of decreasing size. 13549e08aacStbbdev /** nullptr if and only if my_count==0. 13649e08aacStbbdev Each array in the list is half the size of its predecessor. */ 13749e08aacStbbdev std::atomic<array*> my_root; 13849e08aacStbbdev std::atomic<std::size_t> my_count; 13949e08aacStbbdev 14049e08aacStbbdev virtual void* create_local() = 0; 14149e08aacStbbdev virtual void* create_array(std::size_t _size) = 0; // _size in bytes 14249e08aacStbbdev virtual void free_array(void* ptr, std::size_t _size) = 0; // _size in bytes 14349e08aacStbbdev 14449e08aacStbbdev array* allocate( std::size_t lg_size ) { 14549e08aacStbbdev std::size_t n = std::size_t(1) << lg_size; 14649e08aacStbbdev array* a = static_cast<array*>(create_array(sizeof(array) + n * sizeof(slot))); 14749e08aacStbbdev a->lg_size = lg_size; 14849e08aacStbbdev std::memset( a + 1, 0, n * sizeof(slot) ); 14949e08aacStbbdev return a; 15049e08aacStbbdev } 151500abe33SAlex void deallocate(array* a) { 15249e08aacStbbdev std::size_t n = std::size_t(1) << (a->lg_size); 15349e08aacStbbdev free_array( static_cast<void*>(a), std::size_t(sizeof(array) + n * sizeof(slot)) ); 15449e08aacStbbdev } 15549e08aacStbbdev 15649e08aacStbbdev ets_base() : my_root{nullptr}, my_count{0} {} 15749e08aacStbbdev virtual ~ets_base(); // g++ complains if this is not virtual 15849e08aacStbbdev 15949e08aacStbbdev void* table_lookup( bool& exists ); 16049e08aacStbbdev void table_clear(); 16149e08aacStbbdev // The following functions are not used in concurrent context, 16249e08aacStbbdev // so we don't need synchronization and ITT annotations there. 16349e08aacStbbdev template <ets_key_usage_type E2> 16449e08aacStbbdev void table_elementwise_copy( const ets_base& other, 16549e08aacStbbdev void*(*add_element)(ets_base<E2>&, void*) ) { 16657f524caSIlya Isaev __TBB_ASSERT(!my_root.load(std::memory_order_relaxed), nullptr); 16757f524caSIlya Isaev __TBB_ASSERT(!my_count.load(std::memory_order_relaxed), nullptr); 16849e08aacStbbdev if( !other.my_root.load(std::memory_order_relaxed) ) return; 16949e08aacStbbdev array* root = allocate(other.my_root.load(std::memory_order_relaxed)->lg_size); 17049e08aacStbbdev my_root.store(root, std::memory_order_relaxed); 17149e08aacStbbdev root->next = nullptr; 17249e08aacStbbdev my_count.store(other.my_count.load(std::memory_order_relaxed), std::memory_order_relaxed); 17349e08aacStbbdev std::size_t mask = root->mask(); 17449e08aacStbbdev for( array* r = other.my_root.load(std::memory_order_relaxed); r; r = r->next ) { 17549e08aacStbbdev for( std::size_t i = 0; i < r->size(); ++i ) { 17649e08aacStbbdev slot& s1 = r->at(i); 17749e08aacStbbdev if( !s1.empty() ) { 17849e08aacStbbdev for( std::size_t j = root->start(std::hash<key_type>{}(s1.key.load(std::memory_order_relaxed))); ; j = (j+1)&mask ) { 17949e08aacStbbdev slot& s2 = root->at(j); 18049e08aacStbbdev if( s2.empty() ) { 18149e08aacStbbdev s2.ptr = add_element(static_cast<ets_base<E2>&>(*this), s1.ptr); 18249e08aacStbbdev s2.key.store(s1.key.load(std::memory_order_relaxed), std::memory_order_relaxed); 18349e08aacStbbdev break; 18449e08aacStbbdev } 18549e08aacStbbdev else if( s2.match(s1.key.load(std::memory_order_relaxed)) ) 18649e08aacStbbdev break; 18749e08aacStbbdev } 18849e08aacStbbdev } 18949e08aacStbbdev } 19049e08aacStbbdev } 19149e08aacStbbdev } 19249e08aacStbbdev void table_swap( ets_base& other ) { 19349e08aacStbbdev __TBB_ASSERT(this!=&other, "Don't swap an instance with itself"); 19449e08aacStbbdev swap_atomics_relaxed(my_root, other.my_root); 19549e08aacStbbdev swap_atomics_relaxed(my_count, other.my_count); 19649e08aacStbbdev } 19749e08aacStbbdev }; 19849e08aacStbbdev 19949e08aacStbbdev template<ets_key_usage_type ETS_key_type> 20049e08aacStbbdev ets_base<ETS_key_type>::~ets_base() { 20149e08aacStbbdev __TBB_ASSERT(!my_root.load(std::memory_order_relaxed), nullptr); 20249e08aacStbbdev } 20349e08aacStbbdev 20449e08aacStbbdev template<ets_key_usage_type ETS_key_type> 20549e08aacStbbdev void ets_base<ETS_key_type>::table_clear() { 20649e08aacStbbdev while ( array* r = my_root.load(std::memory_order_relaxed) ) { 20749e08aacStbbdev my_root.store(r->next, std::memory_order_relaxed); 208500abe33SAlex deallocate(r); 20949e08aacStbbdev } 21049e08aacStbbdev my_count.store(0, std::memory_order_relaxed); 21149e08aacStbbdev } 21249e08aacStbbdev 21349e08aacStbbdev template<ets_key_usage_type ETS_key_type> 21449e08aacStbbdev void* ets_base<ETS_key_type>::table_lookup( bool& exists ) { 21549e08aacStbbdev const key_type k = ets_key_selector<ETS_key_type>::current_key(); 21649e08aacStbbdev 21757f524caSIlya Isaev __TBB_ASSERT(k != key_type(), nullptr); 21849e08aacStbbdev void* found; 21949e08aacStbbdev std::size_t h = std::hash<key_type>{}(k); 22049e08aacStbbdev for( array* r = my_root.load(std::memory_order_acquire); r; r = r->next ) { 22149e08aacStbbdev call_itt_notify(acquired,r); 22249e08aacStbbdev std::size_t mask=r->mask(); 22349e08aacStbbdev for(std::size_t i = r->start(h); ;i=(i+1)&mask) { 22449e08aacStbbdev slot& s = r->at(i); 22549e08aacStbbdev if( s.empty() ) break; 22649e08aacStbbdev if( s.match(k) ) { 22749e08aacStbbdev if( r == my_root.load(std::memory_order_acquire) ) { 22849e08aacStbbdev // Success at top level 22949e08aacStbbdev exists = true; 23049e08aacStbbdev return s.ptr; 23149e08aacStbbdev } else { 23249e08aacStbbdev // Success at some other level. Need to insert at top level. 23349e08aacStbbdev exists = true; 23449e08aacStbbdev found = s.ptr; 23549e08aacStbbdev goto insert; 23649e08aacStbbdev } 23749e08aacStbbdev } 23849e08aacStbbdev } 23949e08aacStbbdev } 24049e08aacStbbdev // Key does not yet exist. The density of slots in the table does not exceed 0.5, 24149e08aacStbbdev // for if this will occur a new table is allocated with double the current table 24249e08aacStbbdev // size, which is swapped in as the new root table. So an empty slot is guaranteed. 24349e08aacStbbdev exists = false; 24449e08aacStbbdev found = create_local(); 24549e08aacStbbdev { 24649e08aacStbbdev std::size_t c = ++my_count; 24749e08aacStbbdev array* r = my_root.load(std::memory_order_acquire); 24849e08aacStbbdev call_itt_notify(acquired,r); 24949e08aacStbbdev if( !r || c > r->size()/2 ) { 25049e08aacStbbdev std::size_t s = r ? r->lg_size : 2; 25149e08aacStbbdev while( c > std::size_t(1)<<(s-1) ) ++s; 25249e08aacStbbdev array* a = allocate(s); 25349e08aacStbbdev for(;;) { 25449e08aacStbbdev a->next = r; 25549e08aacStbbdev call_itt_notify(releasing,a); 25649e08aacStbbdev array* new_r = r; 25749e08aacStbbdev if( my_root.compare_exchange_strong(new_r, a) ) break; 25849e08aacStbbdev call_itt_notify(acquired, new_r); 25949e08aacStbbdev __TBB_ASSERT(new_r != nullptr, nullptr); 26049e08aacStbbdev if( new_r->lg_size >= s ) { 26149e08aacStbbdev // Another thread inserted an equal or bigger array, so our array is superfluous. 262500abe33SAlex deallocate(a); 26349e08aacStbbdev break; 26449e08aacStbbdev } 26549e08aacStbbdev r = new_r; 26649e08aacStbbdev } 26749e08aacStbbdev } 26849e08aacStbbdev } 26949e08aacStbbdev insert: 27049e08aacStbbdev // Whether a slot has been found in an older table, or if it has been inserted at this level, 27149e08aacStbbdev // it has already been accounted for in the total. Guaranteed to be room for it, and it is 27249e08aacStbbdev // not present, so search for empty slot and use it. 27349e08aacStbbdev array* ir = my_root.load(std::memory_order_acquire); 27449e08aacStbbdev call_itt_notify(acquired, ir); 27549e08aacStbbdev std::size_t mask = ir->mask(); 27649e08aacStbbdev for(std::size_t i = ir->start(h);; i = (i+1)&mask) { 27749e08aacStbbdev slot& s = ir->at(i); 27849e08aacStbbdev if( s.empty() ) { 27949e08aacStbbdev if( s.claim(k) ) { 28049e08aacStbbdev s.ptr = found; 28149e08aacStbbdev return found; 28249e08aacStbbdev } 28349e08aacStbbdev } 28449e08aacStbbdev } 28549e08aacStbbdev } 28649e08aacStbbdev 28749e08aacStbbdev //! Specialization that exploits native TLS 28849e08aacStbbdev template <> 28949e08aacStbbdev class ets_base<ets_key_per_instance>: public ets_base<ets_no_key> { 29049e08aacStbbdev using super = ets_base<ets_no_key>; 29149e08aacStbbdev #if _WIN32||_WIN64 29249e08aacStbbdev #if __TBB_WIN8UI_SUPPORT 29349e08aacStbbdev using tls_key_t = DWORD; 29457f524caSIlya Isaev void create_key() { my_key = FlsAlloc(nullptr); } 29549e08aacStbbdev void destroy_key() { FlsFree(my_key); } 29649e08aacStbbdev void set_tls(void * value) { FlsSetValue(my_key, (LPVOID)value); } 29749e08aacStbbdev void* get_tls() { return (void *)FlsGetValue(my_key); } 29849e08aacStbbdev #else 29949e08aacStbbdev using tls_key_t = DWORD; 30049e08aacStbbdev void create_key() { my_key = TlsAlloc(); } 30149e08aacStbbdev void destroy_key() { TlsFree(my_key); } 30249e08aacStbbdev void set_tls(void * value) { TlsSetValue(my_key, (LPVOID)value); } 30349e08aacStbbdev void* get_tls() { return (void *)TlsGetValue(my_key); } 30449e08aacStbbdev #endif 30549e08aacStbbdev #else 30649e08aacStbbdev using tls_key_t = pthread_key_t; 30757f524caSIlya Isaev void create_key() { pthread_key_create(&my_key, nullptr); } 30849e08aacStbbdev void destroy_key() { pthread_key_delete(my_key); } 30949e08aacStbbdev void set_tls( void * value ) const { pthread_setspecific(my_key, value); } 31049e08aacStbbdev void* get_tls() const { return pthread_getspecific(my_key); } 31149e08aacStbbdev #endif 31249e08aacStbbdev tls_key_t my_key; 31349e08aacStbbdev virtual void* create_local() override = 0; 31449e08aacStbbdev virtual void* create_array(std::size_t _size) override = 0; // _size in bytes 31549e08aacStbbdev virtual void free_array(void* ptr, std::size_t _size) override = 0; // size in bytes 31649e08aacStbbdev protected: 31749e08aacStbbdev ets_base() {create_key();} 31849e08aacStbbdev ~ets_base() {destroy_key();} 31949e08aacStbbdev void* table_lookup( bool& exists ) { 32049e08aacStbbdev void* found = get_tls(); 32149e08aacStbbdev if( found ) { 32249e08aacStbbdev exists=true; 32349e08aacStbbdev } else { 32449e08aacStbbdev found = super::table_lookup(exists); 32549e08aacStbbdev set_tls(found); 32649e08aacStbbdev } 32749e08aacStbbdev return found; 32849e08aacStbbdev } 32949e08aacStbbdev void table_clear() { 33049e08aacStbbdev destroy_key(); 33149e08aacStbbdev create_key(); 33249e08aacStbbdev super::table_clear(); 33349e08aacStbbdev } 33449e08aacStbbdev void table_swap( ets_base& other ) { 33549e08aacStbbdev using std::swap; 33649e08aacStbbdev __TBB_ASSERT(this!=&other, "Don't swap an instance with itself"); 33749e08aacStbbdev swap(my_key, other.my_key); 33849e08aacStbbdev super::table_swap(other); 33949e08aacStbbdev } 34049e08aacStbbdev }; 34149e08aacStbbdev 34249e08aacStbbdev //! Random access iterator for traversing the thread local copies. 34349e08aacStbbdev template< typename Container, typename Value > 34449e08aacStbbdev class enumerable_thread_specific_iterator 34549e08aacStbbdev { 34649e08aacStbbdev //! current position in the concurrent_vector 34749e08aacStbbdev 34849e08aacStbbdev Container *my_container; 34949e08aacStbbdev typename Container::size_type my_index; 35049e08aacStbbdev mutable Value *my_value; 35149e08aacStbbdev 35249e08aacStbbdev template<typename C, typename T, typename U> 35349e08aacStbbdev friend bool operator==( const enumerable_thread_specific_iterator<C, T>& i, 35449e08aacStbbdev const enumerable_thread_specific_iterator<C, U>& j ); 35549e08aacStbbdev 35649e08aacStbbdev template<typename C, typename T, typename U> 35749e08aacStbbdev friend bool operator<( const enumerable_thread_specific_iterator<C,T>& i, 35849e08aacStbbdev const enumerable_thread_specific_iterator<C,U>& j ); 35949e08aacStbbdev 36049e08aacStbbdev template<typename C, typename T, typename U> 36149e08aacStbbdev friend std::ptrdiff_t operator-( const enumerable_thread_specific_iterator<C,T>& i, 36249e08aacStbbdev const enumerable_thread_specific_iterator<C,U>& j ); 36349e08aacStbbdev 36449e08aacStbbdev template<typename C, typename U> 36549e08aacStbbdev friend class enumerable_thread_specific_iterator; 36649e08aacStbbdev 36749e08aacStbbdev public: 36849e08aacStbbdev //! STL support 36949e08aacStbbdev using difference_type = std::ptrdiff_t; 37049e08aacStbbdev using value_type = Value; 37149e08aacStbbdev using pointer = Value*; 37249e08aacStbbdev using reference = Value&; 37349e08aacStbbdev using iterator_category = std::random_access_iterator_tag; 37449e08aacStbbdev 37549e08aacStbbdev enumerable_thread_specific_iterator( const Container &container, typename Container::size_type index ) : 37649e08aacStbbdev my_container(&const_cast<Container &>(container)), my_index(index), my_value(nullptr) {} 37749e08aacStbbdev 37849e08aacStbbdev //! Default constructor 37949e08aacStbbdev enumerable_thread_specific_iterator() : my_container(nullptr), my_index(0), my_value(nullptr) {} 38049e08aacStbbdev 38149e08aacStbbdev template<typename U> 38249e08aacStbbdev enumerable_thread_specific_iterator( const enumerable_thread_specific_iterator<Container, U>& other ) : 38349e08aacStbbdev my_container( other.my_container ), my_index( other.my_index), my_value( const_cast<Value *>(other.my_value) ) {} 38449e08aacStbbdev 38549e08aacStbbdev enumerable_thread_specific_iterator operator+( std::ptrdiff_t offset ) const { 38649e08aacStbbdev return enumerable_thread_specific_iterator(*my_container, my_index + offset); 38749e08aacStbbdev } 38849e08aacStbbdev 38949e08aacStbbdev friend enumerable_thread_specific_iterator operator+( std::ptrdiff_t offset, enumerable_thread_specific_iterator v ) { 39049e08aacStbbdev return enumerable_thread_specific_iterator(*v.my_container, v.my_index + offset); 39149e08aacStbbdev } 39249e08aacStbbdev 39349e08aacStbbdev enumerable_thread_specific_iterator &operator+=( std::ptrdiff_t offset ) { 39449e08aacStbbdev my_index += offset; 39549e08aacStbbdev my_value = nullptr; 39649e08aacStbbdev return *this; 39749e08aacStbbdev } 39849e08aacStbbdev 39949e08aacStbbdev enumerable_thread_specific_iterator operator-( std::ptrdiff_t offset ) const { 40049e08aacStbbdev return enumerable_thread_specific_iterator( *my_container, my_index-offset ); 40149e08aacStbbdev } 40249e08aacStbbdev 40349e08aacStbbdev enumerable_thread_specific_iterator &operator-=( std::ptrdiff_t offset ) { 40449e08aacStbbdev my_index -= offset; 40549e08aacStbbdev my_value = nullptr; 40649e08aacStbbdev return *this; 40749e08aacStbbdev } 40849e08aacStbbdev 40949e08aacStbbdev Value& operator*() const { 41049e08aacStbbdev Value* value = my_value; 41149e08aacStbbdev if( !value ) { 41249e08aacStbbdev value = my_value = (*my_container)[my_index].value(); 41349e08aacStbbdev } 41449e08aacStbbdev __TBB_ASSERT( value==(*my_container)[my_index].value(), "corrupt cache" ); 41549e08aacStbbdev return *value; 41649e08aacStbbdev } 41749e08aacStbbdev 41849e08aacStbbdev Value& operator[]( std::ptrdiff_t k ) const { 41949e08aacStbbdev return *(*my_container)[my_index + k].value(); 42049e08aacStbbdev } 42149e08aacStbbdev 42249e08aacStbbdev Value* operator->() const {return &operator*();} 42349e08aacStbbdev 42449e08aacStbbdev enumerable_thread_specific_iterator& operator++() { 42549e08aacStbbdev ++my_index; 42649e08aacStbbdev my_value = nullptr; 42749e08aacStbbdev return *this; 42849e08aacStbbdev } 42949e08aacStbbdev 43049e08aacStbbdev enumerable_thread_specific_iterator& operator--() { 43149e08aacStbbdev --my_index; 43249e08aacStbbdev my_value = nullptr; 43349e08aacStbbdev return *this; 43449e08aacStbbdev } 43549e08aacStbbdev 43649e08aacStbbdev //! Post increment 43749e08aacStbbdev enumerable_thread_specific_iterator operator++(int) { 43849e08aacStbbdev enumerable_thread_specific_iterator result = *this; 43949e08aacStbbdev ++my_index; 44049e08aacStbbdev my_value = nullptr; 44149e08aacStbbdev return result; 44249e08aacStbbdev } 44349e08aacStbbdev 44449e08aacStbbdev //! Post decrement 44549e08aacStbbdev enumerable_thread_specific_iterator operator--(int) { 44649e08aacStbbdev enumerable_thread_specific_iterator result = *this; 44749e08aacStbbdev --my_index; 44849e08aacStbbdev my_value = nullptr; 44949e08aacStbbdev return result; 45049e08aacStbbdev } 45149e08aacStbbdev }; 45249e08aacStbbdev 45349e08aacStbbdev template<typename Container, typename T, typename U> 45449e08aacStbbdev bool operator==( const enumerable_thread_specific_iterator<Container, T>& i, 45549e08aacStbbdev const enumerable_thread_specific_iterator<Container, U>& j ) { 45649e08aacStbbdev return i.my_index == j.my_index && i.my_container == j.my_container; 45749e08aacStbbdev } 45849e08aacStbbdev 45949e08aacStbbdev template<typename Container, typename T, typename U> 46049e08aacStbbdev bool operator!=( const enumerable_thread_specific_iterator<Container,T>& i, 46149e08aacStbbdev const enumerable_thread_specific_iterator<Container,U>& j ) { 46249e08aacStbbdev return !(i==j); 46349e08aacStbbdev } 46449e08aacStbbdev 46549e08aacStbbdev template<typename Container, typename T, typename U> 46649e08aacStbbdev bool operator<( const enumerable_thread_specific_iterator<Container,T>& i, 46749e08aacStbbdev const enumerable_thread_specific_iterator<Container,U>& j ) { 46849e08aacStbbdev return i.my_index<j.my_index; 46949e08aacStbbdev } 47049e08aacStbbdev 47149e08aacStbbdev template<typename Container, typename T, typename U> 47249e08aacStbbdev bool operator>( const enumerable_thread_specific_iterator<Container,T>& i, 47349e08aacStbbdev const enumerable_thread_specific_iterator<Container,U>& j ) { 47449e08aacStbbdev return j<i; 47549e08aacStbbdev } 47649e08aacStbbdev 47749e08aacStbbdev template<typename Container, typename T, typename U> 47849e08aacStbbdev bool operator>=( const enumerable_thread_specific_iterator<Container,T>& i, 47949e08aacStbbdev const enumerable_thread_specific_iterator<Container,U>& j ) { 48049e08aacStbbdev return !(i<j); 48149e08aacStbbdev } 48249e08aacStbbdev 48349e08aacStbbdev template<typename Container, typename T, typename U> 48449e08aacStbbdev bool operator<=( const enumerable_thread_specific_iterator<Container,T>& i, 48549e08aacStbbdev const enumerable_thread_specific_iterator<Container,U>& j ) { 48649e08aacStbbdev return !(j<i); 48749e08aacStbbdev } 48849e08aacStbbdev 48949e08aacStbbdev template<typename Container, typename T, typename U> 49049e08aacStbbdev std::ptrdiff_t operator-( const enumerable_thread_specific_iterator<Container,T>& i, 49149e08aacStbbdev const enumerable_thread_specific_iterator<Container,U>& j ) { 49249e08aacStbbdev return i.my_index-j.my_index; 49349e08aacStbbdev } 49449e08aacStbbdev 49549e08aacStbbdev template<typename SegmentedContainer, typename Value > 49649e08aacStbbdev class segmented_iterator 49749e08aacStbbdev { 49849e08aacStbbdev template<typename C, typename T, typename U> 49949e08aacStbbdev friend bool operator==(const segmented_iterator<C,T>& i, const segmented_iterator<C,U>& j); 50049e08aacStbbdev 50149e08aacStbbdev template<typename C, typename T, typename U> 50249e08aacStbbdev friend bool operator!=(const segmented_iterator<C,T>& i, const segmented_iterator<C,U>& j); 50349e08aacStbbdev 50449e08aacStbbdev template<typename C, typename U> 50549e08aacStbbdev friend class segmented_iterator; 50649e08aacStbbdev 50749e08aacStbbdev public: 50849e08aacStbbdev segmented_iterator() {my_segcont = nullptr;} 50949e08aacStbbdev 51049e08aacStbbdev segmented_iterator( const SegmentedContainer& _segmented_container ) : 51149e08aacStbbdev my_segcont(const_cast<SegmentedContainer*>(&_segmented_container)), 51249e08aacStbbdev outer_iter(my_segcont->end()) { } 51349e08aacStbbdev 51449e08aacStbbdev ~segmented_iterator() {} 51549e08aacStbbdev 51649e08aacStbbdev using InnerContainer = typename SegmentedContainer::value_type; 51749e08aacStbbdev using inner_iterator = typename InnerContainer::iterator; 51849e08aacStbbdev using outer_iterator = typename SegmentedContainer::iterator; 51949e08aacStbbdev 52049e08aacStbbdev // STL support 52149e08aacStbbdev // TODO: inherit all types from segmented container? 52249e08aacStbbdev using difference_type = std::ptrdiff_t; 52349e08aacStbbdev using value_type = Value; 52449e08aacStbbdev using size_type = typename SegmentedContainer::size_type; 52549e08aacStbbdev using pointer = Value*; 52649e08aacStbbdev using reference = Value&; 52749e08aacStbbdev using iterator_category = std::input_iterator_tag; 52849e08aacStbbdev 52949e08aacStbbdev // Copy Constructor 53049e08aacStbbdev template<typename U> 53149e08aacStbbdev segmented_iterator(const segmented_iterator<SegmentedContainer, U>& other) : 53249e08aacStbbdev my_segcont(other.my_segcont), 53349e08aacStbbdev outer_iter(other.outer_iter), 53449e08aacStbbdev // can we assign a default-constructed iterator to inner if we're at the end? 53549e08aacStbbdev inner_iter(other.inner_iter) 53649e08aacStbbdev {} 53749e08aacStbbdev 53849e08aacStbbdev // assignment 53949e08aacStbbdev template<typename U> 54049e08aacStbbdev segmented_iterator& operator=( const segmented_iterator<SegmentedContainer, U>& other) { 54149e08aacStbbdev my_segcont = other.my_segcont; 54249e08aacStbbdev outer_iter = other.outer_iter; 54349e08aacStbbdev if(outer_iter != my_segcont->end()) inner_iter = other.inner_iter; 54449e08aacStbbdev return *this; 54549e08aacStbbdev } 54649e08aacStbbdev 54749e08aacStbbdev // allow assignment of outer iterator to segmented iterator. Once it is 54849e08aacStbbdev // assigned, move forward until a non-empty inner container is found or 54949e08aacStbbdev // the end of the outer container is reached. 55049e08aacStbbdev segmented_iterator& operator=(const outer_iterator& new_outer_iter) { 55157f524caSIlya Isaev __TBB_ASSERT(my_segcont != nullptr, nullptr); 55249e08aacStbbdev // check that this iterator points to something inside the segmented container 55349e08aacStbbdev for(outer_iter = new_outer_iter ;outer_iter!=my_segcont->end(); ++outer_iter) { 55449e08aacStbbdev if( !outer_iter->empty() ) { 55549e08aacStbbdev inner_iter = outer_iter->begin(); 55649e08aacStbbdev break; 55749e08aacStbbdev } 55849e08aacStbbdev } 55949e08aacStbbdev return *this; 56049e08aacStbbdev } 56149e08aacStbbdev 56249e08aacStbbdev // pre-increment 56349e08aacStbbdev segmented_iterator& operator++() { 56449e08aacStbbdev advance_me(); 56549e08aacStbbdev return *this; 56649e08aacStbbdev } 56749e08aacStbbdev 56849e08aacStbbdev // post-increment 56949e08aacStbbdev segmented_iterator operator++(int) { 57049e08aacStbbdev segmented_iterator tmp = *this; 57149e08aacStbbdev operator++(); 57249e08aacStbbdev return tmp; 57349e08aacStbbdev } 57449e08aacStbbdev 57549e08aacStbbdev bool operator==(const outer_iterator& other_outer) const { 57657f524caSIlya Isaev __TBB_ASSERT(my_segcont != nullptr, nullptr); 57749e08aacStbbdev return (outer_iter == other_outer && 57849e08aacStbbdev (outer_iter == my_segcont->end() || inner_iter == outer_iter->begin())); 57949e08aacStbbdev } 58049e08aacStbbdev 58149e08aacStbbdev bool operator!=(const outer_iterator& other_outer) const { 58249e08aacStbbdev return !operator==(other_outer); 58349e08aacStbbdev 58449e08aacStbbdev } 58549e08aacStbbdev 58649e08aacStbbdev // (i)* RHS 58749e08aacStbbdev reference operator*() const { 58857f524caSIlya Isaev __TBB_ASSERT(my_segcont != nullptr, nullptr); 58949e08aacStbbdev __TBB_ASSERT(outer_iter != my_segcont->end(), "Dereferencing a pointer at end of container"); 59057f524caSIlya Isaev __TBB_ASSERT(inner_iter != outer_iter->end(), nullptr); // should never happen 59149e08aacStbbdev return *inner_iter; 59249e08aacStbbdev } 59349e08aacStbbdev 59449e08aacStbbdev // i-> 59549e08aacStbbdev pointer operator->() const { return &operator*();} 59649e08aacStbbdev 59749e08aacStbbdev private: 59849e08aacStbbdev SegmentedContainer* my_segcont; 59949e08aacStbbdev outer_iterator outer_iter; 60049e08aacStbbdev inner_iterator inner_iter; 60149e08aacStbbdev 60249e08aacStbbdev void advance_me() { 60357f524caSIlya Isaev __TBB_ASSERT(my_segcont != nullptr, nullptr); 60457f524caSIlya Isaev __TBB_ASSERT(outer_iter != my_segcont->end(), nullptr); // not true if there are no inner containers 60557f524caSIlya Isaev __TBB_ASSERT(inner_iter != outer_iter->end(), nullptr); // not true if the inner containers are all empty. 60649e08aacStbbdev ++inner_iter; 60749e08aacStbbdev while(inner_iter == outer_iter->end() && ++outer_iter != my_segcont->end()) { 60849e08aacStbbdev inner_iter = outer_iter->begin(); 60949e08aacStbbdev } 61049e08aacStbbdev } 61149e08aacStbbdev }; // segmented_iterator 61249e08aacStbbdev 61349e08aacStbbdev template<typename SegmentedContainer, typename T, typename U> 61449e08aacStbbdev bool operator==( const segmented_iterator<SegmentedContainer,T>& i, 61549e08aacStbbdev const segmented_iterator<SegmentedContainer,U>& j ) { 61649e08aacStbbdev if(i.my_segcont != j.my_segcont) return false; 61749e08aacStbbdev if(i.my_segcont == nullptr) return true; 61849e08aacStbbdev if(i.outer_iter != j.outer_iter) return false; 61949e08aacStbbdev if(i.outer_iter == i.my_segcont->end()) return true; 62049e08aacStbbdev return i.inner_iter == j.inner_iter; 62149e08aacStbbdev } 62249e08aacStbbdev 62349e08aacStbbdev // != 62449e08aacStbbdev template<typename SegmentedContainer, typename T, typename U> 62549e08aacStbbdev bool operator!=( const segmented_iterator<SegmentedContainer,T>& i, 62649e08aacStbbdev const segmented_iterator<SegmentedContainer,U>& j ) { 62749e08aacStbbdev return !(i==j); 62849e08aacStbbdev } 62949e08aacStbbdev 63049e08aacStbbdev template<typename T> 63149e08aacStbbdev struct construct_by_default: no_assign { 63249e08aacStbbdev void construct(void*where) {new(where) T();} // C++ note: the () in T() ensure zero initialization. 63349e08aacStbbdev construct_by_default( int ) {} 63449e08aacStbbdev }; 63549e08aacStbbdev 63649e08aacStbbdev template<typename T> 63749e08aacStbbdev struct construct_by_exemplar: no_assign { 63849e08aacStbbdev const T exemplar; 63949e08aacStbbdev void construct(void*where) {new(where) T(exemplar);} 64049e08aacStbbdev construct_by_exemplar( const T& t ) : exemplar(t) {} 64149e08aacStbbdev construct_by_exemplar( T&& t ) : exemplar(std::move(t)) {} 64249e08aacStbbdev }; 64349e08aacStbbdev 64449e08aacStbbdev template<typename T, typename Finit> 64549e08aacStbbdev struct construct_by_finit: no_assign { 64649e08aacStbbdev Finit f; 64749e08aacStbbdev void construct(void* where) {new(where) T(f());} 64849e08aacStbbdev construct_by_finit( Finit&& f_ ) : f(std::move(f_)) {} 64949e08aacStbbdev }; 65049e08aacStbbdev 65149e08aacStbbdev template<typename T, typename... P> 65249e08aacStbbdev struct construct_by_args: no_assign { 65349e08aacStbbdev stored_pack<P...> pack; 65449e08aacStbbdev void construct(void* where) { 65549e08aacStbbdev call( [where](const typename std::decay<P>::type&... args ){ 65649e08aacStbbdev new(where) T(args...); 65749e08aacStbbdev }, pack ); 65849e08aacStbbdev } 65949e08aacStbbdev construct_by_args( P&& ... args ) : pack(std::forward<P>(args)...) {} 66049e08aacStbbdev }; 66149e08aacStbbdev 66249e08aacStbbdev // storage for initialization function pointer 66349e08aacStbbdev // TODO: consider removing the template parameter T here and in callback_leaf 66449e08aacStbbdev class callback_base { 66549e08aacStbbdev public: 66649e08aacStbbdev // Clone *this 66749e08aacStbbdev virtual callback_base* clone() const = 0; 66849e08aacStbbdev // Destruct and free *this 66949e08aacStbbdev virtual void destroy() = 0; 67049e08aacStbbdev // Need virtual destructor to satisfy GCC compiler warning 67149e08aacStbbdev virtual ~callback_base() { } 67249e08aacStbbdev // Construct T at where 67349e08aacStbbdev virtual void construct(void* where) = 0; 67449e08aacStbbdev }; 67549e08aacStbbdev 67649e08aacStbbdev template <typename Constructor> 67749e08aacStbbdev class callback_leaf: public callback_base, Constructor { 67849e08aacStbbdev template<typename... P> callback_leaf( P&& ... params ) : Constructor(std::forward<P>(params)...) {} 67949e08aacStbbdev // TODO: make the construction/destruction consistent (use allocator.construct/destroy) 68049e08aacStbbdev using my_allocator_type = typename tbb::tbb_allocator<callback_leaf>; 68149e08aacStbbdev 68249e08aacStbbdev callback_base* clone() const override { 68349e08aacStbbdev return make(*this); 68449e08aacStbbdev } 68549e08aacStbbdev 68649e08aacStbbdev void destroy() override { 68749e08aacStbbdev my_allocator_type alloc; 68849e08aacStbbdev tbb::detail::allocator_traits<my_allocator_type>::destroy(alloc, this); 68949e08aacStbbdev tbb::detail::allocator_traits<my_allocator_type>::deallocate(alloc, this, 1); 69049e08aacStbbdev } 69149e08aacStbbdev 69249e08aacStbbdev void construct(void* where) override { 69349e08aacStbbdev Constructor::construct(where); 69449e08aacStbbdev } 69549e08aacStbbdev 69649e08aacStbbdev public: 69749e08aacStbbdev template<typename... P> 69849e08aacStbbdev static callback_base* make( P&& ... params ) { 69949e08aacStbbdev void* where = my_allocator_type().allocate(1); 70049e08aacStbbdev return new(where) callback_leaf( std::forward<P>(params)... ); 70149e08aacStbbdev } 70249e08aacStbbdev }; 70349e08aacStbbdev 70449e08aacStbbdev //! Template for recording construction of objects in table 70549e08aacStbbdev /** All maintenance of the space will be done explicitly on push_back, 70649e08aacStbbdev and all thread local copies must be destroyed before the concurrent 70749e08aacStbbdev vector is deleted. 70849e08aacStbbdev 70949e08aacStbbdev The flag is_built is initialized to false. When the local is 71049e08aacStbbdev successfully-constructed, set the flag to true or call value_committed(). 71149e08aacStbbdev If the constructor throws, the flag will be false. 71249e08aacStbbdev */ 71349e08aacStbbdev template<typename U> 71449e08aacStbbdev struct ets_element { 71549e08aacStbbdev detail::aligned_space<U> my_space; 71649e08aacStbbdev bool is_built; 71749e08aacStbbdev ets_element() { is_built = false; } // not currently-built 71849e08aacStbbdev U* value() { return my_space.begin(); } 71949e08aacStbbdev U* value_committed() { is_built = true; return my_space.begin(); } 72049e08aacStbbdev ~ets_element() { 72149e08aacStbbdev if(is_built) { 72249e08aacStbbdev my_space.begin()->~U(); 72349e08aacStbbdev is_built = false; 72449e08aacStbbdev } 72549e08aacStbbdev } 72649e08aacStbbdev }; 72749e08aacStbbdev 72849e08aacStbbdev // A predicate that can be used for a compile-time compatibility check of ETS instances 72949e08aacStbbdev // Ideally, it should have been declared inside the ETS class, but unfortunately 73049e08aacStbbdev // in that case VS2013 does not enable the variadic constructor. 73149e08aacStbbdev template<typename T, typename ETS> struct is_compatible_ets : std::false_type {}; 73249e08aacStbbdev template<typename T, typename U, typename A, ets_key_usage_type C> 73349e08aacStbbdev struct is_compatible_ets< T, enumerable_thread_specific<U,A,C> > : std::is_same<T, U> {}; 73449e08aacStbbdev 73549e08aacStbbdev // A predicate that checks whether, for a variable 'foo' of type T, foo() is a valid expression 73649e08aacStbbdev template <typename T> using has_empty_braces_operator = decltype(std::declval<T>()()); 73749e08aacStbbdev template <typename T> using is_callable_no_args = supports<T, has_empty_braces_operator>; 73849e08aacStbbdev 73949e08aacStbbdev //! The enumerable_thread_specific container 74049e08aacStbbdev /** enumerable_thread_specific has the following properties: 74149e08aacStbbdev - thread-local copies are lazily created, with default, exemplar or function initialization. 74249e08aacStbbdev - thread-local copies do not move (during lifetime, and excepting clear()) so the address of a copy is invariant. 74349e08aacStbbdev - the contained objects need not have operator=() defined if combine is not used. 74449e08aacStbbdev - enumerable_thread_specific containers may be copy-constructed or assigned. 74549e08aacStbbdev - thread-local copies can be managed by hash-table, or can be accessed via TLS storage for speed. 74649e08aacStbbdev - outside of parallel contexts, the contents of all thread-local copies are accessible by iterator or using combine or combine_each methods 74749e08aacStbbdev 74849e08aacStbbdev @par Segmented iterator 74949e08aacStbbdev When the thread-local objects are containers with input_iterators defined, a segmented iterator may 75049e08aacStbbdev be used to iterate over all the elements of all thread-local copies. 75149e08aacStbbdev 75249e08aacStbbdev @par combine and combine_each 75349e08aacStbbdev - Both methods are defined for enumerable_thread_specific. 75449e08aacStbbdev - combine() requires the type T have operator=() defined. 75549e08aacStbbdev - neither method modifies the contents of the object (though there is no guarantee that the applied methods do not modify the object.) 75649e08aacStbbdev - Both are evaluated in serial context (the methods are assumed to be non-benign.) 75749e08aacStbbdev 75849e08aacStbbdev @ingroup containers */ 75949e08aacStbbdev template <typename T, typename Allocator=cache_aligned_allocator<T>, 76049e08aacStbbdev ets_key_usage_type ETS_key_type=ets_no_key > 76149e08aacStbbdev class enumerable_thread_specific: ets_base<ETS_key_type> { 76249e08aacStbbdev 76349e08aacStbbdev template<typename U, typename A, ets_key_usage_type C> friend class enumerable_thread_specific; 76449e08aacStbbdev 76549e08aacStbbdev using padded_element = padded<ets_element<T>>; 76649e08aacStbbdev 76749e08aacStbbdev //! A generic range, used to create range objects from the iterators 76849e08aacStbbdev template<typename I> 76949e08aacStbbdev class generic_range_type: public blocked_range<I> { 77049e08aacStbbdev public: 77149e08aacStbbdev using value_type = T; 77249e08aacStbbdev using reference = T&; 77349e08aacStbbdev using const_reference = const T&; 77449e08aacStbbdev using iterator = I; 77549e08aacStbbdev using difference_type = std::ptrdiff_t; 77649e08aacStbbdev 77749e08aacStbbdev generic_range_type( I begin_, I end_, std::size_t grainsize_ = 1) : blocked_range<I>(begin_,end_,grainsize_) {} 77849e08aacStbbdev template<typename U> 77949e08aacStbbdev generic_range_type( const generic_range_type<U>& r) : blocked_range<I>(r.begin(),r.end(),r.grainsize()) {} 78049e08aacStbbdev generic_range_type( generic_range_type& r, split ) : blocked_range<I>(r,split()) {} 78149e08aacStbbdev }; 78249e08aacStbbdev 78349e08aacStbbdev using allocator_traits_type = tbb::detail::allocator_traits<Allocator>; 78449e08aacStbbdev 78549e08aacStbbdev using padded_allocator_type = typename allocator_traits_type::template rebind_alloc<padded_element>; 78649e08aacStbbdev using internal_collection_type = tbb::concurrent_vector< padded_element, padded_allocator_type >; 78749e08aacStbbdev 78849e08aacStbbdev callback_base *my_construct_callback; 78949e08aacStbbdev 79049e08aacStbbdev internal_collection_type my_locals; 79149e08aacStbbdev 79249e08aacStbbdev // TODO: consider unifying the callback mechanism for all create_local* methods below 79349e08aacStbbdev // (likely non-compatible and requires interface version increase) 79449e08aacStbbdev void* create_local() override { 79549e08aacStbbdev padded_element& lref = *my_locals.grow_by(1); 79649e08aacStbbdev my_construct_callback->construct(lref.value()); 79749e08aacStbbdev return lref.value_committed(); 79849e08aacStbbdev } 79949e08aacStbbdev 80049e08aacStbbdev static void* create_local_by_copy( ets_base<ETS_key_type>& base, void* p ) { 80149e08aacStbbdev enumerable_thread_specific& ets = static_cast<enumerable_thread_specific&>(base); 80249e08aacStbbdev padded_element& lref = *ets.my_locals.grow_by(1); 80349e08aacStbbdev new(lref.value()) T(*static_cast<T*>(p)); 80449e08aacStbbdev return lref.value_committed(); 80549e08aacStbbdev } 80649e08aacStbbdev 80749e08aacStbbdev static void* create_local_by_move( ets_base<ETS_key_type>& base, void* p ) { 80849e08aacStbbdev enumerable_thread_specific& ets = static_cast<enumerable_thread_specific&>(base); 80949e08aacStbbdev padded_element& lref = *ets.my_locals.grow_by(1); 81049e08aacStbbdev new(lref.value()) T(std::move(*static_cast<T*>(p))); 81149e08aacStbbdev return lref.value_committed(); 81249e08aacStbbdev } 81349e08aacStbbdev 81449e08aacStbbdev using array_allocator_type = typename allocator_traits_type::template rebind_alloc<uintptr_t>; 81549e08aacStbbdev 81649e08aacStbbdev // _size is in bytes 81749e08aacStbbdev void* create_array(std::size_t _size) override { 81849e08aacStbbdev std::size_t nelements = (_size + sizeof(uintptr_t) -1) / sizeof(uintptr_t); 81949e08aacStbbdev return array_allocator_type().allocate(nelements); 82049e08aacStbbdev } 82149e08aacStbbdev 82249e08aacStbbdev void free_array( void* _ptr, std::size_t _size) override { 82349e08aacStbbdev std::size_t nelements = (_size + sizeof(uintptr_t) -1) / sizeof(uintptr_t); 82449e08aacStbbdev array_allocator_type().deallocate( reinterpret_cast<uintptr_t *>(_ptr),nelements); 82549e08aacStbbdev } 82649e08aacStbbdev 82749e08aacStbbdev public: 82849e08aacStbbdev 82949e08aacStbbdev //! Basic types 83049e08aacStbbdev using value_type = T; 83149e08aacStbbdev using allocator_type = Allocator; 83249e08aacStbbdev using size_type = typename internal_collection_type::size_type; 83349e08aacStbbdev using difference_type = typename internal_collection_type::difference_type; 83449e08aacStbbdev using reference = value_type&; 83549e08aacStbbdev using const_reference = const value_type&; 83649e08aacStbbdev 83749e08aacStbbdev using pointer = typename allocator_traits_type::pointer; 83849e08aacStbbdev using const_pointer = typename allocator_traits_type::const_pointer; 83949e08aacStbbdev 84049e08aacStbbdev // Iterator types 84149e08aacStbbdev using iterator = enumerable_thread_specific_iterator<internal_collection_type, value_type>; 84249e08aacStbbdev using const_iterator = enumerable_thread_specific_iterator<internal_collection_type, const value_type>; 84349e08aacStbbdev 84449e08aacStbbdev // Parallel range types 84549e08aacStbbdev using range_type = generic_range_type<iterator>; 84649e08aacStbbdev using const_range_type = generic_range_type<const_iterator>; 84749e08aacStbbdev 84849e08aacStbbdev //! Default constructor. Each local instance of T is default constructed. 84949e08aacStbbdev enumerable_thread_specific() : my_construct_callback( 85049e08aacStbbdev callback_leaf<construct_by_default<T> >::make(/*dummy argument*/0) 85149e08aacStbbdev ){} 85249e08aacStbbdev 85349e08aacStbbdev //! Constructor with initializer functor. Each local instance of T is constructed by T(finit()). 85449e08aacStbbdev template <typename Finit , typename = typename std::enable_if<is_callable_no_args<typename std::decay<Finit>::type>::value>::type> 85549e08aacStbbdev explicit enumerable_thread_specific( Finit finit ) : my_construct_callback( 85649e08aacStbbdev callback_leaf<construct_by_finit<T,Finit> >::make( std::move(finit) ) 85749e08aacStbbdev ){} 85849e08aacStbbdev 85949e08aacStbbdev //! Constructor with exemplar. Each local instance of T is copy-constructed from the exemplar. 86049e08aacStbbdev explicit enumerable_thread_specific( const T& exemplar ) : my_construct_callback( 86149e08aacStbbdev callback_leaf<construct_by_exemplar<T> >::make( exemplar ) 86249e08aacStbbdev ){} 86349e08aacStbbdev 86449e08aacStbbdev explicit enumerable_thread_specific( T&& exemplar ) : my_construct_callback( 86549e08aacStbbdev callback_leaf<construct_by_exemplar<T> >::make( std::move(exemplar) ) 86649e08aacStbbdev ){} 86749e08aacStbbdev 86849e08aacStbbdev //! Variadic constructor with initializer arguments. Each local instance of T is constructed by T(args...) 86949e08aacStbbdev template <typename P1, typename... P, 87049e08aacStbbdev typename = typename std::enable_if<!is_callable_no_args<typename std::decay<P1>::type>::value 87149e08aacStbbdev && !is_compatible_ets<T, typename std::decay<P1>::type>::value 87249e08aacStbbdev && !std::is_same<T, typename std::decay<P1>::type>::value 87349e08aacStbbdev >::type> 87449e08aacStbbdev enumerable_thread_specific( P1&& arg1, P&& ... args ) : my_construct_callback( 87549e08aacStbbdev callback_leaf<construct_by_args<T,P1,P...> >::make( std::forward<P1>(arg1), std::forward<P>(args)... ) 87649e08aacStbbdev ){} 87749e08aacStbbdev 87849e08aacStbbdev //! Destructor 87949e08aacStbbdev ~enumerable_thread_specific() { 88049e08aacStbbdev if(my_construct_callback) my_construct_callback->destroy(); 88149e08aacStbbdev // Deallocate the hash table before overridden free_array() becomes inaccessible 88249e08aacStbbdev this->ets_base<ETS_key_type>::table_clear(); 88349e08aacStbbdev } 88449e08aacStbbdev 88549e08aacStbbdev //! returns reference to local, discarding exists 88649e08aacStbbdev reference local() { 88749e08aacStbbdev bool exists; 88849e08aacStbbdev return local(exists); 88949e08aacStbbdev } 89049e08aacStbbdev 89149e08aacStbbdev //! Returns reference to calling thread's local copy, creating one if necessary 89249e08aacStbbdev reference local(bool& exists) { 89349e08aacStbbdev void* ptr = this->table_lookup(exists); 89449e08aacStbbdev return *(T*)ptr; 89549e08aacStbbdev } 89649e08aacStbbdev 89749e08aacStbbdev //! Get the number of local copies 89849e08aacStbbdev size_type size() const { return my_locals.size(); } 89949e08aacStbbdev 90049e08aacStbbdev //! true if there have been no local copies created 90149e08aacStbbdev bool empty() const { return my_locals.empty(); } 90249e08aacStbbdev 90349e08aacStbbdev //! begin iterator 90449e08aacStbbdev iterator begin() { return iterator( my_locals, 0 ); } 90549e08aacStbbdev //! end iterator 90649e08aacStbbdev iterator end() { return iterator(my_locals, my_locals.size() ); } 90749e08aacStbbdev 90849e08aacStbbdev //! begin const iterator 90949e08aacStbbdev const_iterator begin() const { return const_iterator(my_locals, 0); } 91049e08aacStbbdev 91149e08aacStbbdev //! end const iterator 91249e08aacStbbdev const_iterator end() const { return const_iterator(my_locals, my_locals.size()); } 91349e08aacStbbdev 91449e08aacStbbdev //! Get range for parallel algorithms 91549e08aacStbbdev range_type range( std::size_t grainsize=1 ) { return range_type( begin(), end(), grainsize ); } 91649e08aacStbbdev 91749e08aacStbbdev //! Get const range for parallel algorithms 91849e08aacStbbdev const_range_type range( std::size_t grainsize=1 ) const { return const_range_type( begin(), end(), grainsize ); } 91949e08aacStbbdev 92049e08aacStbbdev //! Destroys local copies 92149e08aacStbbdev void clear() { 92249e08aacStbbdev my_locals.clear(); 92349e08aacStbbdev this->table_clear(); 92449e08aacStbbdev // callback is not destroyed 92549e08aacStbbdev } 92649e08aacStbbdev 92749e08aacStbbdev private: 92849e08aacStbbdev template<typename A2, ets_key_usage_type C2> 92949e08aacStbbdev void internal_copy(const enumerable_thread_specific<T, A2, C2>& other) { 93049e08aacStbbdev // this tests is_compatible_ets 93149e08aacStbbdev static_assert( (is_compatible_ets<T, typename std::decay<decltype(other)>::type>::value), "is_compatible_ets fails" ); 93249e08aacStbbdev // Initialize my_construct_callback first, so that it is valid even if rest of this routine throws an exception. 93349e08aacStbbdev my_construct_callback = other.my_construct_callback->clone(); 93457f524caSIlya Isaev __TBB_ASSERT(my_locals.size()==0, nullptr); 93549e08aacStbbdev my_locals.reserve(other.size()); 93649e08aacStbbdev this->table_elementwise_copy( other, create_local_by_copy ); 93749e08aacStbbdev } 93849e08aacStbbdev 93949e08aacStbbdev void internal_swap(enumerable_thread_specific& other) { 94049e08aacStbbdev using std::swap; 94157f524caSIlya Isaev __TBB_ASSERT( this!=&other, nullptr); 94249e08aacStbbdev swap(my_construct_callback, other.my_construct_callback); 94349e08aacStbbdev // concurrent_vector::swap() preserves storage space, 94449e08aacStbbdev // so addresses to the vector kept in ETS hash table remain valid. 94549e08aacStbbdev swap(my_locals, other.my_locals); 94649e08aacStbbdev this->ets_base<ETS_key_type>::table_swap(other); 94749e08aacStbbdev } 94849e08aacStbbdev 94949e08aacStbbdev template<typename A2, ets_key_usage_type C2> 95049e08aacStbbdev void internal_move(enumerable_thread_specific<T, A2, C2>&& other) { 95149e08aacStbbdev static_assert( (is_compatible_ets<T, typename std::decay<decltype(other)>::type>::value), "is_compatible_ets fails" ); 95249e08aacStbbdev my_construct_callback = other.my_construct_callback; 95349e08aacStbbdev other.my_construct_callback = nullptr; 95457f524caSIlya Isaev __TBB_ASSERT(my_locals.size()==0, nullptr); 95549e08aacStbbdev my_locals.reserve(other.size()); 95649e08aacStbbdev this->table_elementwise_copy( other, create_local_by_move ); 95749e08aacStbbdev } 95849e08aacStbbdev 95949e08aacStbbdev public: 96049e08aacStbbdev enumerable_thread_specific( const enumerable_thread_specific& other ) 96149e08aacStbbdev : ets_base<ETS_key_type>() /* prevents GCC warnings with -Wextra */ 96249e08aacStbbdev { 96349e08aacStbbdev internal_copy(other); 96449e08aacStbbdev } 96549e08aacStbbdev 96649e08aacStbbdev template<typename Alloc, ets_key_usage_type Cachetype> 96749e08aacStbbdev enumerable_thread_specific( const enumerable_thread_specific<T, Alloc, Cachetype>& other ) 96849e08aacStbbdev { 96949e08aacStbbdev internal_copy(other); 97049e08aacStbbdev } 97149e08aacStbbdev 97249e08aacStbbdev enumerable_thread_specific( enumerable_thread_specific&& other ) : my_construct_callback() 97349e08aacStbbdev { 97449e08aacStbbdev // TODO: use internal_move correctly here 97549e08aacStbbdev internal_swap(other); 97649e08aacStbbdev } 97749e08aacStbbdev 97849e08aacStbbdev template<typename Alloc, ets_key_usage_type Cachetype> 97949e08aacStbbdev enumerable_thread_specific( enumerable_thread_specific<T, Alloc, Cachetype>&& other ) : my_construct_callback() 98049e08aacStbbdev { 98149e08aacStbbdev internal_move(std::move(other)); 98249e08aacStbbdev } 98349e08aacStbbdev 98449e08aacStbbdev enumerable_thread_specific& operator=( const enumerable_thread_specific& other ) 98549e08aacStbbdev { 98649e08aacStbbdev if( this != &other ) { 98749e08aacStbbdev this->clear(); 98849e08aacStbbdev my_construct_callback->destroy(); 98949e08aacStbbdev internal_copy( other ); 99049e08aacStbbdev } 99149e08aacStbbdev return *this; 99249e08aacStbbdev } 99349e08aacStbbdev 99449e08aacStbbdev template<typename Alloc, ets_key_usage_type Cachetype> 99549e08aacStbbdev enumerable_thread_specific& operator=( const enumerable_thread_specific<T, Alloc, Cachetype>& other ) 99649e08aacStbbdev { 99757f524caSIlya Isaev __TBB_ASSERT( static_cast<void*>(this)!=static_cast<const void*>(&other), nullptr); // Objects of different types 99849e08aacStbbdev this->clear(); 99949e08aacStbbdev my_construct_callback->destroy(); 100049e08aacStbbdev internal_copy(other); 100149e08aacStbbdev return *this; 100249e08aacStbbdev } 100349e08aacStbbdev 100449e08aacStbbdev enumerable_thread_specific& operator=( enumerable_thread_specific&& other ) 100549e08aacStbbdev { 100649e08aacStbbdev if( this != &other ) { 100749e08aacStbbdev // TODO: use internal_move correctly here 100849e08aacStbbdev internal_swap(other); 100949e08aacStbbdev } 101049e08aacStbbdev return *this; 101149e08aacStbbdev } 101249e08aacStbbdev 101349e08aacStbbdev template<typename Alloc, ets_key_usage_type Cachetype> 101449e08aacStbbdev enumerable_thread_specific& operator=( enumerable_thread_specific<T, Alloc, Cachetype>&& other ) 101549e08aacStbbdev { 101657f524caSIlya Isaev __TBB_ASSERT( static_cast<void*>(this)!=static_cast<const void*>(&other), nullptr); // Objects of different types 101749e08aacStbbdev this->clear(); 101849e08aacStbbdev my_construct_callback->destroy(); 101949e08aacStbbdev internal_move(std::move(other)); 102049e08aacStbbdev return *this; 102149e08aacStbbdev } 102249e08aacStbbdev 102349e08aacStbbdev // CombineFunc has signature T(T,T) or T(const T&, const T&) 102449e08aacStbbdev template <typename CombineFunc> 102549e08aacStbbdev T combine(CombineFunc f_combine) { 102649e08aacStbbdev if(begin() == end()) { 102749e08aacStbbdev ets_element<T> location; 102849e08aacStbbdev my_construct_callback->construct(location.value()); 102949e08aacStbbdev return *location.value_committed(); 103049e08aacStbbdev } 103149e08aacStbbdev const_iterator ci = begin(); 103249e08aacStbbdev T my_result = *ci; 103349e08aacStbbdev while(++ci != end()) 103449e08aacStbbdev my_result = f_combine( my_result, *ci ); 103549e08aacStbbdev return my_result; 103649e08aacStbbdev } 103749e08aacStbbdev 103849e08aacStbbdev // combine_func_t takes T by value or by [const] reference, and returns nothing 103949e08aacStbbdev template <typename CombineFunc> 104049e08aacStbbdev void combine_each(CombineFunc f_combine) { 104149e08aacStbbdev for(iterator ci = begin(); ci != end(); ++ci) { 104249e08aacStbbdev f_combine( *ci ); 104349e08aacStbbdev } 104449e08aacStbbdev } 104549e08aacStbbdev 104649e08aacStbbdev }; // enumerable_thread_specific 104749e08aacStbbdev 104849e08aacStbbdev template< typename Container > 104949e08aacStbbdev class flattened2d { 105049e08aacStbbdev // This intermediate typedef is to address issues with VC7.1 compilers 105149e08aacStbbdev using conval_type = typename Container::value_type; 105249e08aacStbbdev 105349e08aacStbbdev public: 105449e08aacStbbdev //! Basic types 105549e08aacStbbdev using size_type = typename conval_type::size_type; 105649e08aacStbbdev using difference_type = typename conval_type::difference_type; 105749e08aacStbbdev using allocator_type = typename conval_type::allocator_type; 105849e08aacStbbdev using value_type = typename conval_type::value_type; 105949e08aacStbbdev using reference = typename conval_type::reference; 106049e08aacStbbdev using const_reference = typename conval_type::const_reference; 106149e08aacStbbdev using pointer = typename conval_type::pointer; 106249e08aacStbbdev using const_pointer = typename conval_type::const_pointer; 106349e08aacStbbdev 106449e08aacStbbdev using iterator = segmented_iterator<Container, value_type>; 106549e08aacStbbdev using const_iterator = segmented_iterator<Container, const value_type>; 106649e08aacStbbdev 106749e08aacStbbdev flattened2d( const Container &c, typename Container::const_iterator b, typename Container::const_iterator e ) : 106849e08aacStbbdev my_container(const_cast<Container*>(&c)), my_begin(b), my_end(e) { } 106949e08aacStbbdev 107049e08aacStbbdev explicit flattened2d( const Container &c ) : 107149e08aacStbbdev my_container(const_cast<Container*>(&c)), my_begin(c.begin()), my_end(c.end()) { } 107249e08aacStbbdev 107349e08aacStbbdev iterator begin() { return iterator(*my_container) = my_begin; } 107449e08aacStbbdev iterator end() { return iterator(*my_container) = my_end; } 107549e08aacStbbdev const_iterator begin() const { return const_iterator(*my_container) = my_begin; } 107649e08aacStbbdev const_iterator end() const { return const_iterator(*my_container) = my_end; } 107749e08aacStbbdev 107849e08aacStbbdev size_type size() const { 107949e08aacStbbdev size_type tot_size = 0; 108049e08aacStbbdev for(typename Container::const_iterator i = my_begin; i != my_end; ++i) { 108149e08aacStbbdev tot_size += i->size(); 108249e08aacStbbdev } 108349e08aacStbbdev return tot_size; 108449e08aacStbbdev } 108549e08aacStbbdev 108649e08aacStbbdev private: 108749e08aacStbbdev Container *my_container; 108849e08aacStbbdev typename Container::const_iterator my_begin; 108949e08aacStbbdev typename Container::const_iterator my_end; 109049e08aacStbbdev }; 109149e08aacStbbdev 109249e08aacStbbdev template <typename Container> 109349e08aacStbbdev flattened2d<Container> flatten2d(const Container &c, const typename Container::const_iterator b, const typename Container::const_iterator e) { 109449e08aacStbbdev return flattened2d<Container>(c, b, e); 109549e08aacStbbdev } 109649e08aacStbbdev 109749e08aacStbbdev template <typename Container> 109849e08aacStbbdev flattened2d<Container> flatten2d(const Container &c) { 109949e08aacStbbdev return flattened2d<Container>(c); 110049e08aacStbbdev } 110149e08aacStbbdev 110249e08aacStbbdev } // namespace d1 110349e08aacStbbdev } // namespace detail 110449e08aacStbbdev 110549e08aacStbbdev inline namespace v1 { 110649e08aacStbbdev using detail::d1::enumerable_thread_specific; 110749e08aacStbbdev using detail::d1::flattened2d; 110849e08aacStbbdev using detail::d1::flatten2d; 110949e08aacStbbdev // ets enum keys 111049e08aacStbbdev using detail::d1::ets_key_usage_type; 111149e08aacStbbdev using detail::d1::ets_key_per_instance; 111249e08aacStbbdev using detail::d1::ets_no_key; 111349e08aacStbbdev #if __TBB_RESUMABLE_TASKS 111449e08aacStbbdev using detail::d1::ets_suspend_aware; 111549e08aacStbbdev #endif 111649e08aacStbbdev } // inline namespace v1 111749e08aacStbbdev 111849e08aacStbbdev } // namespace tbb 111949e08aacStbbdev 112049e08aacStbbdev #endif // __TBB_enumerable_thread_specific_H 112149e08aacStbbdev 1122