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