| _concurrent_queue_internal.h | | _concurrent_queue_internal.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 38 | | skipping to change at line 38 | |
| | | | |
| #ifndef __TBB_concurrent_queue_internal_H | | #ifndef __TBB_concurrent_queue_internal_H | |
| #define __TBB_concurrent_queue_internal_H | | #define __TBB_concurrent_queue_internal_H | |
| | | | |
| #include "tbb_stddef.h" | | #include "tbb_stddef.h" | |
| #include "tbb_machine.h" | | #include "tbb_machine.h" | |
| #include "atomic.h" | | #include "atomic.h" | |
| #include "spin_mutex.h" | | #include "spin_mutex.h" | |
| #include "cache_aligned_allocator.h" | | #include "cache_aligned_allocator.h" | |
| #include "tbb_exception.h" | | #include "tbb_exception.h" | |
|
| #include <iterator> | | | |
| #include <new> | | #include <new> | |
| | | | |
|
| | | #if !TBB_USE_EXCEPTIONS && _MSC_VER | |
| | | // Suppress "C++ exception handler used, but unwind semantics are not e | |
| | | nabled" warning in STL headers | |
| | | #pragma warning (push) | |
| | | #pragma warning (disable: 4530) | |
| | | #endif | |
| | | | |
| | | #include <iterator> | |
| | | | |
| | | #if !TBB_USE_EXCEPTIONS && _MSC_VER | |
| | | #pragma warning (pop) | |
| | | #endif | |
| | | | |
| namespace tbb { | | namespace tbb { | |
| | | | |
| #if !__TBB_TEMPLATE_FRIENDS_BROKEN | | #if !__TBB_TEMPLATE_FRIENDS_BROKEN | |
| | | | |
| // forward declaration | | // forward declaration | |
| namespace strict_ppl { | | namespace strict_ppl { | |
| template<typename T, typename A> class concurrent_queue; | | template<typename T, typename A> class concurrent_queue; | |
| } | | } | |
| | | | |
| template<typename T, typename A> class concurrent_bounded_queue; | | template<typename T, typename A> class concurrent_bounded_queue; | |
| | | | |
| skipping to change at line 67 | | skipping to change at line 78 | |
| //! For internal use only. | | //! For internal use only. | |
| namespace strict_ppl { | | namespace strict_ppl { | |
| | | | |
| //! @cond INTERNAL | | //! @cond INTERNAL | |
| namespace internal { | | namespace internal { | |
| | | | |
| using namespace tbb::internal; | | using namespace tbb::internal; | |
| | | | |
| typedef size_t ticket; | | typedef size_t ticket; | |
| | | | |
|
| static void* invalid_page; | | | |
| | | | |
| template<typename T> class micro_queue ; | | template<typename T> class micro_queue ; | |
| template<typename T> class micro_queue_pop_finalizer ; | | template<typename T> class micro_queue_pop_finalizer ; | |
| template<typename T> class concurrent_queue_base_v3; | | template<typename T> class concurrent_queue_base_v3; | |
| | | | |
| //! parts of concurrent_queue_rep that do not have references to micro_queu
e | | //! parts of concurrent_queue_rep that do not have references to micro_queu
e | |
| /** | | /** | |
| * For internal use only. | | * For internal use only. | |
| */ | | */ | |
| struct concurrent_queue_rep_base : no_copy { | | struct concurrent_queue_rep_base : no_copy { | |
| template<typename T> friend class micro_queue; | | template<typename T> friend class micro_queue; | |
| | | | |
| skipping to change at line 112 | | skipping to change at line 121 | |
| | | | |
| //! Size of an item | | //! Size of an item | |
| size_t item_size; | | size_t item_size; | |
| | | | |
| //! number of invalid entries in the queue | | //! number of invalid entries in the queue | |
| atomic<size_t> n_invalid_entries; | | atomic<size_t> n_invalid_entries; | |
| | | | |
| char pad3[NFS_MaxLineSize-sizeof(size_t)-sizeof(size_t)-sizeof(atomic<s
ize_t>)]; | | char pad3[NFS_MaxLineSize-sizeof(size_t)-sizeof(size_t)-sizeof(atomic<s
ize_t>)]; | |
| } ; | | } ; | |
| | | | |
|
| | | inline bool is_valid_page(const concurrent_queue_rep_base::page* p) { | |
| | | return uintptr_t(p)>1; | |
| | | } | |
| | | | |
| //! Abstract class to define interface for page allocation/deallocation | | //! Abstract class to define interface for page allocation/deallocation | |
| /** | | /** | |
| * For internal use only. | | * For internal use only. | |
| */ | | */ | |
| class concurrent_queue_page_allocator | | class concurrent_queue_page_allocator | |
| { | | { | |
| template<typename T> friend class micro_queue ; | | template<typename T> friend class micro_queue ; | |
| template<typename T> friend class micro_queue_pop_finalizer ; | | template<typename T> friend class micro_queue_pop_finalizer ; | |
| protected: | | protected: | |
| virtual ~concurrent_queue_page_allocator() {} | | virtual ~concurrent_queue_page_allocator() {} | |
| | | | |
| skipping to change at line 148 | | skipping to change at line 161 | |
| typedef concurrent_queue_rep_base::page page; | | typedef concurrent_queue_rep_base::page page; | |
| | | | |
| //! Class used to ensure exception-safety of method "pop" | | //! Class used to ensure exception-safety of method "pop" | |
| class destroyer: no_copy { | | class destroyer: no_copy { | |
| T& my_value; | | T& my_value; | |
| public: | | public: | |
| destroyer( T& value ) : my_value(value) {} | | destroyer( T& value ) : my_value(value) {} | |
| ~destroyer() {my_value.~T();} | | ~destroyer() {my_value.~T();} | |
| }; | | }; | |
| | | | |
|
| T& get_ref( page& page, size_t index ) { | | | |
| return static_cast<T*>(static_cast<void*>(&page+1))[index]; | | | |
| } | | | |
| | | | |
| void copy_item( page& dst, size_t index, const void* src ) { | | void copy_item( page& dst, size_t index, const void* src ) { | |
| new( &get_ref(dst,index) ) T(*static_cast<const T*>(src)); | | new( &get_ref(dst,index) ) T(*static_cast<const T*>(src)); | |
| } | | } | |
| | | | |
| void copy_item( page& dst, size_t dindex, const page& src, size_t sinde
x ) { | | void copy_item( page& dst, size_t dindex, const page& src, size_t sinde
x ) { | |
|
| new( &get_ref(dst,dindex) ) T( static_cast<const T*>(static_cast<co
nst void*>(&src+1))[sindex] ); | | new( &get_ref(dst,dindex) ) T( get_ref(const_cast<page&>(src),sinde
x) ); | |
| } | | } | |
| | | | |
| void assign_and_destroy_item( void* dst, page& src, size_t index ) { | | void assign_and_destroy_item( void* dst, page& src, size_t index ) { | |
| T& from = get_ref(src,index); | | T& from = get_ref(src,index); | |
| destroyer d(from); | | destroyer d(from); | |
| *static_cast<T*>(dst) = from; | | *static_cast<T*>(dst) = from; | |
| } | | } | |
| | | | |
| void spin_wait_until_my_turn( atomic<ticket>& counter, ticket k, concur
rent_queue_rep_base& rb ) const ; | | void spin_wait_until_my_turn( atomic<ticket>& counter, ticket k, concur
rent_queue_rep_base& rb ) const ; | |
| | | | |
| public: | | public: | |
| friend class micro_queue_pop_finalizer<T>; | | friend class micro_queue_pop_finalizer<T>; | |
| | | | |
|
| | | struct padded_page: page { | |
| | | //! Not defined anywhere - exists to quiet warnings. | |
| | | padded_page(); | |
| | | //! Not defined anywhere - exists to quiet warnings. | |
| | | void operator=( const padded_page& ); | |
| | | //! Must be last field. | |
| | | T last; | |
| | | }; | |
| | | | |
| | | static T& get_ref( page& p, size_t index ) { | |
| | | return (&static_cast<padded_page*>(static_cast<void*>(&p))->last)[i | |
| | | ndex]; | |
| | | } | |
| | | | |
| atomic<page*> head_page; | | atomic<page*> head_page; | |
| atomic<ticket> head_counter; | | atomic<ticket> head_counter; | |
| | | | |
| atomic<page*> tail_page; | | atomic<page*> tail_page; | |
| atomic<ticket> tail_counter; | | atomic<ticket> tail_counter; | |
| | | | |
| spin_mutex page_mutex; | | spin_mutex page_mutex; | |
| | | | |
| void push( const void* item, ticket k, concurrent_queue_base_v3<T>& bas
e ) ; | | void push( const void* item, ticket k, concurrent_queue_base_v3<T>& bas
e ) ; | |
| | | | |
| bool pop( void* dst, ticket k, concurrent_queue_base_v3<T>& base ) ; | | bool pop( void* dst, ticket k, concurrent_queue_base_v3<T>& base ) ; | |
| | | | |
| micro_queue& assign( const micro_queue& src, concurrent_queue_base_v3<T
>& base ) ; | | micro_queue& assign( const micro_queue& src, concurrent_queue_base_v3<T
>& base ) ; | |
| | | | |
| page* make_copy( concurrent_queue_base_v3<T>& base, const page* src_pag
e, size_t begin_in_page, size_t end_in_page, ticket& g_index ) ; | | page* make_copy( concurrent_queue_base_v3<T>& base, const page* src_pag
e, size_t begin_in_page, size_t end_in_page, ticket& g_index ) ; | |
| | | | |
|
| void make_invalid( ticket k ) ; | | void invalidate_page_and_rethrow( ticket k ) ; | |
| }; | | }; | |
| | | | |
| template<typename T> | | template<typename T> | |
| void micro_queue<T>::spin_wait_until_my_turn( atomic<ticket>& counter, tick
et k, concurrent_queue_rep_base& rb ) const { | | void micro_queue<T>::spin_wait_until_my_turn( atomic<ticket>& counter, tick
et k, concurrent_queue_rep_base& rb ) const { | |
| atomic_backoff backoff; | | atomic_backoff backoff; | |
| do { | | do { | |
| backoff.pause(); | | backoff.pause(); | |
|
| if( counter&0x1 ) { | | if( counter&1 ) { | |
| ++rb.n_invalid_entries; | | ++rb.n_invalid_entries; | |
|
| throw_bad_last_alloc_exception_v4(); | | throw_exception( eid_bad_last_alloc ); | |
| } | | } | |
| } while( counter!=k ) ; | | } while( counter!=k ) ; | |
| } | | } | |
| | | | |
| template<typename T> | | template<typename T> | |
| void micro_queue<T>::push( const void* item, ticket k, concurrent_queue_bas
e_v3<T>& base ) { | | void micro_queue<T>::push( const void* item, ticket k, concurrent_queue_bas
e_v3<T>& base ) { | |
| k &= -concurrent_queue_rep_base::n_queue; | | k &= -concurrent_queue_rep_base::n_queue; | |
| page* p = NULL; | | page* p = NULL; | |
| size_t index = k/concurrent_queue_rep_base::n_queue & (base.my_rep->ite
ms_per_page-1); | | size_t index = k/concurrent_queue_rep_base::n_queue & (base.my_rep->ite
ms_per_page-1); | |
| if( !index ) { | | if( !index ) { | |
|
| try { | | __TBB_TRY { | |
| concurrent_queue_page_allocator& pa = base; | | concurrent_queue_page_allocator& pa = base; | |
| p = pa.allocate_page(); | | p = pa.allocate_page(); | |
|
| } catch (...) { | | } __TBB_CATCH (...) { | |
| ++base.my_rep->n_invalid_entries; | | ++base.my_rep->n_invalid_entries; | |
|
| make_invalid( k ); | | invalidate_page_and_rethrow( k ); | |
| } | | } | |
| p->mask = 0; | | p->mask = 0; | |
| p->next = NULL; | | p->next = NULL; | |
| } | | } | |
| | | | |
| if( tail_counter!=k ) spin_wait_until_my_turn( tail_counter, k, *base.m
y_rep ); | | if( tail_counter!=k ) spin_wait_until_my_turn( tail_counter, k, *base.m
y_rep ); | |
| | | | |
| if( p ) { | | if( p ) { | |
| spin_mutex::scoped_lock lock( page_mutex ); | | spin_mutex::scoped_lock lock( page_mutex ); | |
|
| if( page* q = tail_page ) | | page* q = tail_page; | |
| | | if( is_valid_page(q) ) | |
| q->next = p; | | q->next = p; | |
| else | | else | |
| head_page = p; | | head_page = p; | |
| tail_page = p; | | tail_page = p; | |
| } else { | | } else { | |
| p = tail_page; | | p = tail_page; | |
| } | | } | |
| | | | |
|
| try { | | __TBB_TRY { | |
| copy_item( *p, index, item ); | | copy_item( *p, index, item ); | |
| // If no exception was thrown, mark item as present. | | // If no exception was thrown, mark item as present. | |
| p->mask |= uintptr_t(1)<<index; | | p->mask |= uintptr_t(1)<<index; | |
| tail_counter += concurrent_queue_rep_base::n_queue; | | tail_counter += concurrent_queue_rep_base::n_queue; | |
|
| } catch (...) { | | } __TBB_CATCH (...) { | |
| ++base.my_rep->n_invalid_entries; | | ++base.my_rep->n_invalid_entries; | |
| tail_counter += concurrent_queue_rep_base::n_queue; | | tail_counter += concurrent_queue_rep_base::n_queue; | |
|
| throw; | | __TBB_RETHROW(); | |
| } | | } | |
| } | | } | |
| | | | |
| template<typename T> | | template<typename T> | |
| bool micro_queue<T>::pop( void* dst, ticket k, concurrent_queue_base_v3<T>&
base ) { | | bool micro_queue<T>::pop( void* dst, ticket k, concurrent_queue_base_v3<T>&
base ) { | |
| k &= -concurrent_queue_rep_base::n_queue; | | k &= -concurrent_queue_rep_base::n_queue; | |
| if( head_counter!=k ) spin_wait_until_eq( head_counter, k ); | | if( head_counter!=k ) spin_wait_until_eq( head_counter, k ); | |
| if( tail_counter==k ) spin_wait_while_eq( tail_counter, k ); | | if( tail_counter==k ) spin_wait_while_eq( tail_counter, k ); | |
| page& p = *head_page; | | page& p = *head_page; | |
| __TBB_ASSERT( &p, NULL ); | | __TBB_ASSERT( &p, NULL ); | |
| | | | |
| skipping to change at line 272 | | skipping to change at line 295 | |
| return success; | | return success; | |
| } | | } | |
| | | | |
| template<typename T> | | template<typename T> | |
| micro_queue<T>& micro_queue<T>::assign( const micro_queue<T>& src, concurre
nt_queue_base_v3<T>& base ) { | | micro_queue<T>& micro_queue<T>::assign( const micro_queue<T>& src, concurre
nt_queue_base_v3<T>& base ) { | |
| head_counter = src.head_counter; | | head_counter = src.head_counter; | |
| tail_counter = src.tail_counter; | | tail_counter = src.tail_counter; | |
| page_mutex = src.page_mutex; | | page_mutex = src.page_mutex; | |
| | | | |
| const page* srcp = src.head_page; | | const page* srcp = src.head_page; | |
|
| if( srcp ) { | | if( is_valid_page(srcp) ) { | |
| ticket g_index = head_counter; | | ticket g_index = head_counter; | |
|
| try { | | __TBB_TRY { | |
| size_t n_items = (tail_counter-head_counter)/concurrent_queue_
rep_base::n_queue; | | size_t n_items = (tail_counter-head_counter)/concurrent_queue_
rep_base::n_queue; | |
| size_t index = head_counter/concurrent_queue_rep_base::n_queue
& (base.my_rep->items_per_page-1); | | size_t index = head_counter/concurrent_queue_rep_base::n_queue
& (base.my_rep->items_per_page-1); | |
| size_t end_in_first_page = (index+n_items<base.my_rep->items_pe
r_page)?(index+n_items):base.my_rep->items_per_page; | | size_t end_in_first_page = (index+n_items<base.my_rep->items_pe
r_page)?(index+n_items):base.my_rep->items_per_page; | |
| | | | |
| head_page = make_copy( base, srcp, index, end_in_first_page, g_
index ); | | head_page = make_copy( base, srcp, index, end_in_first_page, g_
index ); | |
| page* cur_page = head_page; | | page* cur_page = head_page; | |
| | | | |
| if( srcp != src.tail_page ) { | | if( srcp != src.tail_page ) { | |
| for( srcp = srcp->next; srcp!=src.tail_page; srcp=srcp->nex
t ) { | | for( srcp = srcp->next; srcp!=src.tail_page; srcp=srcp->nex
t ) { | |
| cur_page->next = make_copy( base, srcp, 0, base.my_rep-
>items_per_page, g_index ); | | cur_page->next = make_copy( base, srcp, 0, base.my_rep-
>items_per_page, g_index ); | |
| | | | |
| skipping to change at line 296 | | skipping to change at line 319 | |
| } | | } | |
| | | | |
| __TBB_ASSERT( srcp==src.tail_page, NULL ); | | __TBB_ASSERT( srcp==src.tail_page, NULL ); | |
| size_t last_index = tail_counter/concurrent_queue_rep_base:
:n_queue & (base.my_rep->items_per_page-1); | | size_t last_index = tail_counter/concurrent_queue_rep_base:
:n_queue & (base.my_rep->items_per_page-1); | |
| if( last_index==0 ) last_index = base.my_rep->items_per_pag
e; | | if( last_index==0 ) last_index = base.my_rep->items_per_pag
e; | |
| | | | |
| cur_page->next = make_copy( base, srcp, 0, last_index, g_in
dex ); | | cur_page->next = make_copy( base, srcp, 0, last_index, g_in
dex ); | |
| cur_page = cur_page->next; | | cur_page = cur_page->next; | |
| } | | } | |
| tail_page = cur_page; | | tail_page = cur_page; | |
|
| } catch (...) { | | } __TBB_CATCH (...) { | |
| make_invalid( g_index ); | | invalidate_page_and_rethrow( g_index ); | |
| } | | } | |
| } else { | | } else { | |
| head_page = tail_page = NULL; | | head_page = tail_page = NULL; | |
| } | | } | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| template<typename T> | | template<typename T> | |
|
| void micro_queue<T>::make_invalid( ticket k ) { | | void micro_queue<T>::invalidate_page_and_rethrow( ticket k ) { | |
| static page dummy = {static_cast<page*>((void*)1), 0}; | | // Append an invalid page at address 1 so that no more pushes are allow | |
| // mark it so that no more pushes are allowed. | | ed. | |
| invalid_page = &dummy; | | page* invalid_page = (page*)uintptr_t(1); | |
| { | | { | |
| spin_mutex::scoped_lock lock( page_mutex ); | | spin_mutex::scoped_lock lock( page_mutex ); | |
| tail_counter = k+concurrent_queue_rep_base::n_queue+1; | | tail_counter = k+concurrent_queue_rep_base::n_queue+1; | |
|
| if( page* q = tail_page ) | | page* q = tail_page; | |
| q->next = static_cast<page*>(invalid_page); | | if( is_valid_page(q) ) | |
| | | q->next = invalid_page; | |
| else | | else | |
|
| head_page = static_cast<page*>(invalid_page); | | head_page = invalid_page; | |
| tail_page = static_cast<page*>(invalid_page); | | tail_page = invalid_page; | |
| } | | } | |
|
| throw; | | __TBB_RETHROW(); | |
| } | | } | |
| | | | |
| template<typename T> | | template<typename T> | |
| concurrent_queue_rep_base::page* micro_queue<T>::make_copy( concurrent_queu
e_base_v3<T>& base, const concurrent_queue_rep_base::page* src_page, size_t
begin_in_page, size_t end_in_page, ticket& g_index ) { | | concurrent_queue_rep_base::page* micro_queue<T>::make_copy( concurrent_queu
e_base_v3<T>& base, const concurrent_queue_rep_base::page* src_page, size_t
begin_in_page, size_t end_in_page, ticket& g_index ) { | |
| concurrent_queue_page_allocator& pa = base; | | concurrent_queue_page_allocator& pa = base; | |
| page* new_page = pa.allocate_page(); | | page* new_page = pa.allocate_page(); | |
| new_page->next = NULL; | | new_page->next = NULL; | |
| new_page->mask = src_page->mask; | | new_page->mask = src_page->mask; | |
| for( ; begin_in_page!=end_in_page; ++begin_in_page, ++g_index ) | | for( ; begin_in_page!=end_in_page; ++begin_in_page, ++g_index ) | |
| if( new_page->mask & uintptr_t(1)<<begin_in_page ) | | if( new_page->mask & uintptr_t(1)<<begin_in_page ) | |
| | | | |
| skipping to change at line 351 | | skipping to change at line 374 | |
| public: | | public: | |
| micro_queue_pop_finalizer( micro_queue<T>& queue, concurrent_queue_base
_v3<T>& b, ticket k, page* p ) : | | micro_queue_pop_finalizer( micro_queue<T>& queue, concurrent_queue_base
_v3<T>& b, ticket k, page* p ) : | |
| my_ticket(k), my_queue(queue), my_page(p), allocator(b) | | my_ticket(k), my_queue(queue), my_page(p), allocator(b) | |
| {} | | {} | |
| ~micro_queue_pop_finalizer() ; | | ~micro_queue_pop_finalizer() ; | |
| }; | | }; | |
| | | | |
| template<typename T> | | template<typename T> | |
| micro_queue_pop_finalizer<T>::~micro_queue_pop_finalizer() { | | micro_queue_pop_finalizer<T>::~micro_queue_pop_finalizer() { | |
| page* p = my_page; | | page* p = my_page; | |
|
| if( p ) { | | if( is_valid_page(p) ) { | |
| spin_mutex::scoped_lock lock( my_queue.page_mutex ); | | spin_mutex::scoped_lock lock( my_queue.page_mutex ); | |
| page* q = p->next; | | page* q = p->next; | |
| my_queue.head_page = q; | | my_queue.head_page = q; | |
|
| if( !q ) { | | if( !is_valid_page(q) ) { | |
| my_queue.tail_page = NULL; | | my_queue.tail_page = NULL; | |
| } | | } | |
| } | | } | |
| my_queue.head_counter = my_ticket; | | my_queue.head_counter = my_ticket; | |
|
| if( p ) { | | if( is_valid_page(p) ) { | |
| allocator.deallocate_page( p ); | | allocator.deallocate_page( p ); | |
| } | | } | |
| } | | } | |
| | | | |
| #if _MSC_VER && !defined(__INTEL_COMPILER) | | #if _MSC_VER && !defined(__INTEL_COMPILER) | |
| #pragma warning( pop ) | | #pragma warning( pop ) | |
| #endif // warning 4146 is back | | #endif // warning 4146 is back | |
| | | | |
| template<typename T> class concurrent_queue_iterator_rep ; | | template<typename T> class concurrent_queue_iterator_rep ; | |
| template<typename T> class concurrent_queue_iterator_base_v3; | | template<typename T> class concurrent_queue_iterator_base_v3; | |
| | | | |
| skipping to change at line 410 | | skipping to change at line 433 | |
| | | | |
| friend struct concurrent_queue_rep<T>; | | friend struct concurrent_queue_rep<T>; | |
| friend class micro_queue<T>; | | friend class micro_queue<T>; | |
| friend class concurrent_queue_iterator_rep<T>; | | friend class concurrent_queue_iterator_rep<T>; | |
| friend class concurrent_queue_iterator_base_v3<T>; | | friend class concurrent_queue_iterator_base_v3<T>; | |
| | | | |
| protected: | | protected: | |
| typedef typename concurrent_queue_rep<T>::page page; | | typedef typename concurrent_queue_rep<T>::page page; | |
| | | | |
| private: | | private: | |
|
| | | typedef typename micro_queue<T>::padded_page padded_page; | |
| | | | |
| /* override */ virtual page *allocate_page() { | | /* override */ virtual page *allocate_page() { | |
| concurrent_queue_rep<T>& r = *my_rep; | | concurrent_queue_rep<T>& r = *my_rep; | |
|
| size_t n = sizeof(page) + r.items_per_page*r.item_size; | | size_t n = sizeof(padded_page) + (r.items_per_page-1)*sizeof(T); | |
| return reinterpret_cast<page*>(allocate_block ( n )); | | return reinterpret_cast<page*>(allocate_block ( n )); | |
| } | | } | |
| | | | |
| /* override */ virtual void deallocate_page( concurrent_queue_rep_base:
:page *p ) { | | /* override */ virtual void deallocate_page( concurrent_queue_rep_base:
:page *p ) { | |
| concurrent_queue_rep<T>& r = *my_rep; | | concurrent_queue_rep<T>& r = *my_rep; | |
|
| size_t n = sizeof(page) + r.items_per_page*r.item_size; | | size_t n = sizeof(padded_page) + (r.items_per_page-1)*sizeof(T); | |
| deallocate_block( reinterpret_cast<void*>(p), n ); | | deallocate_block( reinterpret_cast<void*>(p), n ); | |
| } | | } | |
| | | | |
| //! custom allocator | | //! custom allocator | |
| virtual void *allocate_block( size_t n ) = 0; | | virtual void *allocate_block( size_t n ) = 0; | |
| | | | |
| //! custom de-allocator | | //! custom de-allocator | |
| virtual void deallocate_block( void *p, size_t n ) = 0; | | virtual void deallocate_block( void *p, size_t n ) = 0; | |
| | | | |
| protected: | | protected: | |
|
| concurrent_queue_base_v3( size_t item_size ) ; | | concurrent_queue_base_v3(); | |
| | | | |
| /* override */ virtual ~concurrent_queue_base_v3() { | | /* override */ virtual ~concurrent_queue_base_v3() { | |
|
| | | #if __TBB_USE_ASSERT | |
| size_t nq = my_rep->n_queue; | | size_t nq = my_rep->n_queue; | |
| for( size_t i=0; i<nq; i++ ) | | for( size_t i=0; i<nq; i++ ) | |
| __TBB_ASSERT( my_rep->array[i].tail_page==NULL, "pages were not
freed properly" ); | | __TBB_ASSERT( my_rep->array[i].tail_page==NULL, "pages were not
freed properly" ); | |
|
| | | #endif /* __TBB_USE_ASSERT */ | |
| cache_aligned_allocator<concurrent_queue_rep<T> >().deallocate(my_r
ep,1); | | cache_aligned_allocator<concurrent_queue_rep<T> >().deallocate(my_r
ep,1); | |
| } | | } | |
| | | | |
| //! Enqueue item at tail of queue | | //! Enqueue item at tail of queue | |
| void internal_push( const void* src ) { | | void internal_push( const void* src ) { | |
| concurrent_queue_rep<T>& r = *my_rep; | | concurrent_queue_rep<T>& r = *my_rep; | |
| ticket k = r.tail_counter++; | | ticket k = r.tail_counter++; | |
| r.choose(k).push( src, k, *this ); | | r.choose(k).push( src, k, *this ); | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 459 | | skipping to change at line 486 | |
| //! Get size of queue; result may be invalid if queue is modified concu
rrently | | //! Get size of queue; result may be invalid if queue is modified concu
rrently | |
| size_t internal_size() const ; | | size_t internal_size() const ; | |
| | | | |
| //! check if the queue is empty; thread safe | | //! check if the queue is empty; thread safe | |
| bool internal_empty() const ; | | bool internal_empty() const ; | |
| | | | |
| //! free any remaining pages | | //! free any remaining pages | |
| /* note that the name may be misleading, but it remains so due to a his
torical accident. */ | | /* note that the name may be misleading, but it remains so due to a his
torical accident. */ | |
| void internal_finish_clear() ; | | void internal_finish_clear() ; | |
| | | | |
|
| //! throw an exception | | //! Obsolete | |
| void internal_throw_exception() const { | | void internal_throw_exception() const { | |
|
| throw std::bad_alloc(); | | throw_exception( eid_bad_alloc ); | |
| } | | } | |
| | | | |
| //! copy internal representation | | //! copy internal representation | |
| void assign( const concurrent_queue_base_v3& src ) ; | | void assign( const concurrent_queue_base_v3& src ) ; | |
| }; | | }; | |
| | | | |
| template<typename T> | | template<typename T> | |
|
| concurrent_queue_base_v3<T>::concurrent_queue_base_v3( size_t item_size ) { | | concurrent_queue_base_v3<T>::concurrent_queue_base_v3() { | |
| | | const size_t item_size = sizeof(T); | |
| my_rep = cache_aligned_allocator<concurrent_queue_rep<T> >().allocate(1
); | | my_rep = cache_aligned_allocator<concurrent_queue_rep<T> >().allocate(1
); | |
| __TBB_ASSERT( (size_t)my_rep % NFS_GetLineSize()==0, "alignment error"
); | | __TBB_ASSERT( (size_t)my_rep % NFS_GetLineSize()==0, "alignment error"
); | |
| __TBB_ASSERT( (size_t)&my_rep->head_counter % NFS_GetLineSize()==0, "al
ignment error" ); | | __TBB_ASSERT( (size_t)&my_rep->head_counter % NFS_GetLineSize()==0, "al
ignment error" ); | |
| __TBB_ASSERT( (size_t)&my_rep->tail_counter % NFS_GetLineSize()==0, "al
ignment error" ); | | __TBB_ASSERT( (size_t)&my_rep->tail_counter % NFS_GetLineSize()==0, "al
ignment error" ); | |
| __TBB_ASSERT( (size_t)&my_rep->array % NFS_GetLineSize()==0, "alignment
error" ); | | __TBB_ASSERT( (size_t)&my_rep->array % NFS_GetLineSize()==0, "alignment
error" ); | |
| memset(my_rep,0,sizeof(concurrent_queue_rep<T>)); | | memset(my_rep,0,sizeof(concurrent_queue_rep<T>)); | |
| my_rep->item_size = item_size; | | my_rep->item_size = item_size; | |
| my_rep->items_per_page = item_size<=8 ? 32 : | | my_rep->items_per_page = item_size<=8 ? 32 : | |
| item_size<=16 ? 16 : | | item_size<=16 ? 16 : | |
| item_size<=32 ? 8 : | | item_size<=32 ? 8 : | |
| | | | |
| skipping to change at line 541 | | skipping to change at line 569 | |
| // if tc!=r.tail_counter, the queue was not empty at some point between
the two reads. | | // if tc!=r.tail_counter, the queue was not empty at some point between
the two reads. | |
| return tc==r.tail_counter && tc==hc+r.n_invalid_entries ; | | return tc==r.tail_counter && tc==hc+r.n_invalid_entries ; | |
| } | | } | |
| | | | |
| template<typename T> | | template<typename T> | |
| void concurrent_queue_base_v3<T>::internal_finish_clear() { | | void concurrent_queue_base_v3<T>::internal_finish_clear() { | |
| concurrent_queue_rep<T>& r = *my_rep; | | concurrent_queue_rep<T>& r = *my_rep; | |
| size_t nq = r.n_queue; | | size_t nq = r.n_queue; | |
| for( size_t i=0; i<nq; ++i ) { | | for( size_t i=0; i<nq; ++i ) { | |
| page* tp = r.array[i].tail_page; | | page* tp = r.array[i].tail_page; | |
|
| __TBB_ASSERT( r.array[i].head_page==tp, "at most one page should re | | if( is_valid_page(tp) ) { | |
| main" ); | | __TBB_ASSERT( r.array[i].head_page==tp, "at most one page shoul | |
| if( tp!=NULL) { | | d remain" ); | |
| if( tp!=invalid_page ) deallocate_page( tp ); | | deallocate_page( tp ); | |
| r.array[i].tail_page = NULL; | | r.array[i].tail_page = NULL; | |
|
| } | | } else | |
| | | __TBB_ASSERT( !is_valid_page(r.array[i].head_page), "head page | |
| | | pointer corrupt?" ); | |
| } | | } | |
| } | | } | |
| | | | |
| template<typename T> | | template<typename T> | |
| void concurrent_queue_base_v3<T>::assign( const concurrent_queue_base_v3& s
rc ) { | | void concurrent_queue_base_v3<T>::assign( const concurrent_queue_base_v3& s
rc ) { | |
| concurrent_queue_rep<T>& r = *my_rep; | | concurrent_queue_rep<T>& r = *my_rep; | |
| r.items_per_page = src.my_rep->items_per_page; | | r.items_per_page = src.my_rep->items_per_page; | |
| | | | |
| // copy concurrent_queue_rep. | | // copy concurrent_queue_rep. | |
| r.head_counter = src.my_rep->head_counter; | | r.head_counter = src.my_rep->head_counter; | |
| | | | |
| skipping to change at line 571 | | skipping to change at line 600 | |
| r.array[i].assign( src.my_rep->array[i], *this); | | r.array[i].assign( src.my_rep->array[i], *this); | |
| | | | |
| __TBB_ASSERT( r.head_counter==src.my_rep->head_counter && r.tail_counte
r==src.my_rep->tail_counter, | | __TBB_ASSERT( r.head_counter==src.my_rep->head_counter && r.tail_counte
r==src.my_rep->tail_counter, | |
| "the source concurrent queue should not be concurrently modifie
d." ); | | "the source concurrent queue should not be concurrently modifie
d." ); | |
| } | | } | |
| | | | |
| template<typename Container, typename Value> class concurrent_queue_iterato
r; | | template<typename Container, typename Value> class concurrent_queue_iterato
r; | |
| | | | |
| template<typename T> | | template<typename T> | |
| class concurrent_queue_iterator_rep: no_assign { | | class concurrent_queue_iterator_rep: no_assign { | |
|
| | | typedef typename micro_queue<T>::padded_page padded_page; | |
| public: | | public: | |
| ticket head_counter; | | ticket head_counter; | |
| const concurrent_queue_base_v3<T>& my_queue; | | const concurrent_queue_base_v3<T>& my_queue; | |
| typename concurrent_queue_base_v3<T>::page* array[concurrent_queue_rep<
T>::n_queue]; | | typename concurrent_queue_base_v3<T>::page* array[concurrent_queue_rep<
T>::n_queue]; | |
| concurrent_queue_iterator_rep( const concurrent_queue_base_v3<T>& queue
) : | | concurrent_queue_iterator_rep( const concurrent_queue_base_v3<T>& queue
) : | |
| head_counter(queue.my_rep->head_counter), | | head_counter(queue.my_rep->head_counter), | |
| my_queue(queue) | | my_queue(queue) | |
| { | | { | |
| for( size_t k=0; k<concurrent_queue_rep<T>::n_queue; ++k ) | | for( size_t k=0; k<concurrent_queue_rep<T>::n_queue; ++k ) | |
| array[k] = queue.my_rep->array[k].head_page; | | array[k] = queue.my_rep->array[k].head_page; | |
| } | | } | |
| | | | |
| //! Set item to point to kth element. Return true if at end of queue o
r item is marked valid; false otherwise. | | //! Set item to point to kth element. Return true if at end of queue o
r item is marked valid; false otherwise. | |
|
| bool get_item( void*& item, size_t k ) ; | | bool get_item( T*& item, size_t k ) ; | |
| }; | | }; | |
| | | | |
| template<typename T> | | template<typename T> | |
|
| bool concurrent_queue_iterator_rep<T>::get_item( void*& item, size_t k ) { | | bool concurrent_queue_iterator_rep<T>::get_item( T*& item, size_t k ) { | |
| if( k==my_queue.my_rep->tail_counter ) { | | if( k==my_queue.my_rep->tail_counter ) { | |
| item = NULL; | | item = NULL; | |
| return true; | | return true; | |
| } else { | | } else { | |
| typename concurrent_queue_base_v3<T>::page* p = array[concurrent_qu
eue_rep<T>::index(k)]; | | typename concurrent_queue_base_v3<T>::page* p = array[concurrent_qu
eue_rep<T>::index(k)]; | |
| __TBB_ASSERT(p,NULL); | | __TBB_ASSERT(p,NULL); | |
| size_t i = k/concurrent_queue_rep<T>::n_queue & (my_queue.my_rep->i
tems_per_page-1); | | size_t i = k/concurrent_queue_rep<T>::n_queue & (my_queue.my_rep->i
tems_per_page-1); | |
|
| item = static_cast<unsigned char*>(static_cast<void*>(p+1)) + my_qu
eue.my_rep->item_size*i; | | item = µ_queue<T>::get_ref(*p,i); | |
| return (p->mask & uintptr_t(1)<<i)!=0; | | return (p->mask & uintptr_t(1)<<i)!=0; | |
| } | | } | |
| } | | } | |
| | | | |
|
| //! Type-independent portion of concurrent_queue_iterator. | | //! Constness-independent portion of concurrent_queue_iterator. | |
| /** @ingroup containers */ | | /** @ingroup containers */ | |
| template<typename Value> | | template<typename Value> | |
| class concurrent_queue_iterator_base_v3 : no_assign { | | class concurrent_queue_iterator_base_v3 : no_assign { | |
| //! Concurrentconcurrent_queue over which we are iterating. | | //! Concurrentconcurrent_queue over which we are iterating. | |
| /** NULL if one past last element in queue. */ | | /** NULL if one past last element in queue. */ | |
| concurrent_queue_iterator_rep<Value>* my_rep; | | concurrent_queue_iterator_rep<Value>* my_rep; | |
| | | | |
| template<typename C, typename T, typename U> | | template<typename C, typename T, typename U> | |
| friend bool operator==( const concurrent_queue_iterator<C,T>& i, const
concurrent_queue_iterator<C,U>& j ); | | friend bool operator==( const concurrent_queue_iterator<C,T>& i, const
concurrent_queue_iterator<C,U>& j ); | |
| | | | |
| template<typename C, typename T, typename U> | | template<typename C, typename T, typename U> | |
| friend bool operator!=( const concurrent_queue_iterator<C,T>& i, const
concurrent_queue_iterator<C,U>& j ); | | friend bool operator!=( const concurrent_queue_iterator<C,T>& i, const
concurrent_queue_iterator<C,U>& j ); | |
| protected: | | protected: | |
| //! Pointer to current item | | //! Pointer to current item | |
|
| mutable void* my_item; | | Value* my_item; | |
| | | | |
|
| public: | | | |
| //! Default constructor | | //! Default constructor | |
| concurrent_queue_iterator_base_v3() : my_rep(NULL), my_item(NULL) { | | concurrent_queue_iterator_base_v3() : my_rep(NULL), my_item(NULL) { | |
| #if __GNUC__==4&&__GNUC_MINOR__==3 | | #if __GNUC__==4&&__GNUC_MINOR__==3 | |
| // to get around a possible gcc 4.3 bug | | // to get around a possible gcc 4.3 bug | |
| __asm__ __volatile__("": : :"memory"); | | __asm__ __volatile__("": : :"memory"); | |
| #endif | | #endif | |
| } | | } | |
| | | | |
| //! Copy constructor | | //! Copy constructor | |
| concurrent_queue_iterator_base_v3( const concurrent_queue_iterator_base
_v3& i ) : my_rep(NULL), my_item(NULL) { | | concurrent_queue_iterator_base_v3( const concurrent_queue_iterator_base
_v3& i ) : my_rep(NULL), my_item(NULL) { | |
| assign(i); | | assign(i); | |
| } | | } | |
| | | | |
| //! Construct iterator pointing to head of queue. | | //! Construct iterator pointing to head of queue. | |
| concurrent_queue_iterator_base_v3( const concurrent_queue_base_v3<Value
>& queue ) ; | | concurrent_queue_iterator_base_v3( const concurrent_queue_base_v3<Value
>& queue ) ; | |
| | | | |
|
| protected: | | | |
| //! Assignment | | //! Assignment | |
| void assign( const concurrent_queue_iterator_base_v3<Value>& other ) ; | | void assign( const concurrent_queue_iterator_base_v3<Value>& other ) ; | |
| | | | |
| //! Advance iterator one step towards tail of queue. | | //! Advance iterator one step towards tail of queue. | |
| void advance() ; | | void advance() ; | |
| | | | |
| //! Destructor | | //! Destructor | |
| ~concurrent_queue_iterator_base_v3() { | | ~concurrent_queue_iterator_base_v3() { | |
| cache_aligned_allocator<concurrent_queue_iterator_rep<Value> >().de
allocate(my_rep, 1); | | cache_aligned_allocator<concurrent_queue_iterator_rep<Value> >().de
allocate(my_rep, 1); | |
| my_rep = NULL; | | my_rep = NULL; | |
| | | | |
| skipping to change at line 678 | | skipping to change at line 706 | |
| } | | } | |
| my_item = other.my_item; | | my_item = other.my_item; | |
| } | | } | |
| | | | |
| template<typename Value> | | template<typename Value> | |
| void concurrent_queue_iterator_base_v3<Value>::advance() { | | void concurrent_queue_iterator_base_v3<Value>::advance() { | |
| __TBB_ASSERT( my_item, "attempt to increment iterator past end of queue
" ); | | __TBB_ASSERT( my_item, "attempt to increment iterator past end of queue
" ); | |
| size_t k = my_rep->head_counter; | | size_t k = my_rep->head_counter; | |
| const concurrent_queue_base_v3<Value>& queue = my_rep->my_queue; | | const concurrent_queue_base_v3<Value>& queue = my_rep->my_queue; | |
| #if TBB_USE_ASSERT | | #if TBB_USE_ASSERT | |
|
| void* tmp; | | Value* tmp; | |
| my_rep->get_item(tmp,k); | | my_rep->get_item(tmp,k); | |
| __TBB_ASSERT( my_item==tmp, NULL ); | | __TBB_ASSERT( my_item==tmp, NULL ); | |
| #endif /* TBB_USE_ASSERT */ | | #endif /* TBB_USE_ASSERT */ | |
| size_t i = k/concurrent_queue_rep<Value>::n_queue & (queue.my_rep->item
s_per_page-1); | | size_t i = k/concurrent_queue_rep<Value>::n_queue & (queue.my_rep->item
s_per_page-1); | |
| if( i==queue.my_rep->items_per_page-1 ) { | | if( i==queue.my_rep->items_per_page-1 ) { | |
| typename concurrent_queue_base_v3<Value>::page*& root = my_rep->arr
ay[concurrent_queue_rep<Value>::index(k)]; | | typename concurrent_queue_base_v3<Value>::page*& root = my_rep->arr
ay[concurrent_queue_rep<Value>::index(k)]; | |
| root = root->next; | | root = root->next; | |
| } | | } | |
| // advance k | | // advance k | |
| my_rep->head_counter = ++k; | | my_rep->head_counter = ++k; | |
| if( !my_rep->get_item(my_item, k) ) advance(); | | if( !my_rep->get_item(my_item, k) ) advance(); | |
| } | | } | |
| | | | |
|
| template<typename T> | | //! Similar to C++0x std::remove_cv | |
| static inline const concurrent_queue_iterator_base_v3<const T>& add_constne | | /** "tbb_" prefix added to avoid overload confusion with C++0x implementati | |
| ss( const concurrent_queue_iterator_base_v3<T>& q ) | | ons. */ | |
| { | | template<typename T> struct tbb_remove_cv {typedef T type;}; | |
| return *reinterpret_cast<const concurrent_queue_iterator_base_v3<const | | template<typename T> struct tbb_remove_cv<const T> {typedef T type;}; | |
| T> *>(&q) ; | | template<typename T> struct tbb_remove_cv<volatile T> {typedef T type;}; | |
| } | | template<typename T> struct tbb_remove_cv<const volatile T> {typedef T type | |
| | | ;}; | |
| | | | |
| //! Meets requirements of a forward iterator for STL. | | //! Meets requirements of a forward iterator for STL. | |
| /** Value is either the T or const T type of the container. | | /** Value is either the T or const T type of the container. | |
| @ingroup containers */ | | @ingroup containers */ | |
| template<typename Container, typename Value> | | template<typename Container, typename Value> | |
|
| class concurrent_queue_iterator: public concurrent_queue_iterator_base_v3<V
alue>, | | class concurrent_queue_iterator: public concurrent_queue_iterator_base_v3<t
ypename tbb_remove_cv<Value>::type>, | |
| public std::iterator<std::forward_iterator_tag,Value> { | | public std::iterator<std::forward_iterator_tag,Value> { | |
| #if !__TBB_TEMPLATE_FRIENDS_BROKEN | | #if !__TBB_TEMPLATE_FRIENDS_BROKEN | |
| template<typename T, class A> | | template<typename T, class A> | |
| friend class ::tbb::strict_ppl::concurrent_queue; | | friend class ::tbb::strict_ppl::concurrent_queue; | |
| #else | | #else | |
| public: // workaround for MSVC | | public: // workaround for MSVC | |
| #endif | | #endif | |
| //! Construct iterator pointing to head of queue. | | //! Construct iterator pointing to head of queue. | |
| concurrent_queue_iterator( const concurrent_queue_base_v3<Value>& queue
) : | | concurrent_queue_iterator( const concurrent_queue_base_v3<Value>& queue
) : | |
|
| concurrent_queue_iterator_base_v3<Value>(queue) | | concurrent_queue_iterator_base_v3<typename tbb_remove_cv<Value>::ty
pe>(queue) | |
| { | | { | |
| } | | } | |
| | | | |
| public: | | public: | |
| concurrent_queue_iterator() {} | | concurrent_queue_iterator() {} | |
| | | | |
|
| //! Copy constructor | | concurrent_queue_iterator( const concurrent_queue_iterator<Container,ty | |
| concurrent_queue_iterator( const concurrent_queue_iterator<Container,Va | | pename Container::value_type>& other ) : | |
| lue>& other ) : | | concurrent_queue_iterator_base_v3<typename tbb_remove_cv<Value>::ty | |
| concurrent_queue_iterator_base_v3<Value>(other) | | pe>(other) | |
| { | | {} | |
| } | | | |
| | | | |
| template<typename T> | | | |
| concurrent_queue_iterator( const concurrent_queue_iterator<Container,T> | | | |
| & other ) : | | | |
| concurrent_queue_iterator_base_v3<Value>(add_constness(other)) | | | |
| { | | | |
| } | | | |
| | | | |
| //! Iterator assignment | | //! Iterator assignment | |
| concurrent_queue_iterator& operator=( const concurrent_queue_iterator&
other ) { | | concurrent_queue_iterator& operator=( const concurrent_queue_iterator&
other ) { | |
| assign(other); | | assign(other); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| //! Reference to current item | | //! Reference to current item | |
| Value& operator*() const { | | Value& operator*() const { | |
| return *static_cast<Value*>(this->my_item); | | return *static_cast<Value*>(this->my_item); | |
| | | | |
| skipping to change at line 810 | | skipping to change at line 831 | |
| | | | |
| //! Capacity of the queue | | //! Capacity of the queue | |
| ptrdiff_t my_capacity; | | ptrdiff_t my_capacity; | |
| | | | |
| //! Always a power of 2 | | //! Always a power of 2 | |
| size_t items_per_page; | | size_t items_per_page; | |
| | | | |
| //! Size of an item | | //! Size of an item | |
| size_t item_size; | | size_t item_size; | |
| | | | |
|
| | | #if __GNUC__==3&&__GNUC_MINOR__==3 | |
| | | public: | |
| | | #endif /* __GNUC__==3&&__GNUC_MINOR__==3 */ | |
| | | template<typename T> | |
| | | struct padded_page: page { | |
| | | //! Not defined anywhere - exists to quiet warnings. | |
| | | padded_page(); | |
| | | //! Not defined anywhere - exists to quiet warnings. | |
| | | void operator=( const padded_page& ); | |
| | | //! Must be last field. | |
| | | T last; | |
| | | }; | |
| | | | |
| private: | | private: | |
| virtual void copy_item( page& dst, size_t index, const void* src ) = 0; | | virtual void copy_item( page& dst, size_t index, const void* src ) = 0; | |
| virtual void assign_and_destroy_item( void* dst, page& src, size_t inde
x ) = 0; | | virtual void assign_and_destroy_item( void* dst, page& src, size_t inde
x ) = 0; | |
| protected: | | protected: | |
| __TBB_EXPORTED_METHOD concurrent_queue_base_v3( size_t item_size ); | | __TBB_EXPORTED_METHOD concurrent_queue_base_v3( size_t item_size ); | |
| virtual __TBB_EXPORTED_METHOD ~concurrent_queue_base_v3(); | | virtual __TBB_EXPORTED_METHOD ~concurrent_queue_base_v3(); | |
| | | | |
| //! Enqueue item at tail of queue | | //! Enqueue item at tail of queue | |
| void __TBB_EXPORTED_METHOD internal_push( const void* src ); | | void __TBB_EXPORTED_METHOD internal_push( const void* src ); | |
| | | | |
| | | | |
| skipping to change at line 862 | | skipping to change at line 896 | |
| //! copy internal representation | | //! copy internal representation | |
| void __TBB_EXPORTED_METHOD assign( const concurrent_queue_base_v3& src
) ; | | void __TBB_EXPORTED_METHOD assign( const concurrent_queue_base_v3& src
) ; | |
| | | | |
| private: | | private: | |
| virtual void copy_page_item( page& dst, size_t dindex, const page& src,
size_t sindex ) = 0; | | virtual void copy_page_item( page& dst, size_t dindex, const page& src,
size_t sindex ) = 0; | |
| }; | | }; | |
| | | | |
| //! Type-independent portion of concurrent_queue_iterator. | | //! Type-independent portion of concurrent_queue_iterator. | |
| /** @ingroup containers */ | | /** @ingroup containers */ | |
| class concurrent_queue_iterator_base_v3 { | | class concurrent_queue_iterator_base_v3 { | |
|
| //! Concurrentconcurrent_queue over which we are iterating. | | //! concurrent_queue over which we are iterating. | |
| /** NULL if one past last element in queue. */ | | /** NULL if one past last element in queue. */ | |
| concurrent_queue_iterator_rep* my_rep; | | concurrent_queue_iterator_rep* my_rep; | |
| | | | |
| template<typename C, typename T, typename U> | | template<typename C, typename T, typename U> | |
| friend bool operator==( const concurrent_queue_iterator<C,T>& i, const
concurrent_queue_iterator<C,U>& j ); | | friend bool operator==( const concurrent_queue_iterator<C,T>& i, const
concurrent_queue_iterator<C,U>& j ); | |
| | | | |
| template<typename C, typename T, typename U> | | template<typename C, typename T, typename U> | |
| friend bool operator!=( const concurrent_queue_iterator<C,T>& i, const
concurrent_queue_iterator<C,U>& j ); | | friend bool operator!=( const concurrent_queue_iterator<C,T>& i, const
concurrent_queue_iterator<C,U>& j ); | |
|
| | | | |
| | | void initialize( const concurrent_queue_base_v3& queue, size_t offset_o | |
| | | f_data ); | |
| protected: | | protected: | |
| //! Pointer to current item | | //! Pointer to current item | |
|
| mutable void* my_item; | | void* my_item; | |
| | | | |
| //! Default constructor | | //! Default constructor | |
| concurrent_queue_iterator_base_v3() : my_rep(NULL), my_item(NULL) {} | | concurrent_queue_iterator_base_v3() : my_rep(NULL), my_item(NULL) {} | |
| | | | |
| //! Copy constructor | | //! Copy constructor | |
| concurrent_queue_iterator_base_v3( const concurrent_queue_iterator_base
_v3& i ) : my_rep(NULL), my_item(NULL) { | | concurrent_queue_iterator_base_v3( const concurrent_queue_iterator_base
_v3& i ) : my_rep(NULL), my_item(NULL) { | |
| assign(i); | | assign(i); | |
| } | | } | |
| | | | |
|
| //! Construct iterator pointing to head of queue. | | //! Obsolete entry point for constructing iterator pointing to head of | |
| | | queue. | |
| | | /** Does not work correctly for SSE types. */ | |
| __TBB_EXPORTED_METHOD concurrent_queue_iterator_base_v3( const concurre
nt_queue_base_v3& queue ); | | __TBB_EXPORTED_METHOD concurrent_queue_iterator_base_v3( const concurre
nt_queue_base_v3& queue ); | |
| | | | |
|
| | | //! Construct iterator pointing to head of queue. | |
| | | __TBB_EXPORTED_METHOD concurrent_queue_iterator_base_v3( const concurre | |
| | | nt_queue_base_v3& queue, size_t offset_of_data ); | |
| | | | |
| //! Assignment | | //! Assignment | |
| void __TBB_EXPORTED_METHOD assign( const concurrent_queue_iterator_base
_v3& i ); | | void __TBB_EXPORTED_METHOD assign( const concurrent_queue_iterator_base
_v3& i ); | |
| | | | |
| //! Advance iterator one step towards tail of queue. | | //! Advance iterator one step towards tail of queue. | |
| void __TBB_EXPORTED_METHOD advance(); | | void __TBB_EXPORTED_METHOD advance(); | |
| | | | |
| //! Destructor | | //! Destructor | |
| __TBB_EXPORTED_METHOD ~concurrent_queue_iterator_base_v3(); | | __TBB_EXPORTED_METHOD ~concurrent_queue_iterator_base_v3(); | |
| }; | | }; | |
| | | | |
| | | | |
| skipping to change at line 915 | | skipping to change at line 956 | |
| template<typename T, class A> | | template<typename T, class A> | |
| friend class ::tbb::concurrent_bounded_queue; | | friend class ::tbb::concurrent_bounded_queue; | |
| | | | |
| template<typename T, class A> | | template<typename T, class A> | |
| friend class ::tbb::deprecated::concurrent_queue; | | friend class ::tbb::deprecated::concurrent_queue; | |
| #else | | #else | |
| public: // workaround for MSVC | | public: // workaround for MSVC | |
| #endif | | #endif | |
| //! Construct iterator pointing to head of queue. | | //! Construct iterator pointing to head of queue. | |
| concurrent_queue_iterator( const concurrent_queue_base_v3& queue ) : | | concurrent_queue_iterator( const concurrent_queue_base_v3& queue ) : | |
|
| concurrent_queue_iterator_base_v3(queue) | | concurrent_queue_iterator_base_v3(queue,__TBB_offsetof(concurrent_q
ueue_base_v3::padded_page<Value>,last)) | |
| { | | { | |
| } | | } | |
| | | | |
| public: | | public: | |
| concurrent_queue_iterator() {} | | concurrent_queue_iterator() {} | |
| | | | |
| /** If Value==Container::value_type, then this routine is the copy cons
tructor. | | /** If Value==Container::value_type, then this routine is the copy cons
tructor. | |
| If Value==const Container::value_type, then this routine is a conve
rsion constructor. */ | | If Value==const Container::value_type, then this routine is a conve
rsion constructor. */ | |
| concurrent_queue_iterator( const concurrent_queue_iterator<Container,ty
pename Container::value_type>& other ) : | | concurrent_queue_iterator( const concurrent_queue_iterator<Container,ty
pename Container::value_type>& other ) : | |
| concurrent_queue_iterator_base_v3(other) | | concurrent_queue_iterator_base_v3(other) | |
| | | | |
End of changes. 59 change blocks. |
| 80 lines changed or deleted | | 127 lines changed or added | |
|
| concurrent_hash_map.h | | concurrent_hash_map.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 32 | | skipping to change at line 32 | |
| this file and link it with other files to produce an executable, this | | this file and link it with other files to produce an executable, this | |
| file does not by itself cause the resulting executable to be covered by | | file does not by itself cause the resulting executable to be covered by | |
| the GNU General Public License. This exception does not however | | the GNU General Public License. This exception does not however | |
| invalidate any other reasons why the executable file might be covered b
y | | invalidate any other reasons why the executable file might be covered b
y | |
| the GNU General Public License. | | the GNU General Public License. | |
| */ | | */ | |
| | | | |
| #ifndef __TBB_concurrent_hash_map_H | | #ifndef __TBB_concurrent_hash_map_H | |
| #define __TBB_concurrent_hash_map_H | | #define __TBB_concurrent_hash_map_H | |
| | | | |
|
| #include <stdexcept> | | #include "tbb_stddef.h" | |
| | | | |
| | | #if !TBB_USE_EXCEPTIONS && _MSC_VER | |
| | | // Suppress "C++ exception handler used, but unwind semantics are not e | |
| | | nabled" warning in STL headers | |
| | | #pragma warning (push) | |
| | | #pragma warning (disable: 4530) | |
| | | #endif | |
| | | | |
| #include <iterator> | | #include <iterator> | |
| #include <utility> // Need std::pair | | #include <utility> // Need std::pair | |
| #include <cstring> // Need std::memset | | #include <cstring> // Need std::memset | |
|
| #include <string> | | | |
| #include "tbb_stddef.h" | | #if !TBB_USE_EXCEPTIONS && _MSC_VER | |
| | | #pragma warning (pop) | |
| | | #endif | |
| | | | |
| #include "cache_aligned_allocator.h" | | #include "cache_aligned_allocator.h" | |
| #include "tbb_allocator.h" | | #include "tbb_allocator.h" | |
| #include "spin_rw_mutex.h" | | #include "spin_rw_mutex.h" | |
| #include "atomic.h" | | #include "atomic.h" | |
| #include "aligned_space.h" | | #include "aligned_space.h" | |
|
| | | #include "tbb_exception.h" | |
| | | #include "_concurrent_unordered_internal.h" // Need tbb_hasher | |
| #if TBB_USE_PERFORMANCE_WARNINGS | | #if TBB_USE_PERFORMANCE_WARNINGS | |
| #include <typeinfo> | | #include <typeinfo> | |
| #endif | | #endif | |
| | | | |
| namespace tbb { | | namespace tbb { | |
| | | | |
|
| template<typename T> struct tbb_hash_compare; | | | |
| template<typename Key, typename T, typename HashCompare = tbb_hash_compare< | | | |
| Key>, typename A = tbb_allocator<std::pair<Key, T> > > | | | |
| class concurrent_hash_map; | | | |
| | | | |
| //! @cond INTERNAL | | //! @cond INTERNAL | |
| namespace internal { | | namespace internal { | |
| //! ITT instrumented routine that loads pointer from location pointed t
o by src. | | //! ITT instrumented routine that loads pointer from location pointed t
o by src. | |
| void* __TBB_EXPORTED_FUNC itt_load_pointer_with_acquire_v3( const void*
src ); | | void* __TBB_EXPORTED_FUNC itt_load_pointer_with_acquire_v3( const void*
src ); | |
| //! ITT instrumented routine that stores src into location pointed to b
y dst. | | //! ITT instrumented routine that stores src into location pointed to b
y dst. | |
| void __TBB_EXPORTED_FUNC itt_store_pointer_with_release_v3( void* dst,
void* src ); | | void __TBB_EXPORTED_FUNC itt_store_pointer_with_release_v3( void* dst,
void* src ); | |
| //! Routine that loads pointer from location pointed to by src without
causing ITT to report a race. | | //! Routine that loads pointer from location pointed to by src without
causing ITT to report a race. | |
| void* __TBB_EXPORTED_FUNC itt_load_pointer_v3( const void* src ); | | void* __TBB_EXPORTED_FUNC itt_load_pointer_v3( const void* src ); | |
|
| | | } | |
| | | //! @endcond | |
| | | | |
| | | //! hash_compare that is default argument for concurrent_hash_map | |
| | | template<typename Key> | |
| | | struct tbb_hash_compare { | |
| | | static size_t hash( const Key& a ) { return tbb_hasher(a); } | |
| | | static bool equal( const Key& a, const Key& b ) { return a == b; } | |
| | | }; | |
| | | | |
| | | namespace interface4 { | |
| | | | |
| | | template<typename Key, typename T, typename HashCompare = tbb_hash_comp | |
| | | are<Key>, typename A = tbb_allocator<std::pair<Key, T> > > | |
| | | class concurrent_hash_map; | |
| | | | |
| | | //! @cond INTERNAL | |
| | | namespace internal { | |
| | | | |
| //! Type of a hash code. | | //! Type of a hash code. | |
| typedef size_t hashcode_t; | | typedef size_t hashcode_t; | |
|
| | | //! Node base type | |
| | | struct hash_map_node_base : tbb::internal::no_copy { | |
| | | //! Mutex type | |
| | | typedef spin_rw_mutex mutex_t; | |
| | | //! Scoped lock type for mutex | |
| | | typedef mutex_t::scoped_lock scoped_t; | |
| | | //! Next node in chain | |
| | | hash_map_node_base *next; | |
| | | mutex_t mutex; | |
| | | }; | |
| | | //! Incompleteness flag value | |
| | | static hash_map_node_base *const rehash_req = reinterpret_cast<hash_map | |
| | | _node_base*>(size_t(3)); | |
| | | //! Rehashed empty bucket flag | |
| | | static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash | |
| | | _map_node_base*>(size_t(0)); | |
| //! base class of concurrent_hash_map | | //! base class of concurrent_hash_map | |
| class hash_map_base { | | class hash_map_base { | |
| public: | | public: | |
| //! Size type | | //! Size type | |
| typedef size_t size_type; | | typedef size_t size_type; | |
| //! Type of a hash code. | | //! Type of a hash code. | |
| typedef size_t hashcode_t; | | typedef size_t hashcode_t; | |
| //! Segment index type | | //! Segment index type | |
| typedef size_t segment_index_t; | | typedef size_t segment_index_t; | |
| //! Node base type | | //! Node base type | |
|
| struct node_base : no_copy { | | typedef hash_map_node_base node_base; | |
| //! Mutex type | | | |
| typedef spin_rw_mutex mutex_t; | | | |
| //! Scoped lock type for mutex | | | |
| typedef mutex_t::scoped_lock scoped_t; | | | |
| //! Next node in chain | | | |
| node_base *next; | | | |
| mutex_t mutex; | | | |
| }; | | | |
| //! Incompleteness flag value | | | |
| # define __TBB_rehash_req reinterpret_cast<node_base*>(1) | | | |
| //! Rehashed empty bucket flag | | | |
| # define __TBB_empty_rehashed reinterpret_cast<node_base*>(0) | | | |
| //! Bucket type | | //! Bucket type | |
|
| struct bucket : no_copy { | | struct bucket : tbb::internal::no_copy { | |
| //! Mutex type for buckets | | //! Mutex type for buckets | |
| typedef spin_rw_mutex mutex_t; | | typedef spin_rw_mutex mutex_t; | |
| //! Scoped lock type for mutex | | //! Scoped lock type for mutex | |
| typedef mutex_t::scoped_lock scoped_t; | | typedef mutex_t::scoped_lock scoped_t; | |
| mutex_t mutex; | | mutex_t mutex; | |
| node_base *node_list; | | node_base *node_list; | |
| }; | | }; | |
| //! Count of segments in the first block | | //! Count of segments in the first block | |
| static size_type const embedded_block = 1; | | static size_type const embedded_block = 1; | |
| //! Count of segments in the first block | | //! Count of segments in the first block | |
| | | | |
| skipping to change at line 145 | | skipping to change at line 172 | |
| return (segment_index_t(1)<<k & ~segment_index_t(1)); | | return (segment_index_t(1)<<k & ~segment_index_t(1)); | |
| } | | } | |
| | | | |
| //! @return segment size except for @arg k == 0 | | //! @return segment size except for @arg k == 0 | |
| static size_type segment_size( segment_index_t k ) { | | static size_type segment_size( segment_index_t k ) { | |
| return size_type(1)<<k; // fake value for k==0 | | return size_type(1)<<k; // fake value for k==0 | |
| } | | } | |
| | | | |
| //! @return true if @arg ptr is valid pointer | | //! @return true if @arg ptr is valid pointer | |
| static bool is_valid( void *ptr ) { | | static bool is_valid( void *ptr ) { | |
|
| return ptr > reinterpret_cast<void*>(1); | | return reinterpret_cast<size_t>(ptr) > size_t(63); | |
| } | | } | |
| | | | |
| //! Initialize buckets | | //! Initialize buckets | |
| static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_
initial ) { | | static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_
initial ) { | |
| if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) ); | | if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) ); | |
| else for(size_type i = 0; i < sz; i++, ptr++) { | | else for(size_type i = 0; i < sz; i++, ptr++) { | |
| *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0; | | *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0; | |
|
| ptr->node_list = __TBB_rehash_req; | | ptr->node_list = rehash_req; | |
| } | | } | |
| } | | } | |
| | | | |
| //! Add node @arg n to bucket @arg b | | //! Add node @arg n to bucket @arg b | |
| static void add_to_bucket( bucket *b, node_base *n ) { | | static void add_to_bucket( bucket *b, node_base *n ) { | |
|
| | | __TBB_ASSERT(b->node_list != rehash_req, NULL); | |
| n->next = b->node_list; | | n->next = b->node_list; | |
| b->node_list = n; // its under lock and flag is set | | b->node_list = n; // its under lock and flag is set | |
| } | | } | |
| | | | |
| //! Exception safety helper | | //! Exception safety helper | |
| struct enable_segment_failsafe { | | struct enable_segment_failsafe { | |
| segment_ptr_t *my_segment_ptr; | | segment_ptr_t *my_segment_ptr; | |
| enable_segment_failsafe(segments_table_t &table, segment_index_
t k) : my_segment_ptr(&table[k]) {} | | enable_segment_failsafe(segments_table_t &table, segment_index_
t k) : my_segment_ptr(&table[k]) {} | |
| ~enable_segment_failsafe() { | | ~enable_segment_failsafe() { | |
| if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no al
location in progress | | if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no al
location in progress | |
| | | | |
| skipping to change at line 184 | | skipping to change at line 212 | |
| __TBB_ASSERT( k, "Zero segment must be embedded" ); | | __TBB_ASSERT( k, "Zero segment must be embedded" ); | |
| enable_segment_failsafe watchdog( my_table, k ); | | enable_segment_failsafe watchdog( my_table, k ); | |
| cache_aligned_allocator<bucket> alloc; | | cache_aligned_allocator<bucket> alloc; | |
| size_type sz; | | size_type sz; | |
| __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignm
ent"); | | __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignm
ent"); | |
| if( k >= first_block ) { | | if( k >= first_block ) { | |
| sz = segment_size( k ); | | sz = segment_size( k ); | |
| segment_ptr_t ptr = alloc.allocate( sz ); | | segment_ptr_t ptr = alloc.allocate( sz ); | |
| init_buckets( ptr, sz, is_initial ); | | init_buckets( ptr, sz, is_initial ); | |
| #if TBB_USE_THREADING_TOOLS | | #if TBB_USE_THREADING_TOOLS | |
|
| // TODO: actually, fence and notification are unneccessary
here and below | | // TODO: actually, fence and notification are unnecessary h
ere and below | |
| itt_store_pointer_with_release_v3( my_table + k, ptr ); | | itt_store_pointer_with_release_v3( my_table + k, ptr ); | |
| #else | | #else | |
| my_table[k] = ptr;// my_mask has release fence | | my_table[k] = ptr;// my_mask has release fence | |
| #endif | | #endif | |
| sz <<= 1;// double it to get entire capacity of the contain
er | | sz <<= 1;// double it to get entire capacity of the contain
er | |
| } else { // the first block | | } else { // the first block | |
| __TBB_ASSERT( k == embedded_block, "Wrong segment index" ); | | __TBB_ASSERT( k == embedded_block, "Wrong segment index" ); | |
| sz = segment_size( first_block ); | | sz = segment_size( first_block ); | |
| segment_ptr_t ptr = alloc.allocate( sz - embedded_buckets )
; | | segment_ptr_t ptr = alloc.allocate( sz - embedded_buckets )
; | |
| init_buckets( ptr, sz - embedded_buckets, is_initial ); | | init_buckets( ptr, sz - embedded_buckets, is_initial ); | |
| | | | |
| skipping to change at line 220 | | skipping to change at line 248 | |
| | | | |
| //! Get bucket by (masked) hashcode | | //! Get bucket by (masked) hashcode | |
| bucket *get_bucket( hashcode_t h ) const throw() { // TODO: add thr
ow() everywhere? | | bucket *get_bucket( hashcode_t h ) const throw() { // TODO: add thr
ow() everywhere? | |
| segment_index_t s = segment_index_of( h ); | | segment_index_t s = segment_index_of( h ); | |
| h -= segment_base(s); | | h -= segment_base(s); | |
| segment_ptr_t seg = my_table[s]; | | segment_ptr_t seg = my_table[s]; | |
| __TBB_ASSERT( is_valid(seg), "hashcode must be cut by valid mas
k for allocated segments" ); | | __TBB_ASSERT( is_valid(seg), "hashcode must be cut by valid mas
k for allocated segments" ); | |
| return &seg[h]; | | return &seg[h]; | |
| } | | } | |
| | | | |
|
| | | // internal serial rehashing helper | |
| | | void mark_rehashed_levels( hashcode_t h ) throw () { | |
| | | segment_index_t s = segment_index_of( h ); | |
| | | while( segment_ptr_t seg = my_table[++s] ) | |
| | | if( seg[h].node_list == rehash_req ) { | |
| | | seg[h].node_list = empty_rehashed; | |
| | | mark_rehashed_levels( h + segment_base(s) ); | |
| | | } | |
| | | } | |
| | | | |
| //! Check for mask race | | //! Check for mask race | |
| // Splitting into two functions should help inlining | | // Splitting into two functions should help inlining | |
| inline bool check_mask_race( const hashcode_t h, hashcode_t &m ) co
nst { | | inline bool check_mask_race( const hashcode_t h, hashcode_t &m ) co
nst { | |
| hashcode_t m_now, m_old = m; | | hashcode_t m_now, m_old = m; | |
| #if TBB_USE_THREADING_TOOLS | | #if TBB_USE_THREADING_TOOLS | |
| m_now = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask
); | | m_now = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask
); | |
| #else | | #else | |
| m_now = my_mask; | | m_now = my_mask; | |
| #endif | | #endif | |
| if( m_old != m_now ) | | if( m_old != m_now ) | |
| | | | |
| skipping to change at line 245 | | skipping to change at line 283 | |
| bool check_rehashing_collision( const hashcode_t h, hashcode_t m_ol
d, hashcode_t m ) const { | | bool check_rehashing_collision( const hashcode_t h, hashcode_t m_ol
d, hashcode_t m ) const { | |
| __TBB_ASSERT(m_old != m, NULL); // TODO?: m arg could be optimi
zed out by passing h = h&m | | __TBB_ASSERT(m_old != m, NULL); // TODO?: m arg could be optimi
zed out by passing h = h&m | |
| if( (h & m_old) != (h & m) ) { // mask changed for this hashcod
e, rare event | | if( (h & m_old) != (h & m) ) { // mask changed for this hashcod
e, rare event | |
| // condition above proves that 'h' has some other bits set
beside 'm_old' | | // condition above proves that 'h' has some other bits set
beside 'm_old' | |
| // find next applicable mask after m_old //TODO: look at
bsl instruction | | // find next applicable mask after m_old //TODO: look at
bsl instruction | |
| for( ++m_old; !(h & m_old); m_old <<= 1 ); // at maximum fe
w rounds depending on the first block size | | for( ++m_old; !(h & m_old); m_old <<= 1 ); // at maximum fe
w rounds depending on the first block size | |
| m_old = (m_old<<1) - 1; // get full mask from a bit | | m_old = (m_old<<1) - 1; // get full mask from a bit | |
| __TBB_ASSERT((m_old&(m_old+1))==0 && m_old <= m, NULL); | | __TBB_ASSERT((m_old&(m_old+1))==0 && m_old <= m, NULL); | |
| // check whether it is rehashing/ed | | // check whether it is rehashing/ed | |
| #if TBB_USE_THREADING_TOOLS | | #if TBB_USE_THREADING_TOOLS | |
|
| if( itt_load_pointer_with_acquire_v3(&( get_bucket(h & m_ol
d)->node_list )) != __TBB_rehash_req ) | | if( itt_load_pointer_with_acquire_v3(&( get_bucket(h & m_ol
d)->node_list )) != rehash_req ) | |
| #else | | #else | |
|
| if( __TBB_load_with_acquire(get_bucket( h & m_old )->node_l
ist) != __TBB_rehash_req ) | | if( __TBB_load_with_acquire(get_bucket( h & m_old )->node_l
ist) != rehash_req ) | |
| #endif | | #endif | |
| return true; | | return true; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| //! Insert a node and check for load factor. @return segment index
to enable. | | //! Insert a node and check for load factor. @return segment index
to enable. | |
| segment_index_t insert_new_node( bucket *b, node_base *n, hashcode_
t mask ) { | | segment_index_t insert_new_node( bucket *b, node_base *n, hashcode_
t mask ) { | |
| size_type sz = ++my_size; // prefix form is to enforce allocati
on after the first item inserted | | size_type sz = ++my_size; // prefix form is to enforce allocati
on after the first item inserted | |
| add_to_bucket( b, n ); | | add_to_bucket( b, n ); | |
| // check load factor | | // check load factor | |
| if( sz >= mask ) { // TODO: add custom load_factor | | if( sz >= mask ) { // TODO: add custom load_factor | |
| segment_index_t new_seg = segment_index_of( mask+1 ); | | segment_index_t new_seg = segment_index_of( mask+1 ); | |
| __TBB_ASSERT( is_valid(my_table[new_seg-1]), "new allocatio
ns must not publish new mask until segment has allocated"); | | __TBB_ASSERT( is_valid(my_table[new_seg-1]), "new allocatio
ns must not publish new mask until segment has allocated"); | |
| #if TBB_USE_THREADING_TOOLS | | #if TBB_USE_THREADING_TOOLS | |
| if( !itt_load_pointer_v3(my_table+new_seg) | | if( !itt_load_pointer_v3(my_table+new_seg) | |
| #else | | #else | |
| if( !my_table[new_seg] | | if( !my_table[new_seg] | |
| #endif | | #endif | |
|
| && __TBB_CompareAndSwapW(&my_table[new_seg
], 1, 0) == 0 ) | | && __TBB_CompareAndSwapW(&my_table[new_seg], 2, 0) == 0 ) | |
| return new_seg; // The value must be processed | | return new_seg; // The value must be processed | |
| } | | } | |
| return 0; | | return 0; | |
| } | | } | |
| | | | |
| //! Prepare enough segments for number of buckets | | //! Prepare enough segments for number of buckets | |
| void reserve(size_type buckets) { | | void reserve(size_type buckets) { | |
| if( !buckets-- ) return; | | if( !buckets-- ) return; | |
| bool is_initial = !my_size; | | bool is_initial = !my_size; | |
| for( size_type m = my_mask; buckets > m; m = my_mask ) | | for( size_type m = my_mask; buckets > m; m = my_mask ) | |
| | | | |
| skipping to change at line 316 | | skipping to change at line 354 | |
| template<typename C, typename T, typename U> | | template<typename C, typename T, typename U> | |
| friend bool operator==( const hash_map_iterator<C,T>& i, const hash
_map_iterator<C,U>& j ); | | friend bool operator==( const hash_map_iterator<C,T>& i, const hash
_map_iterator<C,U>& j ); | |
| | | | |
| template<typename C, typename T, typename U> | | template<typename C, typename T, typename U> | |
| friend bool operator!=( const hash_map_iterator<C,T>& i, const hash
_map_iterator<C,U>& j ); | | friend bool operator!=( const hash_map_iterator<C,T>& i, const hash
_map_iterator<C,U>& j ); | |
| | | | |
| template<typename C, typename T, typename U> | | template<typename C, typename T, typename U> | |
| friend ptrdiff_t operator-( const hash_map_iterator<C,T>& i, const
hash_map_iterator<C,U>& j ); | | friend ptrdiff_t operator-( const hash_map_iterator<C,T>& i, const
hash_map_iterator<C,U>& j ); | |
| | | | |
| template<typename C, typename U> | | template<typename C, typename U> | |
|
| friend class internal::hash_map_iterator; | | friend class hash_map_iterator; | |
| | | | |
| template<typename I> | | template<typename I> | |
|
| friend class internal::hash_map_range; | | friend class hash_map_range; | |
| | | | |
| void advance_to_next_bucket() { // TODO?: refactor to iterator_base
class | | void advance_to_next_bucket() { // TODO?: refactor to iterator_base
class | |
| size_t k = my_index+1; | | size_t k = my_index+1; | |
| while( my_bucket && k <= my_map->my_mask ) { | | while( my_bucket && k <= my_map->my_mask ) { | |
| // Following test uses 2's-complement wizardry | | // Following test uses 2's-complement wizardry | |
|
| if( k& (k-2) ) // not the begining of a segment | | if( k& (k-2) ) // not the beginning of a segment | |
| ++my_bucket; | | ++my_bucket; | |
| else my_bucket = my_map->get_bucket( k ); | | else my_bucket = my_map->get_bucket( k ); | |
| my_node = static_cast<node*>( my_bucket->node_list ); | | my_node = static_cast<node*>( my_bucket->node_list ); | |
| if( hash_map_base::is_valid(my_node) ) { | | if( hash_map_base::is_valid(my_node) ) { | |
| my_index = k; return; | | my_index = k; return; | |
| } | | } | |
| ++k; | | ++k; | |
| } | | } | |
| my_bucket = 0; my_node = 0; my_index = k; // the end | | my_bucket = 0; my_node = 0; my_index = k; // the end | |
| } | | } | |
| #if !defined(_MSC_VER) || defined(__INTEL_COMPILER) | | #if !defined(_MSC_VER) || defined(__INTEL_COMPILER) | |
| template<typename Key, typename T, typename HashCompare, typename A
> | | template<typename Key, typename T, typename HashCompare, typename A
> | |
|
| friend class tbb::concurrent_hash_map; | | friend class interface4::concurrent_hash_map; | |
| #else | | #else | |
| public: // workaround | | public: // workaround | |
| #endif | | #endif | |
| //! concurrent_hash_map over which we are iterating. | | //! concurrent_hash_map over which we are iterating. | |
| const Container *my_map; | | const Container *my_map; | |
| | | | |
| //! Index in hash table for current item | | //! Index in hash table for current item | |
| size_t my_index; | | size_t my_index; | |
| | | | |
| //! Pointer to bucket | | //! Pointer to bucket | |
| | | | |
| skipping to change at line 456 | | skipping to change at line 494 | |
| //! type conversion | | //! type conversion | |
| template<typename U> | | template<typename U> | |
| hash_map_range( hash_map_range<U>& r) : | | hash_map_range( hash_map_range<U>& r) : | |
| my_begin(r.my_begin), | | my_begin(r.my_begin), | |
| my_end(r.my_end), | | my_end(r.my_end), | |
| my_midpoint(r.my_midpoint), | | my_midpoint(r.my_midpoint), | |
| my_grainsize(r.my_grainsize) | | my_grainsize(r.my_grainsize) | |
| {} | | {} | |
| #if TBB_DEPRECATED | | #if TBB_DEPRECATED | |
| //! Init range with iterators and grainsize specified | | //! Init range with iterators and grainsize specified | |
|
| hash_map_range( const Iterator& begin_, const Iterator& end_, size_
type grainsize = 1 ) : | | hash_map_range( const Iterator& begin_, const Iterator& end_, size_
type grainsize_ = 1 ) : | |
| my_begin(begin_), | | my_begin(begin_), | |
| my_end(end_), | | my_end(end_), | |
|
| my_grainsize(grainsize) | | my_grainsize(grainsize_) | |
| { | | { | |
| if(!my_end.my_index && !my_end.my_bucket) // end | | if(!my_end.my_index && !my_end.my_bucket) // end | |
| my_end.my_index = my_end.my_map->my_mask + 1; | | my_end.my_index = my_end.my_map->my_mask + 1; | |
| set_midpoint(); | | set_midpoint(); | |
|
| __TBB_ASSERT( grainsize>0, "grainsize must be positive" ); | | __TBB_ASSERT( grainsize_>0, "grainsize must be positive" ); | |
| } | | } | |
| #endif | | #endif | |
| //! Init range with container and grainsize specified | | //! Init range with container and grainsize specified | |
|
| hash_map_range( const map_type &map, size_type grainsize = 1 ) : | | hash_map_range( const map_type &map, size_type grainsize_ = 1 ) : | |
| my_begin( Iterator( map, 0, map.my_embedded_segment, map.my_emb
edded_segment->node_list ) ), | | my_begin( Iterator( map, 0, map.my_embedded_segment, map.my_emb
edded_segment->node_list ) ), | |
| my_end( Iterator( map, map.my_mask + 1, 0, 0 ) ), | | my_end( Iterator( map, map.my_mask + 1, 0, 0 ) ), | |
|
| my_grainsize( grainsize ) | | my_grainsize( grainsize_ ) | |
| { | | { | |
|
| __TBB_ASSERT( grainsize>0, "grainsize must be positive" ); | | __TBB_ASSERT( grainsize_>0, "grainsize must be positive" ); | |
| set_midpoint(); | | set_midpoint(); | |
| } | | } | |
| const Iterator& begin() const {return my_begin;} | | const Iterator& begin() const {return my_begin;} | |
| const Iterator& end() const {return my_end;} | | const Iterator& end() const {return my_end;} | |
| //! The grain size for this range. | | //! The grain size for this range. | |
| size_type grainsize() const {return my_grainsize;} | | size_type grainsize() const {return my_grainsize;} | |
| }; | | }; | |
| | | | |
| template<typename Iterator> | | template<typename Iterator> | |
| void hash_map_range<Iterator>::set_midpoint() const { | | void hash_map_range<Iterator>::set_midpoint() const { | |
| | | | |
| skipping to change at line 500 | | skipping to change at line 538 | |
| } else { | | } else { | |
| my_midpoint = my_end; | | my_midpoint = my_end; | |
| } | | } | |
| __TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index, | | __TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index, | |
| "my_begin is after my_midpoint" ); | | "my_begin is after my_midpoint" ); | |
| __TBB_ASSERT( my_midpoint.my_index <= my_end.my_index, | | __TBB_ASSERT( my_midpoint.my_index <= my_end.my_index, | |
| "my_midpoint is after my_end" ); | | "my_midpoint is after my_end" ); | |
| __TBB_ASSERT( my_begin != my_midpoint || my_begin == my_end, | | __TBB_ASSERT( my_begin != my_midpoint || my_begin == my_end, | |
| "[my_begin, my_midpoint) range should not be empty" ); | | "[my_begin, my_midpoint) range should not be empty" ); | |
| } | | } | |
|
| } // namespace internal | | | |
| //! @endcond | | | |
| | | | |
| //! Hash multiplier | | | |
| static const size_t hash_multiplier = sizeof(size_t)==4? 2654435769U : 1140 | | | |
| 0714819323198485ULL; | | | |
| //! Hasher functions | | | |
| template<typename T> | | | |
| inline static size_t tbb_hasher( const T& t ) { | | | |
| return static_cast<size_t>( t ) * hash_multiplier; | | | |
| } | | | |
| template<typename P> | | | |
| inline static size_t tbb_hasher( P* ptr ) { | | | |
| size_t const h = reinterpret_cast<size_t>( ptr ); | | | |
| return (h >> 3) ^ h; | | | |
| } | | | |
| template<typename E, typename S, typename A> | | | |
| inline static size_t tbb_hasher( const std::basic_string<E,S,A>& s ) { | | | |
| size_t h = 0; | | | |
| for( const E* c = s.c_str(); *c; c++ ) | | | |
| h = static_cast<size_t>(*c) ^ (h * hash_multiplier); | | | |
| return h; | | | |
| } | | | |
| template<typename F, typename S> | | | |
| inline static size_t tbb_hasher( const std::pair<F,S>& p ) { | | | |
| return tbb_hasher(p.first) ^ tbb_hasher(p.second); | | | |
| } | | | |
| | | | |
|
| //! hash_compare - default argument | | } // internal | |
| template<typename T> | | //! @endcond | |
| struct tbb_hash_compare { | | | |
| static size_t hash( const T& t ) { return tbb_hasher(t); } | | | |
| static bool equal( const T& a, const T& b ) { return a == b; } | | | |
| }; | | | |
| | | | |
| //! Unordered map from Key to T. | | //! Unordered map from Key to T. | |
| /** concurrent_hash_map is associative container with concurrent access. | | /** concurrent_hash_map is associative container with concurrent access. | |
| | | | |
| @par Compatibility | | @par Compatibility | |
| The class meets all Container Requirements from C++ Standard (See ISO/I
EC 14882:2003(E), clause 23.1). | | The class meets all Container Requirements from C++ Standard (See ISO/I
EC 14882:2003(E), clause 23.1). | |
| | | | |
| @par Exception Safety | | @par Exception Safety | |
| - Hash function is not permitted to throw an exception. User-defined ty
pes Key and T are forbidden from throwing an exception in destructors. | | - Hash function is not permitted to throw an exception. User-defined ty
pes Key and T are forbidden from throwing an exception in destructors. | |
| - If exception happens during insert() operations, it has no effect (un
less exception raised by HashCompare::hash() function during grow_segment). | | - If exception happens during insert() operations, it has no effect (un
less exception raised by HashCompare::hash() function during grow_segment). | |
| - If exception happens during operator=() operation, the container can
have a part of source items, and methods size() and empty() can return wron
g results. | | - If exception happens during operator=() operation, the container can
have a part of source items, and methods size() and empty() can return wron
g results. | |
| | | | |
| @par Changes since TBB 2.1 | | @par Changes since TBB 2.1 | |
| - Replaced internal algorithm and data structure. Patent is pending. | | - Replaced internal algorithm and data structure. Patent is pending. | |
|
| | | - Added buckets number argument for constructor | |
| | | | |
| @par Changes since TBB 2.0 | | @par Changes since TBB 2.0 | |
| - Fixed exception-safety | | - Fixed exception-safety | |
| - Added template argument for allocator | | - Added template argument for allocator | |
| - Added allocator argument in constructors | | - Added allocator argument in constructors | |
| - Added constructor from a range of iterators | | - Added constructor from a range of iterators | |
| - Added several new overloaded insert() methods | | - Added several new overloaded insert() methods | |
| - Added get_allocator() | | - Added get_allocator() | |
| - Added swap() | | - Added swap() | |
| - Added count() | | - Added count() | |
| | | | |
| skipping to change at line 575 | | skipping to change at line 584 | |
| template<typename Container, typename Value> | | template<typename Container, typename Value> | |
| friend class internal::hash_map_iterator; | | friend class internal::hash_map_iterator; | |
| | | | |
| template<typename I> | | template<typename I> | |
| friend class internal::hash_map_range; | | friend class internal::hash_map_range; | |
| | | | |
| public: | | public: | |
| typedef Key key_type; | | typedef Key key_type; | |
| typedef T mapped_type; | | typedef T mapped_type; | |
| typedef std::pair<const Key,T> value_type; | | typedef std::pair<const Key,T> value_type; | |
|
| typedef internal::hash_map_base::size_type size_type; | | typedef hash_map_base::size_type size_type; | |
| typedef ptrdiff_t difference_type; | | typedef ptrdiff_t difference_type; | |
| typedef value_type *pointer; | | typedef value_type *pointer; | |
| typedef const value_type *const_pointer; | | typedef const value_type *const_pointer; | |
| typedef value_type &reference; | | typedef value_type &reference; | |
| typedef const value_type &const_reference; | | typedef const value_type &const_reference; | |
| typedef internal::hash_map_iterator<concurrent_hash_map,value_type> ite
rator; | | typedef internal::hash_map_iterator<concurrent_hash_map,value_type> ite
rator; | |
| typedef internal::hash_map_iterator<concurrent_hash_map,const value_typ
e> const_iterator; | | typedef internal::hash_map_iterator<concurrent_hash_map,const value_typ
e> const_iterator; | |
| typedef internal::hash_map_range<iterator> range_type; | | typedef internal::hash_map_range<iterator> range_type; | |
| typedef internal::hash_map_range<const_iterator> const_range_type; | | typedef internal::hash_map_range<const_iterator> const_range_type; | |
| typedef Allocator allocator_type; | | typedef Allocator allocator_type; | |
| | | | |
| skipping to change at line 601 | | skipping to change at line 610 | |
| node_allocator_type my_allocator; | | node_allocator_type my_allocator; | |
| HashCompare my_hash_compare; | | HashCompare my_hash_compare; | |
| | | | |
| struct node : public node_base { | | struct node : public node_base { | |
| value_type item; | | value_type item; | |
| node( const Key &key ) : item(key, T()) {} | | node( const Key &key ) : item(key, T()) {} | |
| node( const Key &key, const T &t ) : item(key, t) {} | | node( const Key &key, const T &t ) : item(key, t) {} | |
| // exception-safe allocation, see C++ Standard 2003, clause 5.3.4p1
7 | | // exception-safe allocation, see C++ Standard 2003, clause 5.3.4p1
7 | |
| void *operator new( size_t /*size*/, node_allocator_type &a ) { | | void *operator new( size_t /*size*/, node_allocator_type &a ) { | |
| void *ptr = a.allocate(1); | | void *ptr = a.allocate(1); | |
|
| if(!ptr) throw std::bad_alloc(); | | if(!ptr) | |
| | | tbb::internal::throw_exception(tbb::internal::eid_bad_alloc | |
| | | ); | |
| return ptr; | | return ptr; | |
| } | | } | |
| // match placement-new form above to be called if exception thrown
in constructor | | // match placement-new form above to be called if exception thrown
in constructor | |
| void operator delete( void *ptr, node_allocator_type &a ) {return a
.deallocate(static_cast<node*>(ptr),1); } | | void operator delete( void *ptr, node_allocator_type &a ) {return a
.deallocate(static_cast<node*>(ptr),1); } | |
| }; | | }; | |
| | | | |
| void delete_node( node_base *n ) { | | void delete_node( node_base *n ) { | |
| my_allocator.destroy( static_cast<node*>(n) ); | | my_allocator.destroy( static_cast<node*>(n) ); | |
| my_allocator.deallocate( static_cast<node*>(n), 1); | | my_allocator.deallocate( static_cast<node*>(n), 1); | |
| } | | } | |
| | | | |
| node *search_bucket( const key_type &key, bucket *b ) const { | | node *search_bucket( const key_type &key, bucket *b ) const { | |
| node *n = static_cast<node*>( b->node_list ); | | node *n = static_cast<node*>( b->node_list ); | |
| while( is_valid(n) && !my_hash_compare.equal(key, n->item.first) ) | | while( is_valid(n) && !my_hash_compare.equal(key, n->item.first) ) | |
| n = static_cast<node*>( n->next ); | | n = static_cast<node*>( n->next ); | |
|
| __TBB_ASSERT(n != __TBB_rehash_req, "Search can be executed only fo
r rehashed bucket"); | | __TBB_ASSERT(n != internal::rehash_req, "Search can be executed onl
y for rehashed bucket"); | |
| return n; | | return n; | |
| } | | } | |
| | | | |
| //! bucket accessor is to find, rehash, acquire a lock, and access a bu
cket | | //! bucket accessor is to find, rehash, acquire a lock, and access a bu
cket | |
| class bucket_accessor : public bucket::scoped_t { | | class bucket_accessor : public bucket::scoped_t { | |
| bool my_is_writer; // TODO: use it from base type | | bool my_is_writer; // TODO: use it from base type | |
| bucket *my_b; | | bucket *my_b; | |
| public: | | public: | |
| bucket_accessor( concurrent_hash_map *base, const hashcode_t h, boo
l writer = false ) { acquire( base, h, writer ); } | | bucket_accessor( concurrent_hash_map *base, const hashcode_t h, boo
l writer = false ) { acquire( base, h, writer ); } | |
|
| //! find a bucket by maksed hashcode, optionally rehash, and acquir
e the lock | | //! find a bucket by masked hashcode, optionally rehash, and acquir
e the lock | |
| inline void acquire( concurrent_hash_map *base, const hashcode_t h,
bool writer = false ) { | | inline void acquire( concurrent_hash_map *base, const hashcode_t h,
bool writer = false ) { | |
| my_b = base->get_bucket( h ); | | my_b = base->get_bucket( h ); | |
| #if TBB_USE_THREADING_TOOLS | | #if TBB_USE_THREADING_TOOLS | |
|
| // TODO: actually, notification is unneccessary here, just hidi | | // TODO: actually, notification is unnecessary here, just hidin | |
| ng double-check | | g double-check | |
| if( itt_load_pointer_with_acquire_v3(&my_b->node_list) == __TBB | | if( itt_load_pointer_with_acquire_v3(&my_b->node_list) == inter | |
| _rehash_req | | nal::rehash_req | |
| #else | | #else | |
|
| if( __TBB_load_with_acquire(my_b->node_list) == __TBB_rehash_re
q | | if( __TBB_load_with_acquire(my_b->node_list) == internal::rehas
h_req | |
| #endif | | #endif | |
| && try_acquire( my_b->mutex, /*write=*/true ) ) | | && try_acquire( my_b->mutex, /*write=*/true ) ) | |
| { | | { | |
|
| if( my_b->node_list == __TBB_rehash_req ) base->rehash_buck
et( my_b, h ); //recursive rehashing | | if( my_b->node_list == internal::rehash_req ) base->rehash_
bucket( my_b, h ); //recursive rehashing | |
| my_is_writer = true; | | my_is_writer = true; | |
| } | | } | |
| else bucket::scoped_t::acquire( my_b->mutex, /*write=*/my_is_wr
iter = writer ); | | else bucket::scoped_t::acquire( my_b->mutex, /*write=*/my_is_wr
iter = writer ); | |
|
| __TBB_ASSERT( my_b->node_list != __TBB_rehash_req, NULL); | | __TBB_ASSERT( my_b->node_list != internal::rehash_req, NULL); | |
| } | | } | |
| //! check whether bucket is locked for write | | //! check whether bucket is locked for write | |
| bool is_writer() { return my_is_writer; } | | bool is_writer() { return my_is_writer; } | |
| //! get bucket pointer | | //! get bucket pointer | |
| bucket *operator() () { return my_b; } | | bucket *operator() () { return my_b; } | |
| // TODO: optimize out | | // TODO: optimize out | |
| bool upgrade_to_writer() { my_is_writer = true; return bucket::scop
ed_t::upgrade_to_writer(); } | | bool upgrade_to_writer() { my_is_writer = true; return bucket::scop
ed_t::upgrade_to_writer(); } | |
| }; | | }; | |
| | | | |
| // TODO refactor to hash_base | | // TODO refactor to hash_base | |
| void rehash_bucket( bucket *b_new, const hashcode_t h ) { | | void rehash_bucket( bucket *b_new, const hashcode_t h ) { | |
| __TBB_ASSERT( *(intptr_t*)(&b_new->mutex), "b_new must be locked (f
or write)"); | | __TBB_ASSERT( *(intptr_t*)(&b_new->mutex), "b_new must be locked (f
or write)"); | |
| __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" ); | | __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" ); | |
|
| __TBB_store_with_release(b_new->node_list, __TBB_empty_rehashed); /
/ mark rehashed | | __TBB_store_with_release(b_new->node_list, internal::empty_rehashed
); // mark rehashed | |
| hashcode_t mask = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask f
rom the topmost bit | | hashcode_t mask = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask f
rom the topmost bit | |
| | | | |
| bucket_accessor b_old( this, h & mask ); | | bucket_accessor b_old( this, h & mask ); | |
| | | | |
| mask = (mask<<1) | 1; // get full mask for new bucket | | mask = (mask<<1) | 1; // get full mask for new bucket | |
| __TBB_ASSERT( (mask&(mask+1))==0 && (h & mask) == h, NULL ); | | __TBB_ASSERT( (mask&(mask+1))==0 && (h & mask) == h, NULL ); | |
| restart: | | restart: | |
| for( node_base **p = &b_old()->node_list, *n = __TBB_load_with_acqu
ire(*p); is_valid(n); n = *p ) { | | for( node_base **p = &b_old()->node_list, *n = __TBB_load_with_acqu
ire(*p); is_valid(n); n = *p ) { | |
| hashcode_t c = my_hash_compare.hash( static_cast<node*>(n)->ite
m.first ); | | hashcode_t c = my_hash_compare.hash( static_cast<node*>(n)->ite
m.first ); | |
|
| | | #if TBB_USE_ASSERT | |
| | | hashcode_t bmask = h & (mask>>1); | |
| | | bmask = bmask==0? 1 : ( 1u<<(__TBB_Log2( bmask )+1 ) ) - 1; // | |
| | | minimal mask of parent bucket | |
| | | __TBB_ASSERT( (c & bmask) == (h & bmask), "hash() function chan | |
| | | ged for key in table" ); | |
| | | #endif | |
| if( (c & mask) == h ) { | | if( (c & mask) == h ) { | |
| if( !b_old.is_writer() ) | | if( !b_old.is_writer() ) | |
| if( !b_old.upgrade_to_writer() ) { | | if( !b_old.upgrade_to_writer() ) { | |
| goto restart; // node ptr can be invalid due to con
current erase | | goto restart; // node ptr can be invalid due to con
current erase | |
| } | | } | |
| *p = n->next; // exclude from b_old | | *p = n->next; // exclude from b_old | |
| add_to_bucket( b_new, n ); | | add_to_bucket( b_new, n ); | |
| } else p = &n->next; // iterate to next item | | } else p = &n->next; // iterate to next item | |
| } | | } | |
| } | | } | |
| | | | |
| skipping to change at line 748 | | skipping to change at line 763 | |
| pointer operator->() const { | | pointer operator->() const { | |
| return &operator*(); | | return &operator*(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| //! Construct empty table. | | //! Construct empty table. | |
| concurrent_hash_map(const allocator_type &a = allocator_type()) | | concurrent_hash_map(const allocator_type &a = allocator_type()) | |
| : my_allocator(a) | | : my_allocator(a) | |
| {} | | {} | |
| | | | |
|
| | | //! Construct empty table with n preallocated buckets. This number serv | |
| | | es also as initial concurrency level. | |
| | | concurrent_hash_map(size_type n, const allocator_type &a = allocator_ty | |
| | | pe()) | |
| | | : my_allocator(a) | |
| | | { | |
| | | reserve( n ); | |
| | | } | |
| | | | |
| //! Copy constructor | | //! Copy constructor | |
| concurrent_hash_map( const concurrent_hash_map& table, const allocator_
type &a = allocator_type()) | | concurrent_hash_map( const concurrent_hash_map& table, const allocator_
type &a = allocator_type()) | |
| : my_allocator(a) | | : my_allocator(a) | |
| { | | { | |
| internal_copy(table); | | internal_copy(table); | |
| } | | } | |
| | | | |
| //! Construction with copying iteration range and given allocator insta
nce | | //! Construction with copying iteration range and given allocator insta
nce | |
| template<typename I> | | template<typename I> | |
| concurrent_hash_map(I first, I last, const allocator_type &a = allocato
r_type()) | | concurrent_hash_map(I first, I last, const allocator_type &a = allocato
r_type()) | |
| | | | |
| skipping to change at line 773 | | skipping to change at line 795 | |
| | | | |
| //! Assignment | | //! Assignment | |
| concurrent_hash_map& operator=( const concurrent_hash_map& table ) { | | concurrent_hash_map& operator=( const concurrent_hash_map& table ) { | |
| if( this!=&table ) { | | if( this!=&table ) { | |
| clear(); | | clear(); | |
| internal_copy(table); | | internal_copy(table); | |
| } | | } | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
|
| | | //! Rehashes and optionally resizes the whole table. | |
| | | /** Useful to optimize performance before or after concurrent operation | |
| | | s. | |
| | | Also enables using of find() and count() concurrent methods in seri | |
| | | al context. */ | |
| | | void rehash(size_type n = 0); | |
| | | | |
| //! Clear table | | //! Clear table | |
| void clear(); | | void clear(); | |
| | | | |
| //! Clear table and destroy it. | | //! Clear table and destroy it. | |
| ~concurrent_hash_map() { clear(); } | | ~concurrent_hash_map() { clear(); } | |
| | | | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| // Parallel algorithm support | | // Parallel algorithm support | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| range_type range( size_type grainsize=1 ) { | | range_type range( size_type grainsize=1 ) { | |
| | | | |
| skipping to change at line 808 | | skipping to change at line 835 | |
| | | | |
| //! Number of items in table. | | //! Number of items in table. | |
| size_type size() const { return my_size; } | | size_type size() const { return my_size; } | |
| | | | |
| //! True if size()==0. | | //! True if size()==0. | |
| bool empty() const { return my_size == 0; } | | bool empty() const { return my_size == 0; } | |
| | | | |
| //! Upper bound on size. | | //! Upper bound on size. | |
| size_type max_size() const {return (~size_type(0))/sizeof(node);} | | size_type max_size() const {return (~size_type(0))/sizeof(node);} | |
| | | | |
|
| | | //! Returns the current number of buckets | |
| | | size_type bucket_count() const { return my_mask+1; } | |
| | | | |
| //! return allocator object | | //! return allocator object | |
| allocator_type get_allocator() const { return this->my_allocator; } | | allocator_type get_allocator() const { return this->my_allocator; } | |
| | | | |
| //! swap two instances. Iterators are invalidated | | //! swap two instances. Iterators are invalidated | |
| void swap(concurrent_hash_map &table); | | void swap(concurrent_hash_map &table); | |
| | | | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| // concurrent map operations | | // concurrent map operations | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| | | | |
| | | | |
| skipping to change at line 911 | | skipping to change at line 941 | |
| //! Returns an iterator for an item defined by the key, or for the next
item after it (if upper==true) | | //! Returns an iterator for an item defined by the key, or for the next
item after it (if upper==true) | |
| template<typename I> | | template<typename I> | |
| std::pair<I, I> internal_equal_range( const Key& key, I end ) const; | | std::pair<I, I> internal_equal_range( const Key& key, I end ) const; | |
| | | | |
| //! Copy "source" to *this, where *this must start out empty. | | //! Copy "source" to *this, where *this must start out empty. | |
| void internal_copy( const concurrent_hash_map& source ); | | void internal_copy( const concurrent_hash_map& source ); | |
| | | | |
| template<typename I> | | template<typename I> | |
| void internal_copy(I first, I last); | | void internal_copy(I first, I last); | |
| | | | |
|
| //! fast find when no concurrent erasure is used | | //! Fast find when no concurrent erasure is used. For internal use insi | |
| const_pointer find( const Key& key ) const { | | de TBB only! | |
| | | /** Return pointer to item with given key, or NULL if no such item exis | |
| | | ts. | |
| | | Must not be called concurrently with erasure operations. */ | |
| | | const_pointer internal_fast_find( const Key& key ) const { | |
| hashcode_t h = my_hash_compare.hash( key ); | | hashcode_t h = my_hash_compare.hash( key ); | |
|
| | | #if TBB_USE_THREADING_TOOLS | |
| | | hashcode_t m = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_m | |
| | | ask ); | |
| | | #else | |
| hashcode_t m = my_mask; | | hashcode_t m = my_mask; | |
|
| | | #endif | |
| | | node *n; | |
| restart: | | restart: | |
| __TBB_ASSERT((m&(m+1))==0, NULL); | | __TBB_ASSERT((m&(m+1))==0, NULL); | |
| bucket *b = get_bucket( h & m ); | | bucket *b = get_bucket( h & m ); | |
|
| if( b->node_list == __TBB_rehash_req ) { | | #if TBB_USE_THREADING_TOOLS | |
| | | // TODO: actually, notification is unnecessary here, just hiding do | |
| | | uble-check | |
| | | if( itt_load_pointer_with_acquire_v3(&b->node_list) == internal::re | |
| | | hash_req ) | |
| | | #else | |
| | | if( __TBB_load_with_acquire(b->node_list) == internal::rehash_req ) | |
| | | #endif | |
| | | { | |
| bucket::scoped_t lock; | | bucket::scoped_t lock; | |
|
| if( lock.try_acquire( b->mutex, /*write=*/true ) && b->node_lis | | if( lock.try_acquire( b->mutex, /*write=*/true ) ) { | |
| t == __TBB_rehash_req ) | | if( b->node_list == internal::rehash_req) | |
| const_cast<concurrent_hash_map*>(this)->rehash_bucket( b, h | | const_cast<concurrent_hash_map*>(this)->rehash_bucket( | |
| & m ); //recursive rehashing | | b, h & m ); //recursive rehashing | |
| else internal::spin_wait_while_eq( b->node_list, __TBB_rehash_r | | } | |
| eq ); //TODO: rework for fast find? | | else lock.acquire( b->mutex, /*write=*/false ); | |
| | | __TBB_ASSERT(b->node_list!=internal::rehash_req,NULL); | |
| } | | } | |
|
| node *n = search_bucket( key, b ); | | n = search_bucket( key, b ); | |
| if( check_mask_race( h, m ) ) | | | |
| goto restart; | | | |
| if( n ) | | if( n ) | |
| return &n->item; | | return &n->item; | |
|
| | | else if( check_mask_race( h, m ) ) | |
| | | goto restart; | |
| return 0; | | return 0; | |
| } | | } | |
| }; | | }; | |
| | | | |
| #if _MSC_VER && !defined(__INTEL_COMPILER) | | #if _MSC_VER && !defined(__INTEL_COMPILER) | |
| // Suppress "conditional expression is constant" warning. | | // Suppress "conditional expression is constant" warning. | |
| #pragma warning( push ) | | #pragma warning( push ) | |
| #pragma warning( disable: 4127 ) | | #pragma warning( disable: 4127 ) | |
| #endif | | #endif | |
| | | | |
| | | | |
| skipping to change at line 962 | | skipping to change at line 1008 | |
| {//lock scope | | {//lock scope | |
| __TBB_ASSERT((m&(m+1))==0, NULL); | | __TBB_ASSERT((m&(m+1))==0, NULL); | |
| return_value = false; | | return_value = false; | |
| // get bucket | | // get bucket | |
| bucket_accessor b( this, h & m ); | | bucket_accessor b( this, h & m ); | |
| | | | |
| // find a node | | // find a node | |
| n = search_bucket( key, b() ); | | n = search_bucket( key, b() ); | |
| if( op_insert ) { | | if( op_insert ) { | |
| // [opt] insert a key | | // [opt] insert a key | |
|
| if( !is_valid(n) ) { | | if( !n ) { | |
| if( !tmp_n ) { | | if( !tmp_n ) { | |
| if(t) tmp_n = new( my_allocator ) node(key, *t); | | if(t) tmp_n = new( my_allocator ) node(key, *t); | |
| else tmp_n = new( my_allocator ) node(key); | | else tmp_n = new( my_allocator ) node(key); | |
| } | | } | |
| if( !b.is_writer() && !b.upgrade_to_writer() ) { // TODO: i
mproved insertion | | if( !b.is_writer() && !b.upgrade_to_writer() ) { // TODO: i
mproved insertion | |
| // Rerun search_list, in case another thread inserted t
he item during the upgrade. | | // Rerun search_list, in case another thread inserted t
he item during the upgrade. | |
| n = search_bucket( key, b() ); | | n = search_bucket( key, b() ); | |
| if( is_valid(n) ) { // unfortunately, it did | | if( is_valid(n) ) { // unfortunately, it did | |
| b.downgrade_to_reader(); | | b.downgrade_to_reader(); | |
| goto exists; | | goto exists; | |
| | | | |
| skipping to change at line 998 | | skipping to change at line 1044 | |
| return false; | | return false; | |
| } | | } | |
| return_value = true; | | return_value = true; | |
| grow_segment = 0; | | grow_segment = 0; | |
| } | | } | |
| if( !result ) goto check_growth; | | if( !result ) goto check_growth; | |
| // TODO: the following seems as generic/regular operation | | // TODO: the following seems as generic/regular operation | |
| // acquire the item | | // acquire the item | |
| if( !result->my_lock.try_acquire( n->mutex, write ) ) { | | if( !result->my_lock.try_acquire( n->mutex, write ) ) { | |
| // we are unlucky, prepare for longer wait | | // we are unlucky, prepare for longer wait | |
|
| internal::atomic_backoff trials; | | tbb::internal::atomic_backoff trials; | |
| do { | | do { | |
| if( !trials.bounded_pause() ) { | | if( !trials.bounded_pause() ) { | |
| // the wait takes really long, restart the operation | | // the wait takes really long, restart the operation | |
| b.release(); | | b.release(); | |
|
| | | __TBB_ASSERT( !op_insert || !return_value, "Can't acqui
re new item in locked bucket?" ); | |
| __TBB_Yield(); | | __TBB_Yield(); | |
|
| | | #if TBB_USE_THREADING_TOOLS | |
| | | m = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_ | |
| | | mask ); | |
| | | #else | |
| m = my_mask; | | m = my_mask; | |
|
| | | #endif | |
| goto restart; | | goto restart; | |
| } | | } | |
| } while( !result->my_lock.try_acquire( n->mutex, write ) ); | | } while( !result->my_lock.try_acquire( n->mutex, write ) ); | |
| } | | } | |
| }//lock scope | | }//lock scope | |
| result->my_node = n; | | result->my_node = n; | |
| result->my_hash = h; | | result->my_hash = h; | |
| check_growth: | | check_growth: | |
| // [opt] grow the container | | // [opt] grow the container | |
| if( grow_segment ) | | if( grow_segment ) | |
| enable_segment( grow_segment ); | | enable_segment( grow_segment ); | |
| if( tmp_n ) // if op_insert only | | if( tmp_n ) // if op_insert only | |
| delete_node( tmp_n ); | | delete_node( tmp_n ); | |
| return return_value; | | return return_value; | |
| } | | } | |
| | | | |
| template<typename Key, typename T, typename HashCompare, typename A> | | template<typename Key, typename T, typename HashCompare, typename A> | |
| template<typename I> | | template<typename I> | |
|
| std::pair<I, I> concurrent_hash_map<Key,T,HashCompare,A>::internal_equal_ra
nge( const Key& key, I end ) const { | | std::pair<I, I> concurrent_hash_map<Key,T,HashCompare,A>::internal_equal_ra
nge( const Key& key, I end_ ) const { | |
| hashcode_t h = my_hash_compare.hash( key ); | | hashcode_t h = my_hash_compare.hash( key ); | |
| hashcode_t m = my_mask; | | hashcode_t m = my_mask; | |
| __TBB_ASSERT((m&(m+1))==0, NULL); | | __TBB_ASSERT((m&(m+1))==0, NULL); | |
| h &= m; | | h &= m; | |
| bucket *b = get_bucket( h ); | | bucket *b = get_bucket( h ); | |
|
| while( b->node_list == __TBB_rehash_req ) { | | while( b->node_list == internal::rehash_req ) { | |
| m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmos
t bit | | m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmos
t bit | |
| b = get_bucket( h &= m ); | | b = get_bucket( h &= m ); | |
| } | | } | |
| node *n = search_bucket( key, b ); | | node *n = search_bucket( key, b ); | |
| if( !n ) | | if( !n ) | |
|
| return std::make_pair(end, end); | | return std::make_pair(end_, end_); | |
| iterator lower(*this, h, b, n), upper(lower); | | iterator lower(*this, h, b, n), upper(lower); | |
| return std::make_pair(lower, ++upper); | | return std::make_pair(lower, ++upper); | |
| } | | } | |
| | | | |
| template<typename Key, typename T, typename HashCompare, typename A> | | template<typename Key, typename T, typename HashCompare, typename A> | |
| bool concurrent_hash_map<Key,T,HashCompare,A>::exclude( const_accessor &ite
m_accessor, bool readonly ) { | | bool concurrent_hash_map<Key,T,HashCompare,A>::exclude( const_accessor &ite
m_accessor, bool readonly ) { | |
| __TBB_ASSERT( item_accessor.my_node, NULL ); | | __TBB_ASSERT( item_accessor.my_node, NULL ); | |
| node_base *const n = item_accessor.my_node; | | node_base *const n = item_accessor.my_node; | |
| item_accessor.my_node = NULL; // we ought release accessor anyway | | item_accessor.my_node = NULL; // we ought release accessor anyway | |
| hashcode_t const h = item_accessor.my_hash; | | hashcode_t const h = item_accessor.my_hash; | |
|
| | | #if TBB_USE_THREADING_TOOLS | |
| | | hashcode_t m = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask | |
| | | ); | |
| | | #else | |
| hashcode_t m = my_mask; | | hashcode_t m = my_mask; | |
|
| | | #endif | |
| do { | | do { | |
| // get bucket | | // get bucket | |
| bucket_accessor b( this, h & m, /*writer=*/true ); | | bucket_accessor b( this, h & m, /*writer=*/true ); | |
| node_base **p = &b()->node_list; | | node_base **p = &b()->node_list; | |
| while( *p && *p != n ) | | while( *p && *p != n ) | |
| p = &(*p)->next; | | p = &(*p)->next; | |
| if( !*p ) { // someone else was the first | | if( !*p ) { // someone else was the first | |
| if( check_mask_race( h, m ) ) | | if( check_mask_race( h, m ) ) | |
| continue; | | continue; | |
| item_accessor.my_lock.release(); | | item_accessor.my_lock.release(); | |
| | | | |
| skipping to change at line 1075 | | skipping to change at line 1130 | |
| item_accessor.my_lock.upgrade_to_writer(); // return value means no
thing here | | item_accessor.my_lock.upgrade_to_writer(); // return value means no
thing here | |
| item_accessor.my_lock.release(); | | item_accessor.my_lock.release(); | |
| delete_node( n ); // Only one thread can delete it due to write lock on
the chain_mutex | | delete_node( n ); // Only one thread can delete it due to write lock on
the chain_mutex | |
| return true; | | return true; | |
| } | | } | |
| | | | |
| template<typename Key, typename T, typename HashCompare, typename A> | | template<typename Key, typename T, typename HashCompare, typename A> | |
| bool concurrent_hash_map<Key,T,HashCompare,A>::erase( const Key &key ) { | | bool concurrent_hash_map<Key,T,HashCompare,A>::erase( const Key &key ) { | |
| node_base *n; | | node_base *n; | |
| hashcode_t const h = my_hash_compare.hash( key ); | | hashcode_t const h = my_hash_compare.hash( key ); | |
|
| | | #if TBB_USE_THREADING_TOOLS | |
| | | hashcode_t m = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask | |
| | | ); | |
| | | #else | |
| hashcode_t m = my_mask; | | hashcode_t m = my_mask; | |
|
| | | #endif | |
| | | restart: | |
| {//lock scope | | {//lock scope | |
|
| restart: | | | |
| // get bucket | | // get bucket | |
| bucket_accessor b( this, h & m ); | | bucket_accessor b( this, h & m ); | |
| search: | | search: | |
| node_base **p = &b()->node_list; | | node_base **p = &b()->node_list; | |
| n = *p; | | n = *p; | |
| while( is_valid(n) && !my_hash_compare.equal(key, static_cast<node*
>(n)->item.first ) ) { | | while( is_valid(n) && !my_hash_compare.equal(key, static_cast<node*
>(n)->item.first ) ) { | |
| p = &n->next; | | p = &n->next; | |
| n = *p; | | n = *p; | |
| } | | } | |
| if( !n ) { // not found, but mask could be changed | | if( !n ) { // not found, but mask could be changed | |
| | | | |
| skipping to change at line 1116 | | skipping to change at line 1175 | |
| } | | } | |
| | | | |
| template<typename Key, typename T, typename HashCompare, typename A> | | template<typename Key, typename T, typename HashCompare, typename A> | |
| void concurrent_hash_map<Key,T,HashCompare,A>::swap(concurrent_hash_map<Key
,T,HashCompare,A> &table) { | | void concurrent_hash_map<Key,T,HashCompare,A>::swap(concurrent_hash_map<Key
,T,HashCompare,A> &table) { | |
| std::swap(this->my_allocator, table.my_allocator); | | std::swap(this->my_allocator, table.my_allocator); | |
| std::swap(this->my_hash_compare, table.my_hash_compare); | | std::swap(this->my_hash_compare, table.my_hash_compare); | |
| internal_swap(table); | | internal_swap(table); | |
| } | | } | |
| | | | |
| template<typename Key, typename T, typename HashCompare, typename A> | | template<typename Key, typename T, typename HashCompare, typename A> | |
|
| | | void concurrent_hash_map<Key,T,HashCompare,A>::rehash(size_type sz) { | |
| | | reserve( sz ); // TODO: add reduction of number of buckets as well | |
| | | hashcode_t mask = my_mask; | |
| | | hashcode_t b = (mask+1)>>1; // size or first index of the last segment | |
| | | __TBB_ASSERT((b&(b-1))==0, NULL); | |
| | | bucket *bp = get_bucket( b ); // only the last segment should be scanne | |
| | | d for rehashing | |
| | | for(; b <= mask; b++, bp++ ) { | |
| | | node_base *n = bp->node_list; | |
| | | __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == | |
| | | internal::rehash_req, "Broken internal structure" ); | |
| | | __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concu | |
| | | rrent or unexpectedly terminated operation during rehash() execution" ); | |
| | | if( n == internal::rehash_req ) { // rehash bucket, conditional bec | |
| | | ause rehashing of a previous bucket may affect this one | |
| | | hashcode_t h = b; bucket *b_old = bp; | |
| | | do { | |
| | | __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehash | |
| | | ed" ); | |
| | | hashcode_t m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent m | |
| | | ask from the topmost bit | |
| | | b_old = get_bucket( h &= m ); | |
| | | } while( b_old->node_list == internal::rehash_req ); | |
| | | // now h - is index of the root rehashed bucket b_old | |
| | | mark_rehashed_levels( h ); // mark all non-rehashed children re | |
| | | cursively across all segments | |
| | | for( node_base **p = &b_old->node_list, *q = *p; is_valid(q); q | |
| | | = *p ) { | |
| | | hashcode_t c = my_hash_compare.hash( static_cast<node*>(q)- | |
| | | >item.first ); | |
| | | if( (c & mask) != h ) { // should be rehashed | |
| | | *p = q->next; // exclude from b_old | |
| | | bucket *b_new = get_bucket( c & mask ); | |
| | | __TBB_ASSERT( b_new->node_list != internal::rehash_req, | |
| | | "hash() function changed for key in table or internal error" ); | |
| | | add_to_bucket( b_new, q ); | |
| | | } else p = &q->next; // iterate to next item | |
| | | } | |
| | | } | |
| | | } | |
| | | #if TBB_USE_PERFORMANCE_WARNINGS | |
| | | int current_size = int(my_size), buckets = int(mask)+1, empty_buckets = | |
| | | 0, overpopulated_buckets = 0; // usage statistics | |
| | | static bool reported = false; | |
| | | #endif | |
| | | #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS | |
| | | for( b = 0; b <= mask; b++ ) {// only last segment should be scanned fo | |
| | | r rehashing | |
| | | if( b & (b-2) ) ++bp; // not the beginning of a segment | |
| | | else bp = get_bucket( b ); | |
| | | node_base *n = bp->node_list; | |
| | | __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concu | |
| | | rrent or unexpectedly terminated operation during rehash() execution" ); | |
| | | __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed, "Broken | |
| | | internal structure" ); | |
| | | #if TBB_USE_PERFORMANCE_WARNINGS | |
| | | if( n == internal::empty_rehashed ) empty_buckets++; | |
| | | else if( n->next ) overpopulated_buckets++; | |
| | | #endif | |
| | | #if TBB_USE_ASSERT | |
| | | for( ; is_valid(n); n = n->next ) { | |
| | | hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->ite | |
| | | m.first ) & mask; | |
| | | __TBB_ASSERT( h == b, "hash() function changed for key in table | |
| | | or internal error" ); | |
| | | } | |
| | | #endif | |
| | | } | |
| | | #endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS | |
| | | #if TBB_USE_PERFORMANCE_WARNINGS | |
| | | if( buckets > current_size) empty_buckets -= buckets - current_size; | |
| | | else overpopulated_buckets -= current_size - buckets; // TODO: load_fac | |
| | | tor? | |
| | | if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || | |
| | | 2*overpopulated_buckets > current_size ) ) { | |
| | | tbb::internal::runtime_warning( | |
| | | "Performance is not optimal because the hash function produces | |
| | | bad randomness in lower bits in %s.\nSize: %d Empties: %d Overlaps: %d", | |
| | | typeid(*this).name(), current_size, empty_buckets, overpopulate | |
| | | d_buckets ); | |
| | | reported = true; | |
| | | } | |
| | | #endif | |
| | | } | |
| | | | |
| | | template<typename Key, typename T, typename HashCompare, typename A> | |
| void concurrent_hash_map<Key,T,HashCompare,A>::clear() { | | void concurrent_hash_map<Key,T,HashCompare,A>::clear() { | |
| hashcode_t m = my_mask; | | hashcode_t m = my_mask; | |
| __TBB_ASSERT((m&(m+1))==0, NULL); | | __TBB_ASSERT((m&(m+1))==0, NULL); | |
|
| #if TBB_USE_DEBUG || TBB_USE_PERFORMANCE_WARNINGS | | #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS | |
| #if TBB_USE_PERFORMANCE_WARNINGS | | #if TBB_USE_PERFORMANCE_WARNINGS | |
|
| int size = int(my_size), buckets = int(m)+1, empty_buckets = 0, overpop
ulated_buckets = 0; // usage statistics | | int current_size = int(my_size), buckets = int(m)+1, empty_buckets = 0,
overpopulated_buckets = 0; // usage statistics | |
| static bool reported = false; | | static bool reported = false; | |
| #endif | | #endif | |
|
| | | bucket *bp = 0; | |
| // check consistency | | // check consistency | |
| for( segment_index_t b = 0; b <= m; b++ ) { | | for( segment_index_t b = 0; b <= m; b++ ) { | |
|
| node_base *n = get_bucket(b)->node_list; | | if( b & (b-2) ) ++bp; // not the beginning of a segment | |
| | | else bp = get_bucket( b ); | |
| | | node_base *n = bp->node_list; | |
| | | __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == | |
| | | internal::rehash_req, "Broken internal structure" ); | |
| | | __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concu | |
| | | rrent or unexpectedly terminated operation during clear() execution" ); | |
| #if TBB_USE_PERFORMANCE_WARNINGS | | #if TBB_USE_PERFORMANCE_WARNINGS | |
|
| if( n == __TBB_empty_rehashed ) empty_buckets++; | | if( n == internal::empty_rehashed ) empty_buckets++; | |
| else if( n == __TBB_rehash_req ) buckets--; | | else if( n == internal::rehash_req ) buckets--; | |
| else if( n->next ) overpopulated_buckets++; | | else if( n->next ) overpopulated_buckets++; | |
| #endif | | #endif | |
|
| | | #if __TBB_EXTRA_DEBUG | |
| for(; is_valid(n); n = n->next ) { | | for(; is_valid(n); n = n->next ) { | |
| hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->ite
m.first ); | | hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->ite
m.first ); | |
| h &= m; | | h &= m; | |
|
| __TBB_ASSERT( h == b || get_bucket(h)->node_list == __TBB_rehas
h_req, "Rehashing is not finished until serial stage due to concurrent or t
erminated operation" ); | | __TBB_ASSERT( h == b || get_bucket(h)->node_list == internal::r
ehash_req, "hash() function changed for key in table or internal error" ); | |
| } | | } | |
|
| | | #endif | |
| } | | } | |
| #if TBB_USE_PERFORMANCE_WARNINGS | | #if TBB_USE_PERFORMANCE_WARNINGS | |
|
| if( buckets > size) empty_buckets -= buckets - size; | | if( buckets > current_size) empty_buckets -= buckets - current_size; | |
| else overpopulated_buckets -= size - buckets; // TODO: load_factor? | | else overpopulated_buckets -= current_size - buckets; // TODO: load_fac | |
| if( !reported && buckets >= 512 && ( 2*empty_buckets >= size || 2*overp | | tor? | |
| opulated_buckets > size ) ) { | | if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || | |
| internal::runtime_warning( | | 2*overpopulated_buckets > current_size ) ) { | |
| | | tbb::internal::runtime_warning( | |
| "Performance is not optimal because the hash function produces
bad randomness in lower bits in %s.\nSize: %d Empties: %d Overlaps: %d", | | "Performance is not optimal because the hash function produces
bad randomness in lower bits in %s.\nSize: %d Empties: %d Overlaps: %d", | |
|
| typeid(*this).name(), size, empty_buckets, overpopulated_bucket
s ); | | typeid(*this).name(), current_size, empty_buckets, overpopulate
d_buckets ); | |
| reported = true; | | reported = true; | |
| } | | } | |
| #endif | | #endif | |
|
| #endif//TBB_USE_DEBUG || TBB_USE_PERFORMANCE_WARNINGS | | #endif//TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS | |
| my_size = 0; | | my_size = 0; | |
| segment_index_t s = segment_index_of( m ); | | segment_index_t s = segment_index_of( m ); | |
| __TBB_ASSERT( s+1 == pointers_per_table || !my_table[s+1], "wrong mask
or concurrent grow" ); | | __TBB_ASSERT( s+1 == pointers_per_table || !my_table[s+1], "wrong mask
or concurrent grow" ); | |
| cache_aligned_allocator<bucket> alloc; | | cache_aligned_allocator<bucket> alloc; | |
| do { | | do { | |
| __TBB_ASSERT( is_valid( my_table[s] ), "wrong mask or concurrent gr
ow" ); | | __TBB_ASSERT( is_valid( my_table[s] ), "wrong mask or concurrent gr
ow" ); | |
|
| segment_ptr_t buckets = my_table[s]; | | segment_ptr_t buckets_ptr = my_table[s]; | |
| size_type sz = segment_size( s ? s : 1 ); | | size_type sz = segment_size( s ? s : 1 ); | |
| for( segment_index_t i = 0; i < sz; i++ ) | | for( segment_index_t i = 0; i < sz; i++ ) | |
|
| for( node_base *n = buckets[i].node_list; is_valid(n); n = buck | | for( node_base *n = buckets_ptr[i].node_list; is_valid(n); n = | |
| ets[i].node_list ) { | | buckets_ptr[i].node_list ) { | |
| buckets[i].node_list = n->next; | | buckets_ptr[i].node_list = n->next; | |
| delete_node( n ); | | delete_node( n ); | |
| } | | } | |
| if( s >= first_block) // the first segment or the next | | if( s >= first_block) // the first segment or the next | |
|
| alloc.deallocate( buckets, sz ); | | alloc.deallocate( buckets_ptr, sz ); | |
| else if( s == embedded_block && embedded_block != first_block ) | | else if( s == embedded_block && embedded_block != first_block ) | |
|
| alloc.deallocate( buckets, segment_size(first_block)-embedded_b
uckets ); | | alloc.deallocate( buckets_ptr, segment_size(first_block)-embedd
ed_buckets ); | |
| if( s >= embedded_block ) my_table[s] = 0; | | if( s >= embedded_block ) my_table[s] = 0; | |
| } while(s-- > 0); | | } while(s-- > 0); | |
| my_mask = embedded_buckets - 1; | | my_mask = embedded_buckets - 1; | |
| } | | } | |
| | | | |
| template<typename Key, typename T, typename HashCompare, typename A> | | template<typename Key, typename T, typename HashCompare, typename A> | |
| void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy( const concurr
ent_hash_map& source ) { | | void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy( const concurr
ent_hash_map& source ) { | |
| reserve( source.my_size ); // TODO: load_factor? | | reserve( source.my_size ); // TODO: load_factor? | |
|
| if( my_mask == source.my_mask ) { // optimized version | | hashcode_t mask = source.my_mask; | |
| for( const_iterator it = source.begin(), end = source.end(); it != | | if( my_mask == mask ) { // optimized version | |
| end; ++it ) { | | bucket *dst = 0, *src = 0; | |
| bucket *b = get_bucket( it.my_index ); | | bool rehash_required = false; | |
| __TBB_ASSERT( b->node_list != __TBB_rehash_req, "Invalid bucket | | for( hashcode_t k = 0; k <= mask; k++ ) { | |
| in destination table"); | | if( k & (k-2) ) ++dst,src++; // not the beginning of a segment | |
| node *n = new( my_allocator ) node(it->first, it->second); | | else { dst = get_bucket( k ); src = source.get_bucket( k ); } | |
| add_to_bucket( b, n ); | | __TBB_ASSERT( dst->node_list != internal::rehash_req, "Invalid | |
| ++my_size; // TODO: replace by non-atomic op | | bucket in destination table"); | |
| | | node *n = static_cast<node*>( src->node_list ); | |
| | | if( n == internal::rehash_req ) { // source is not rehashed, it | |
| | | ems are in previous buckets | |
| | | rehash_required = true; | |
| | | dst->node_list = internal::rehash_req; | |
| | | } else for(; n; n = static_cast<node*>( n->next ) ) { | |
| | | add_to_bucket( dst, new( my_allocator ) node(n->item.first, | |
| | | n->item.second) ); | |
| | | ++my_size; // TODO: replace by non-atomic op | |
| | | } | |
| } | | } | |
|
| | | if( rehash_required ) rehash(); | |
| } else internal_copy( source.begin(), source.end() ); | | } else internal_copy( source.begin(), source.end() ); | |
| } | | } | |
| | | | |
| template<typename Key, typename T, typename HashCompare, typename A> | | template<typename Key, typename T, typename HashCompare, typename A> | |
| template<typename I> | | template<typename I> | |
| void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy(I first, I las
t) { | | void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy(I first, I las
t) { | |
| hashcode_t m = my_mask; | | hashcode_t m = my_mask; | |
| for(; first != last; ++first) { | | for(; first != last; ++first) { | |
| hashcode_t h = my_hash_compare.hash( first->first ); | | hashcode_t h = my_hash_compare.hash( first->first ); | |
| bucket *b = get_bucket( h & m ); | | bucket *b = get_bucket( h & m ); | |
|
| __TBB_ASSERT( b->node_list != __TBB_rehash_req, "Invalid bucket in
destination table"); | | __TBB_ASSERT( b->node_list != internal::rehash_req, "Invalid bucket
in destination table"); | |
| node *n = new( my_allocator ) node(first->first, first->second); | | node *n = new( my_allocator ) node(first->first, first->second); | |
| add_to_bucket( b, n ); | | add_to_bucket( b, n ); | |
| ++my_size; // TODO: replace by non-atomic op | | ++my_size; // TODO: replace by non-atomic op | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | } // namespace interface4 | |
| | | | |
| | | using interface4::concurrent_hash_map; | |
| | | | |
| template<typename Key, typename T, typename HashCompare, typename A1, typen
ame A2> | | template<typename Key, typename T, typename HashCompare, typename A1, typen
ame A2> | |
| inline bool operator==(const concurrent_hash_map<Key, T, HashCompare, A1> &
a, const concurrent_hash_map<Key, T, HashCompare, A2> &b) { | | inline bool operator==(const concurrent_hash_map<Key, T, HashCompare, A1> &
a, const concurrent_hash_map<Key, T, HashCompare, A2> &b) { | |
| if(a.size() != b.size()) return false; | | if(a.size() != b.size()) return false; | |
| typename concurrent_hash_map<Key, T, HashCompare, A1>::const_iterator i
(a.begin()), i_end(a.end()); | | typename concurrent_hash_map<Key, T, HashCompare, A1>::const_iterator i
(a.begin()), i_end(a.end()); | |
| typename concurrent_hash_map<Key, T, HashCompare, A2>::const_iterator j
, j_end(b.end()); | | typename concurrent_hash_map<Key, T, HashCompare, A2>::const_iterator j
, j_end(b.end()); | |
| for(; i != i_end; ++i) { | | for(; i != i_end; ++i) { | |
| j = b.equal_range(i->first).first; | | j = b.equal_range(i->first).first; | |
| if( j == j_end || !(i->second == j->second) ) return false; | | if( j == j_end || !(i->second == j->second) ) return false; | |
| } | | } | |
| return true; | | return true; | |
| | | | |
End of changes. 83 change blocks. |
| 131 lines changed or deleted | | 316 lines changed or added | |
|
| concurrent_vector.h | | concurrent_vector.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 33 | | skipping to change at line 33 | |
| file does not by itself cause the resulting executable to be covered by | | file does not by itself cause the resulting executable to be covered by | |
| the GNU General Public License. This exception does not however | | the GNU General Public License. This exception does not however | |
| invalidate any other reasons why the executable file might be covered b
y | | invalidate any other reasons why the executable file might be covered b
y | |
| the GNU General Public License. | | the GNU General Public License. | |
| */ | | */ | |
| | | | |
| #ifndef __TBB_concurrent_vector_H | | #ifndef __TBB_concurrent_vector_H | |
| #define __TBB_concurrent_vector_H | | #define __TBB_concurrent_vector_H | |
| | | | |
| #include "tbb_stddef.h" | | #include "tbb_stddef.h" | |
|
| #include <algorithm> | | #include "tbb_exception.h" | |
| #include <iterator> | | | |
| #include <new> | | | |
| #include <cstring> | | | |
| #include "atomic.h" | | #include "atomic.h" | |
| #include "cache_aligned_allocator.h" | | #include "cache_aligned_allocator.h" | |
| #include "blocked_range.h" | | #include "blocked_range.h" | |
|
| | | | |
| #include "tbb_machine.h" | | #include "tbb_machine.h" | |
|
| | | #include <new> | |
| | | | |
| | | #if !TBB_USE_EXCEPTIONS && _MSC_VER | |
| | | // Suppress "C++ exception handler used, but unwind semantics are not e | |
| | | nabled" warning in STL headers | |
| | | #pragma warning (push) | |
| | | #pragma warning (disable: 4530) | |
| | | #endif | |
| | | | |
| | | #include <algorithm> | |
| | | #include <iterator> | |
| | | | |
| | | #if !TBB_USE_EXCEPTIONS && _MSC_VER | |
| | | #pragma warning (pop) | |
| | | #endif | |
| | | | |
| #if _MSC_VER==1500 && !__INTEL_COMPILER | | #if _MSC_VER==1500 && !__INTEL_COMPILER | |
| // VS2008/VC9 seems to have an issue; limits pull in math.h | | // VS2008/VC9 seems to have an issue; limits pull in math.h | |
| #pragma warning( push ) | | #pragma warning( push ) | |
| #pragma warning( disable: 4985 ) | | #pragma warning( disable: 4985 ) | |
| #endif | | #endif | |
| #include <limits> /* std::numeric_limits */ | | #include <limits> /* std::numeric_limits */ | |
| #if _MSC_VER==1500 && !__INTEL_COMPILER | | #if _MSC_VER==1500 && !__INTEL_COMPILER | |
| #pragma warning( pop ) | | #pragma warning( pop ) | |
| #endif | | #endif | |
| | | | |
| skipping to change at line 64 | | skipping to change at line 74 | |
| // Workaround for overzealous compiler warnings in /Wp64 mode | | // Workaround for overzealous compiler warnings in /Wp64 mode | |
| #pragma warning (push) | | #pragma warning (push) | |
| #pragma warning (disable: 4267) | | #pragma warning (disable: 4267) | |
| #endif | | #endif | |
| | | | |
| namespace tbb { | | namespace tbb { | |
| | | | |
| template<typename T, class A = cache_aligned_allocator<T> > | | template<typename T, class A = cache_aligned_allocator<T> > | |
| class concurrent_vector; | | class concurrent_vector; | |
| | | | |
|
| //! Bad allocation marker | | | |
| #define __TBB_BAD_ALLOC reinterpret_cast<void*>(size_t(63)) | | | |
| | | | |
| //! @cond INTERNAL | | //! @cond INTERNAL | |
| namespace internal { | | namespace internal { | |
| | | | |
|
| | | //! Bad allocation marker | |
| | | static void *const vector_allocation_error_flag = reinterpret_cast<void | |
| | | *>(size_t(63)); | |
| | | | |
| //! Routine that loads pointer from location pointed to by src without
any fence, without causing ITT to report a race. | | //! Routine that loads pointer from location pointed to by src without
any fence, without causing ITT to report a race. | |
| void* __TBB_EXPORTED_FUNC itt_load_pointer_v3( const void* src ); | | void* __TBB_EXPORTED_FUNC itt_load_pointer_v3( const void* src ); | |
| | | | |
| //! Base class of concurrent vector implementation. | | //! Base class of concurrent vector implementation. | |
| /** @ingroup containers */ | | /** @ingroup containers */ | |
| class concurrent_vector_base_v3 { | | class concurrent_vector_base_v3 { | |
| protected: | | protected: | |
| | | | |
| // Basic types declarations | | // Basic types declarations | |
| typedef size_t segment_index_t; | | typedef size_t segment_index_t; | |
| | | | |
| skipping to change at line 96 | | skipping to change at line 106 | |
| //! Number of slots for segment's pointers inside the class | | //! Number of slots for segment's pointers inside the class | |
| pointers_per_short_table = 3, // to fit into 8 words of entire
structure | | pointers_per_short_table = 3, // to fit into 8 words of entire
structure | |
| pointers_per_long_table = sizeof(segment_index_t) * 8 // one se
gment per bit | | pointers_per_long_table = sizeof(segment_index_t) * 8 // one se
gment per bit | |
| }; | | }; | |
| | | | |
| // Segment pointer. Can be zero-initialized | | // Segment pointer. Can be zero-initialized | |
| struct segment_t { | | struct segment_t { | |
| void* array; | | void* array; | |
| #if TBB_USE_ASSERT | | #if TBB_USE_ASSERT | |
| ~segment_t() { | | ~segment_t() { | |
|
| __TBB_ASSERT( array <= __TBB_BAD_ALLOC, "should have been f
reed by clear" ); | | __TBB_ASSERT( array <= internal::vector_allocation_error_fl
ag, "should have been freed by clear" ); | |
| } | | } | |
| #endif /* TBB_USE_ASSERT */ | | #endif /* TBB_USE_ASSERT */ | |
| }; | | }; | |
| | | | |
| // Data fields | | // Data fields | |
| | | | |
| //! allocator function pointer | | //! allocator function pointer | |
| void* (*vector_allocator_ptr)(concurrent_vector_base_v3 &, size_t); | | void* (*vector_allocator_ptr)(concurrent_vector_base_v3 &, size_t); | |
| | | | |
| //! count of segments in the first block | | //! count of segments in the first block | |
| | | | |
| skipping to change at line 169 | | skipping to change at line 179 | |
| void __TBB_EXPORTED_METHOD internal_reserve( size_type n, size_type
element_size, size_type max_size ); | | void __TBB_EXPORTED_METHOD internal_reserve( size_type n, size_type
element_size, size_type max_size ); | |
| size_type __TBB_EXPORTED_METHOD internal_capacity() const; | | size_type __TBB_EXPORTED_METHOD internal_capacity() const; | |
| void internal_grow( size_type start, size_type finish, size_type el
ement_size, internal_array_op2 init, const void *src ); | | void internal_grow( size_type start, size_type finish, size_type el
ement_size, internal_array_op2 init, const void *src ); | |
| size_type __TBB_EXPORTED_METHOD internal_grow_by( size_type delta,
size_type element_size, internal_array_op2 init, const void *src ); | | size_type __TBB_EXPORTED_METHOD internal_grow_by( size_type delta,
size_type element_size, internal_array_op2 init, const void *src ); | |
| void* __TBB_EXPORTED_METHOD internal_push_back( size_type element_s
ize, size_type& index ); | | void* __TBB_EXPORTED_METHOD internal_push_back( size_type element_s
ize, size_type& index ); | |
| segment_index_t __TBB_EXPORTED_METHOD internal_clear( internal_arra
y_op1 destroy ); | | segment_index_t __TBB_EXPORTED_METHOD internal_clear( internal_arra
y_op1 destroy ); | |
| void* __TBB_EXPORTED_METHOD internal_compact( size_type element_siz
e, void *table, internal_array_op1 destroy, internal_array_op2 copy ); | | void* __TBB_EXPORTED_METHOD internal_compact( size_type element_siz
e, void *table, internal_array_op1 destroy, internal_array_op2 copy ); | |
| void __TBB_EXPORTED_METHOD internal_copy( const concurrent_vector_b
ase_v3& src, size_type element_size, internal_array_op2 copy ); | | void __TBB_EXPORTED_METHOD internal_copy( const concurrent_vector_b
ase_v3& src, size_type element_size, internal_array_op2 copy ); | |
| void __TBB_EXPORTED_METHOD internal_assign( const concurrent_vector
_base_v3& src, size_type element_size, | | void __TBB_EXPORTED_METHOD internal_assign( const concurrent_vector
_base_v3& src, size_type element_size, | |
| internal_array_op1 destroy, internal_array_op
2 assign, internal_array_op2 copy ); | | internal_array_op1 destroy, internal_array_op
2 assign, internal_array_op2 copy ); | |
|
| | | //! Obsolete | |
| void __TBB_EXPORTED_METHOD internal_throw_exception(size_type) cons
t; | | void __TBB_EXPORTED_METHOD internal_throw_exception(size_type) cons
t; | |
| void __TBB_EXPORTED_METHOD internal_swap(concurrent_vector_base_v3&
v); | | void __TBB_EXPORTED_METHOD internal_swap(concurrent_vector_base_v3&
v); | |
| | | | |
| void __TBB_EXPORTED_METHOD internal_resize( size_type n, size_type
element_size, size_type max_size, const void *src, | | void __TBB_EXPORTED_METHOD internal_resize( size_type n, size_type
element_size, size_type max_size, const void *src, | |
| internal_array_op1 dest
roy, internal_array_op2 init ); | | internal_array_op1 dest
roy, internal_array_op2 init ); | |
| size_type __TBB_EXPORTED_METHOD internal_grow_to_at_least_with_resu
lt( size_type new_size, size_type element_size, internal_array_op2 init, co
nst void *src ); | | size_type __TBB_EXPORTED_METHOD internal_grow_to_at_least_with_resu
lt( size_type new_size, size_type element_size, internal_array_op2 init, co
nst void *src ); | |
| | | | |
| //! Deprecated entry point for backwards compatibility to TBB 2.1. | | //! Deprecated entry point for backwards compatibility to TBB 2.1. | |
| void __TBB_EXPORTED_METHOD internal_grow_to_at_least( size_type new
_size, size_type element_size, internal_array_op2 init, const void *src ); | | void __TBB_EXPORTED_METHOD internal_grow_to_at_least( size_type new
_size, size_type element_size, internal_array_op2 init, const void *src ); | |
| private: | | private: | |
| | | | |
| skipping to change at line 450 | | skipping to change at line 461 | |
| private internal::concurrent_vector_base { | | private internal::concurrent_vector_base { | |
| private: | | private: | |
| template<typename I> | | template<typename I> | |
| class generic_range_type: public blocked_range<I> { | | class generic_range_type: public blocked_range<I> { | |
| public: | | public: | |
| typedef T value_type; | | typedef T value_type; | |
| typedef T& reference; | | typedef T& reference; | |
| typedef const T& const_reference; | | typedef const T& const_reference; | |
| typedef I iterator; | | typedef I iterator; | |
| typedef ptrdiff_t difference_type; | | typedef ptrdiff_t difference_type; | |
|
| generic_range_type( I begin_, I end_, size_t grainsize = 1) : block
ed_range<I>(begin_,end_,grainsize) {} | | generic_range_type( I begin_, I end_, size_t grainsize_ = 1) : bloc
ked_range<I>(begin_,end_,grainsize_) {} | |
| template<typename U> | | template<typename U> | |
| generic_range_type( const generic_range_type<U>& r) : blocked_range
<I>(r.begin(),r.end(),r.grainsize()) {} | | generic_range_type( const generic_range_type<U>& r) : blocked_range
<I>(r.begin(),r.end(),r.grainsize()) {} | |
| generic_range_type( generic_range_type& r, split ) : blocked_range<
I>(r,split()) {} | | generic_range_type( generic_range_type& r, split ) : blocked_range<
I>(r,split()) {} | |
| }; | | }; | |
| | | | |
| template<typename C, typename U> | | template<typename C, typename U> | |
| friend class internal::vector_iterator; | | friend class internal::vector_iterator; | |
| public: | | public: | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| // STL compatible types | | // STL compatible types | |
| | | | |
| skipping to change at line 507 | | skipping to change at line 518 | |
| : internal::allocator_base<T, A>(a) | | : internal::allocator_base<T, A>(a) | |
| { | | { | |
| vector_allocator_ptr = &internal_allocator; | | vector_allocator_ptr = &internal_allocator; | |
| } | | } | |
| | | | |
| //! Copying constructor | | //! Copying constructor | |
| concurrent_vector( const concurrent_vector& vector, const allocator_typ
e& a = allocator_type() ) | | concurrent_vector( const concurrent_vector& vector, const allocator_typ
e& a = allocator_type() ) | |
| : internal::allocator_base<T, A>(a) | | : internal::allocator_base<T, A>(a) | |
| { | | { | |
| vector_allocator_ptr = &internal_allocator; | | vector_allocator_ptr = &internal_allocator; | |
|
| try { | | __TBB_TRY { | |
| internal_copy(vector, sizeof(T), ©_array); | | internal_copy(vector, sizeof(T), ©_array); | |
|
| } catch(...) { | | } __TBB_CATCH(...) { | |
| segment_t *table = my_segment; | | segment_t *table = my_segment; | |
| internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | | internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | |
|
| throw; | | __TBB_RETHROW(); | |
| } | | } | |
| } | | } | |
| | | | |
| //! Copying constructor for vector with different allocator type | | //! Copying constructor for vector with different allocator type | |
| template<class M> | | template<class M> | |
| concurrent_vector( const concurrent_vector<T, M>& vector, const allocat
or_type& a = allocator_type() ) | | concurrent_vector( const concurrent_vector<T, M>& vector, const allocat
or_type& a = allocator_type() ) | |
| : internal::allocator_base<T, A>(a) | | : internal::allocator_base<T, A>(a) | |
| { | | { | |
| vector_allocator_ptr = &internal_allocator; | | vector_allocator_ptr = &internal_allocator; | |
|
| try { | | __TBB_TRY { | |
| internal_copy(vector.internal_vector_base(), sizeof(T), ©_a
rray); | | internal_copy(vector.internal_vector_base(), sizeof(T), ©_a
rray); | |
|
| } catch(...) { | | } __TBB_CATCH(...) { | |
| segment_t *table = my_segment; | | segment_t *table = my_segment; | |
| internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | | internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | |
|
| throw; | | __TBB_RETHROW(); | |
| } | | } | |
| } | | } | |
| | | | |
| //! Construction with initial size specified by argument n | | //! Construction with initial size specified by argument n | |
| explicit concurrent_vector(size_type n) | | explicit concurrent_vector(size_type n) | |
| { | | { | |
| vector_allocator_ptr = &internal_allocator; | | vector_allocator_ptr = &internal_allocator; | |
|
| try { | | __TBB_TRY { | |
| internal_resize( n, sizeof(T), max_size(), NULL, &destroy_array
, &initialize_array ); | | internal_resize( n, sizeof(T), max_size(), NULL, &destroy_array
, &initialize_array ); | |
|
| } catch(...) { | | } __TBB_CATCH(...) { | |
| segment_t *table = my_segment; | | segment_t *table = my_segment; | |
| internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | | internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | |
|
| throw; | | __TBB_RETHROW(); | |
| } | | } | |
| } | | } | |
| | | | |
| //! Construction with initial size specified by argument n, initializat
ion by copying of t, and given allocator instance | | //! Construction with initial size specified by argument n, initializat
ion by copying of t, and given allocator instance | |
| concurrent_vector(size_type n, const_reference t, const allocator_type&
a = allocator_type()) | | concurrent_vector(size_type n, const_reference t, const allocator_type&
a = allocator_type()) | |
| : internal::allocator_base<T, A>(a) | | : internal::allocator_base<T, A>(a) | |
| { | | { | |
| vector_allocator_ptr = &internal_allocator; | | vector_allocator_ptr = &internal_allocator; | |
|
| try { | | __TBB_TRY { | |
| internal_resize( n, sizeof(T), max_size(), static_cast<const vo
id*>(&t), &destroy_array, &initialize_array_by ); | | internal_resize( n, sizeof(T), max_size(), static_cast<const vo
id*>(&t), &destroy_array, &initialize_array_by ); | |
|
| } catch(...) { | | } __TBB_CATCH(...) { | |
| segment_t *table = my_segment; | | segment_t *table = my_segment; | |
| internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | | internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | |
|
| throw; | | __TBB_RETHROW(); | |
| } | | } | |
| } | | } | |
| | | | |
| //! Construction with copying iteration range and given allocator insta
nce | | //! Construction with copying iteration range and given allocator insta
nce | |
| template<class I> | | template<class I> | |
| concurrent_vector(I first, I last, const allocator_type &a = allocator_
type()) | | concurrent_vector(I first, I last, const allocator_type &a = allocator_
type()) | |
| : internal::allocator_base<T, A>(a) | | : internal::allocator_base<T, A>(a) | |
| { | | { | |
| vector_allocator_ptr = &internal_allocator; | | vector_allocator_ptr = &internal_allocator; | |
|
| try { | | __TBB_TRY { | |
| internal_assign_range(first, last, static_cast<is_integer_tag<s
td::numeric_limits<I>::is_integer> *>(0) ); | | internal_assign_range(first, last, static_cast<is_integer_tag<s
td::numeric_limits<I>::is_integer> *>(0) ); | |
|
| } catch(...) { | | } __TBB_CATCH(...) { | |
| segment_t *table = my_segment; | | segment_t *table = my_segment; | |
| internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | | internal_free_segments( reinterpret_cast<void**>(table), intern
al_clear(&destroy_array), my_first_block ); | |
|
| throw; | | __TBB_RETHROW(); | |
| } | | } | |
| } | | } | |
| | | | |
| //! Assignment | | //! Assignment | |
| concurrent_vector& operator=( const concurrent_vector& vector ) { | | concurrent_vector& operator=( const concurrent_vector& vector ) { | |
| if( this != &vector ) | | if( this != &vector ) | |
| internal_assign(vector, sizeof(T), &destroy_array, &assign_arra
y, ©_array); | | internal_assign(vector, sizeof(T), &destroy_array, &assign_arra
y, ©_array); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 893 | | skipping to change at line 904 | |
| ~internal_loop_guide() { | | ~internal_loop_guide() { | |
| if(i < n) // if exception raised, do zerroing on the rest of it
ems | | if(i < n) // if exception raised, do zerroing on the rest of it
ems | |
| std::memset(array+i, 0, (n-i)*sizeof(value_type)); | | std::memset(array+i, 0, (n-i)*sizeof(value_type)); | |
| } | | } | |
| }; | | }; | |
| }; | | }; | |
| | | | |
| template<typename T, class A> | | template<typename T, class A> | |
| void concurrent_vector<T, A>::shrink_to_fit() { | | void concurrent_vector<T, A>::shrink_to_fit() { | |
| internal_segments_table old; | | internal_segments_table old; | |
|
| try { | | __TBB_TRY { | |
| if( internal_compact( sizeof(T), &old, &destroy_array, ©_array
) ) | | if( internal_compact( sizeof(T), &old, &destroy_array, ©_array
) ) | |
| internal_free_segments( old.table, pointers_per_long_table, old
.first_block ); // free joined and unnecessary segments | | internal_free_segments( old.table, pointers_per_long_table, old
.first_block ); // free joined and unnecessary segments | |
|
| } catch(...) { | | } __TBB_CATCH(...) { | |
| if( old.first_block ) // free segment allocated for compacting. Onl
y for support of exceptions in ctor of user T[ype] | | if( old.first_block ) // free segment allocated for compacting. Onl
y for support of exceptions in ctor of user T[ype] | |
| internal_free_segments( old.table, 1, old.first_block ); | | internal_free_segments( old.table, 1, old.first_block ); | |
|
| throw; | | __TBB_RETHROW(); | |
| } | | } | |
| } | | } | |
| | | | |
| template<typename T, class A> | | template<typename T, class A> | |
| void concurrent_vector<T, A>::internal_free_segments(void *table[], segment
_index_t k, segment_index_t first_block) { | | void concurrent_vector<T, A>::internal_free_segments(void *table[], segment
_index_t k, segment_index_t first_block) { | |
| // Free the arrays | | // Free the arrays | |
| while( k > first_block ) { | | while( k > first_block ) { | |
| --k; | | --k; | |
| T* array = static_cast<T*>(table[k]); | | T* array = static_cast<T*>(table[k]); | |
| table[k] = NULL; | | table[k] = NULL; | |
|
| if( array > __TBB_BAD_ALLOC ) // check for correct segment pointer | | if( array > internal::vector_allocation_error_flag ) // check for c
orrect segment pointer | |
| this->my_allocator.deallocate( array, segment_size(k) ); | | this->my_allocator.deallocate( array, segment_size(k) ); | |
| } | | } | |
| T* array = static_cast<T*>(table[0]); | | T* array = static_cast<T*>(table[0]); | |
|
| if( array > __TBB_BAD_ALLOC ) { | | if( array > internal::vector_allocation_error_flag ) { | |
| __TBB_ASSERT( first_block > 0, NULL ); | | __TBB_ASSERT( first_block > 0, NULL ); | |
| while(k > 0) table[--k] = NULL; | | while(k > 0) table[--k] = NULL; | |
| this->my_allocator.deallocate( array, segment_size(first_block) ); | | this->my_allocator.deallocate( array, segment_size(first_block) ); | |
| } | | } | |
| } | | } | |
| | | | |
| template<typename T, class A> | | template<typename T, class A> | |
| T& concurrent_vector<T, A>::internal_subscript( size_type index ) const { | | T& concurrent_vector<T, A>::internal_subscript( size_type index ) const { | |
| __TBB_ASSERT( index < my_early_size, "index out of bounds" ); | | __TBB_ASSERT( index < my_early_size, "index out of bounds" ); | |
| size_type j = index; | | size_type j = index; | |
| segment_index_t k = segment_base_index_of( j ); | | segment_index_t k = segment_base_index_of( j ); | |
|
| __TBB_ASSERT( my_segment != (segment_t*)my_storage || k < pointers_per_
short_table, "index is being allocated" ); | | __TBB_ASSERT( (segment_t*)my_segment != my_storage || k < pointers_per_
short_table, "index is being allocated" ); | |
| // no need in __TBB_load_with_acquire since thread works in own space o
r gets | | // no need in __TBB_load_with_acquire since thread works in own space o
r gets | |
| #if TBB_USE_THREADING_TOOLS | | #if TBB_USE_THREADING_TOOLS | |
| T* array = static_cast<T*>( tbb::internal::itt_load_pointer_v3(&my_segm
ent[k].array)); | | T* array = static_cast<T*>( tbb::internal::itt_load_pointer_v3(&my_segm
ent[k].array)); | |
| #else | | #else | |
| T* array = static_cast<T*>(my_segment[k].array); | | T* array = static_cast<T*>(my_segment[k].array); | |
| #endif /* TBB_USE_THREADING_TOOLS */ | | #endif /* TBB_USE_THREADING_TOOLS */ | |
|
| __TBB_ASSERT( array != __TBB_BAD_ALLOC, "the instance is broken by bad
allocation. Use at() instead" ); | | __TBB_ASSERT( array != internal::vector_allocation_error_flag, "the ins
tance is broken by bad allocation. Use at() instead" ); | |
| __TBB_ASSERT( array, "index is being allocated" ); | | __TBB_ASSERT( array, "index is being allocated" ); | |
| return array[j]; | | return array[j]; | |
| } | | } | |
| | | | |
| template<typename T, class A> | | template<typename T, class A> | |
| T& concurrent_vector<T, A>::internal_subscript_with_exceptions( size_type i
ndex ) const { | | T& concurrent_vector<T, A>::internal_subscript_with_exceptions( size_type i
ndex ) const { | |
| if( index >= my_early_size ) | | if( index >= my_early_size ) | |
|
| internal_throw_exception(0); // throw std::out_of_range | | internal::throw_exception(internal::eid_out_of_range); // throw std
::out_of_range | |
| size_type j = index; | | size_type j = index; | |
| segment_index_t k = segment_base_index_of( j ); | | segment_index_t k = segment_base_index_of( j ); | |
|
| if( my_segment == (segment_t*)my_storage && k >= pointers_per_short_tab | | if( (segment_t*)my_segment == my_storage && k >= pointers_per_short_tab | |
| le ) | | le ) | |
| internal_throw_exception(1); // throw std::range_error | | internal::throw_exception(internal::eid_segment_range_error); // th | |
| | | row std::range_error | |
| void *array = my_segment[k].array; // no need in __TBB_load_with_acquir
e | | void *array = my_segment[k].array; // no need in __TBB_load_with_acquir
e | |
|
| if( array <= __TBB_BAD_ALLOC ) // check for correct segment pointer | | if( array <= internal::vector_allocation_error_flag ) // check for corr | |
| internal_throw_exception(2); // throw std::range_error | | ect segment pointer | |
| | | internal::throw_exception(internal::eid_index_range_error); // thro | |
| | | w std::range_error | |
| return static_cast<T*>(array)[j]; | | return static_cast<T*>(array)[j]; | |
| } | | } | |
| | | | |
| template<typename T, class A> template<class I> | | template<typename T, class A> template<class I> | |
| void concurrent_vector<T, A>::internal_assign_iterators(I first, I last) { | | void concurrent_vector<T, A>::internal_assign_iterators(I first, I last) { | |
| __TBB_ASSERT(my_early_size == 0, NULL); | | __TBB_ASSERT(my_early_size == 0, NULL); | |
| size_type n = std::distance(first, last); | | size_type n = std::distance(first, last); | |
| if( !n ) return; | | if( !n ) return; | |
| internal_reserve(n, sizeof(T), max_size()); | | internal_reserve(n, sizeof(T), max_size()); | |
| my_early_size = n; | | my_early_size = n; | |
| | | | |
End of changes. 34 change blocks. |
| 39 lines changed or deleted | | 55 lines changed or added | |
|
| enumerable_thread_specific.h | | enumerable_thread_specific.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 121 | | skipping to change at line 121 | |
| | | | |
| enumerable_thread_specific_iterator &operator-=( ptrdiff_t offs
et ) { | | enumerable_thread_specific_iterator &operator-=( ptrdiff_t offs
et ) { | |
| my_index -= offset; | | my_index -= offset; | |
| my_value = NULL; | | my_value = NULL; | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| Value& operator*() const { | | Value& operator*() const { | |
| Value* value = my_value; | | Value* value = my_value; | |
| if( !value ) { | | if( !value ) { | |
|
| value = my_value = &(*my_container)[my_index].value; | | value = my_value = reinterpret_cast<Value *>(&(*my_cont
ainer)[my_index].value); | |
| } | | } | |
|
| __TBB_ASSERT( value==&(*my_container)[my_index].value, "cor
rupt cache" ); | | __TBB_ASSERT( value==reinterpret_cast<Value *>(&(*my_contai
ner)[my_index].value), "corrupt cache" ); | |
| return *value; | | return *value; | |
| } | | } | |
| | | | |
| Value& operator[]( ptrdiff_t k ) const { | | Value& operator[]( ptrdiff_t k ) const { | |
| return (*my_container)[my_index + k].value; | | return (*my_container)[my_index + k].value; | |
| } | | } | |
| | | | |
| Value* operator->() const {return &operator*();} | | Value* operator->() const {return &operator*();} | |
| | | | |
| enumerable_thread_specific_iterator& operator++() { | | enumerable_thread_specific_iterator& operator++() { | |
| | | | |
| skipping to change at line 456 | | skipping to change at line 456 | |
| } | | } | |
| /* override */ T apply() { return f(); } // does copy construc
tion of returned value. | | /* override */ T apply() { return f(); } // does copy construc
tion of returned value. | |
| }; | | }; | |
| | | | |
| template<typename Key, typename T, typename HC, typename A> | | template<typename Key, typename T, typename HC, typename A> | |
| class ets_concurrent_hash_map : public tbb::concurrent_hash_map<Key
, T, HC, A> { | | class ets_concurrent_hash_map : public tbb::concurrent_hash_map<Key
, T, HC, A> { | |
| public: | | public: | |
| typedef tbb::concurrent_hash_map<Key, T, HC, A> base_type; | | typedef tbb::concurrent_hash_map<Key, T, HC, A> base_type; | |
| typedef typename base_type::const_pointer const_pointer; | | typedef typename base_type::const_pointer const_pointer; | |
| typedef typename base_type::key_type key_type; | | typedef typename base_type::key_type key_type; | |
|
| const_pointer find( const key_type &k ) { return base_type::fin | | const_pointer find( const key_type &k ) { | |
| d( k ); } // make public | | return this->internal_fast_find( k ); | |
| | | } // make public | |
| | | }; | |
| | | | |
| | | //! Template for adding padding in order to avoid false sharing | |
| | | /** ModularSize should be sizeof(U) modulo the cache line size. | |
| | | All maintenance of the space will be done explicitly on push_ba | |
| | | ck, | |
| | | and all thread local copies must be destroyed before the concur | |
| | | rent | |
| | | vector is deleted. | |
| | | */ | |
| | | template<typename U, size_t ModularSize> | |
| | | struct ets_element { | |
| | | char value[sizeof(U) + NFS_MaxLineSize-ModularSize]; | |
| | | void unconstruct() { | |
| | | // "reinterpret_cast<U*>(&value)->~U();" causes type-punnin | |
| | | g warning with gcc 4.4, | |
| | | // "U* u = reinterpret_cast<U*>(&value); u->~U();" causes u | |
| | | nused variable warning with VS2010. | |
| | | // Thus another "casting via union" hack. | |
| | | union { void* space; U* val; } helper; | |
| | | helper.space = &value; | |
| | | helper.val->~U(); | |
| | | } | |
| | | }; | |
| | | | |
| | | //! Partial specialization for case where no padding is needed. | |
| | | template<typename U> | |
| | | struct ets_element<U,0> { | |
| | | char value[sizeof(U)]; | |
| | | void unconstruct() { // Same implementation as in general case | |
| | | union { void* space; U* val; } helper; | |
| | | helper.space = &value; | |
| | | helper.val->~U(); | |
| | | } | |
| }; | | }; | |
| | | | |
| } // namespace internal | | } // namespace internal | |
| //! @endcond | | //! @endcond | |
| | | | |
|
| //! The thread local class template | | //! The enumerable_thread_specific container | |
| | | /** enumerable_thread_specific has the following properties: | |
| | | - thread-local copies are lazily created, with default, exemplar or | |
| | | function initialization. | |
| | | - thread-local copies do not move (during lifetime, and excepting c | |
| | | lear()) so the address of a copy is invariant. | |
| | | - the contained objects need not have operator=() defined if combin | |
| | | e is not used. | |
| | | - enumerable_thread_specific containers may be copy-constructed or | |
| | | assigned. | |
| | | - thread-local copies can be managed by hash-table, or can be acces | |
| | | sed via TLS storage for speed. | |
| | | - outside of parallel contexts, the contents of all thread-local co | |
| | | pies are accessible by iterator or using combine or combine_each methods | |
| | | | |
| | | @par Segmented iterator | |
| | | When the thread-local objects are containers with input_iterators d | |
| | | efined, a segmented iterator may | |
| | | be used to iterate over all the elements of all thread-local copies | |
| | | . | |
| | | | |
| | | @par combine and combine_each | |
| | | - Both methods are defined for enumerable_thread_specific. | |
| | | - combine() requires the the type T have operator=() defined. | |
| | | - neither method modifies the contents of the object (though there | |
| | | is no guarantee that the applied methods do not modify the object.) | |
| | | - Both are evaluated in serial context (the methods are assumed to | |
| | | be non-benign.) | |
| | | | |
| | | @ingroup containers */ | |
| template <typename T, | | template <typename T, | |
| typename Allocator=cache_aligned_allocator<T>, | | typename Allocator=cache_aligned_allocator<T>, | |
| ets_key_usage_type ETS_key_type=ets_no_key > | | ets_key_usage_type ETS_key_type=ets_no_key > | |
| class enumerable_thread_specific { | | class enumerable_thread_specific { | |
| | | | |
| template<typename U, typename A, ets_key_usage_type C> friend class
enumerable_thread_specific; | | template<typename U, typename A, ets_key_usage_type C> friend class
enumerable_thread_specific; | |
| | | | |
| typedef internal::tls_manager< ETS_key_type > my_tls_manager; | | typedef internal::tls_manager< ETS_key_type > my_tls_manager; | |
| | | | |
|
| //! The padded elements; padded to avoid false sharing | | typedef internal::ets_element<T,sizeof(T)%internal::NFS_MaxLineSize | |
| template<typename U> | | > padded_element; | |
| struct padded_element { | | | |
| U value; | | | |
| char padding[ ( (sizeof(U) - 1) / internal::NFS_MaxLineSize + 1 | | | |
| ) * internal::NFS_MaxLineSize - sizeof(U) ]; | | | |
| padded_element(const U &v) : value(v) {} | | | |
| padded_element() {} | | | |
| }; | | | |
| | | | |
| //! A generic range, used to create range objects from the iterator
s | | //! A generic range, used to create range objects from the iterator
s | |
| template<typename I> | | template<typename I> | |
| class generic_range_type: public blocked_range<I> { | | class generic_range_type: public blocked_range<I> { | |
| public: | | public: | |
| typedef T value_type; | | typedef T value_type; | |
| typedef T& reference; | | typedef T& reference; | |
| typedef const T& const_reference; | | typedef const T& const_reference; | |
| typedef I iterator; | | typedef I iterator; | |
| typedef ptrdiff_t difference_type; | | typedef ptrdiff_t difference_type; | |
|
| generic_range_type( I begin_, I end_, size_t grainsize = 1) : b
locked_range<I>(begin_,end_,grainsize) {} | | generic_range_type( I begin_, I end_, size_t grainsize_ = 1) :
blocked_range<I>(begin_,end_,grainsize_) {} | |
| template<typename U> | | template<typename U> | |
| generic_range_type( const generic_range_type<U>& r) : blocked_r
ange<I>(r.begin(),r.end(),r.grainsize()) {} | | generic_range_type( const generic_range_type<U>& r) : blocked_r
ange<I>(r.begin(),r.end(),r.grainsize()) {} | |
| generic_range_type( generic_range_type& r, split ) : blocked_ra
nge<I>(r,split()) {} | | generic_range_type( generic_range_type& r, split ) : blocked_ra
nge<I>(r,split()) {} | |
| }; | | }; | |
| | | | |
|
| typedef typename Allocator::template rebind< padded_element<T> >::o | | typedef typename Allocator::template rebind< padded_element >::othe | |
| ther padded_allocator_type; | | r padded_allocator_type; | |
| typedef tbb::concurrent_vector< padded_element<T>, padded_allocator | | typedef tbb::concurrent_vector< padded_element, padded_allocator_ty | |
| _type > internal_collection_type; | | pe > internal_collection_type; | |
| typedef typename internal_collection_type::size_type hash_table_ind
ex_type; // storing array indices rather than iterators to simplify | | typedef typename internal_collection_type::size_type hash_table_ind
ex_type; // storing array indices rather than iterators to simplify | |
| // copying the hash table that correlates thread IDs with concurren
t vector elements. | | // copying the hash table that correlates thread IDs with concurren
t vector elements. | |
| | | | |
| typedef typename Allocator::template rebind< std::pair< typename in
ternal::thread_hash_compare::thread_key, hash_table_index_type > >::other h
ash_element_allocator; | | typedef typename Allocator::template rebind< std::pair< typename in
ternal::thread_hash_compare::thread_key, hash_table_index_type > >::other h
ash_element_allocator; | |
| typedef internal::ets_concurrent_hash_map< typename internal::threa
d_hash_compare::thread_key, hash_table_index_type, internal::thread_hash_co
mpare, hash_element_allocator > thread_to_index_type; | | typedef internal::ets_concurrent_hash_map< typename internal::threa
d_hash_compare::thread_key, hash_table_index_type, internal::thread_hash_co
mpare, hash_element_allocator > thread_to_index_type; | |
| | | | |
| typename my_tls_manager::tls_key_t my_key; | | typename my_tls_manager::tls_key_t my_key; | |
| | | | |
| void reset_key() { | | void reset_key() { | |
| my_tls_manager::destroy_key(my_key); | | my_tls_manager::destroy_key(my_key); | |
| my_tls_manager::create_key(my_key); | | my_tls_manager::create_key(my_key); | |
| } | | } | |
| | | | |
| internal::callback_base<T> *my_finit_callback; | | internal::callback_base<T> *my_finit_callback; | |
| | | | |
| // need to use a pointed-to exemplar because T may not be assignabl
e. | | // need to use a pointed-to exemplar because T may not be assignabl
e. | |
| // using tbb_allocator instead of padded_element_allocator because
we may be | | // using tbb_allocator instead of padded_element_allocator because
we may be | |
| // copying an exemplar from one instantiation of ETS to another wit
h a different | | // copying an exemplar from one instantiation of ETS to another wit
h a different | |
| // allocator. | | // allocator. | |
|
| typedef typename tbb::tbb_allocator<padded_element<T> > exemplar_al | | typedef typename tbb::tbb_allocator<padded_element > exemplar_alloc | |
| locator_type; | | ator_type; | |
| static padded_element<T> * create_exemplar(const T& my_value) { | | static padded_element * create_exemplar(const T& my_value) { | |
| padded_element<T> *new_exemplar = 0; | | padded_element *new_exemplar = reinterpret_cast<padded_element | |
| // void *new_space = padded_allocator_type().allocate(1); | | *>(exemplar_allocator_type().allocate(1)); | |
| void *new_space = exemplar_allocator_type().allocate(1); | | new(new_exemplar->value) T(my_value); | |
| new_exemplar = new(new_space) padded_element<T>(my_value); | | | |
| return new_exemplar; | | return new_exemplar; | |
| } | | } | |
| | | | |
|
| static padded_element<T> *create_exemplar( ) { | | static padded_element *create_exemplar( ) { | |
| // void *new_space = padded_allocator_type().allocate(1); | | padded_element *new_exemplar = reinterpret_cast<padded_element | |
| void *new_space = exemplar_allocator_type().allocate(1); | | *>(exemplar_allocator_type().allocate(1)); | |
| padded_element<T> *new_exemplar = new(new_space) padded_element | | new(new_exemplar->value) T( ); | |
| <T>( ); | | | |
| return new_exemplar; | | return new_exemplar; | |
| } | | } | |
| | | | |
|
| static void free_exemplar(padded_element<T> *my_ptr) { | | static void free_exemplar(padded_element *my_ptr) { | |
| // padded_allocator_type().destroy(my_ptr); | | my_ptr->unconstruct(); | |
| // padded_allocator_type().deallocate(my_ptr,1); | | | |
| exemplar_allocator_type().destroy(my_ptr); | | exemplar_allocator_type().destroy(my_ptr); | |
| exemplar_allocator_type().deallocate(my_ptr,1); | | exemplar_allocator_type().deallocate(my_ptr,1); | |
| } | | } | |
| | | | |
|
| padded_element<T>* my_exemplar_ptr; | | padded_element* my_exemplar_ptr; | |
| | | | |
| internal_collection_type my_locals; | | internal_collection_type my_locals; | |
| thread_to_index_type my_hash_tbl; | | thread_to_index_type my_hash_tbl; | |
| | | | |
| public: | | public: | |
| | | | |
| //! Basic types | | //! Basic types | |
| typedef Allocator allocator_type; | | typedef Allocator allocator_type; | |
| typedef T value_type; | | typedef T value_type; | |
| typedef T& reference; | | typedef T& reference; | |
| | | | |
| skipping to change at line 567 | | skipping to change at line 607 | |
| // Iterator types | | // Iterator types | |
| typedef typename internal::enumerable_thread_specific_iterator< int
ernal_collection_type, value_type > iterator; | | typedef typename internal::enumerable_thread_specific_iterator< int
ernal_collection_type, value_type > iterator; | |
| typedef typename internal::enumerable_thread_specific_iterator< int
ernal_collection_type, const value_type > const_iterator; | | typedef typename internal::enumerable_thread_specific_iterator< int
ernal_collection_type, const value_type > const_iterator; | |
| | | | |
| // Parallel range types | | // Parallel range types | |
| typedef generic_range_type< iterator > range_type; | | typedef generic_range_type< iterator > range_type; | |
| typedef generic_range_type< const_iterator > const_range_type; | | typedef generic_range_type< const_iterator > const_range_type; | |
| | | | |
| //! Default constructor, which leads to default construction of loc
al copies | | //! Default constructor, which leads to default construction of loc
al copies | |
| enumerable_thread_specific() : my_finit_callback(0) { | | enumerable_thread_specific() : my_finit_callback(0) { | |
|
| my_exemplar_ptr = create_exemplar(); | | my_exemplar_ptr = 0; | |
| my_tls_manager::create_key(my_key); | | my_tls_manager::create_key(my_key); | |
| } | | } | |
| | | | |
| //! construction with initializer method | | //! construction with initializer method | |
| // Finit should be a function taking 0 parameters and returning a T | | // Finit should be a function taking 0 parameters and returning a T | |
| template <typename Finit> | | template <typename Finit> | |
| enumerable_thread_specific( Finit _finit ) | | enumerable_thread_specific( Finit _finit ) | |
| { | | { | |
| my_finit_callback = internal::callback_leaf<T,Finit>::new_callb
ack( _finit ); | | my_finit_callback = internal::callback_leaf<T,Finit>::new_callb
ack( _finit ); | |
|
| my_tls_manager::create_key(my_key); | | | |
| my_exemplar_ptr = 0; // don't need exemplar if function is prov
ided | | my_exemplar_ptr = 0; // don't need exemplar if function is prov
ided | |
|
| | | my_tls_manager::create_key(my_key); | |
| } | | } | |
| | | | |
| //! Constuction with exemplar, which leads to copy construction of
local copies | | //! Constuction with exemplar, which leads to copy construction of
local copies | |
| enumerable_thread_specific(const T &_exemplar) : my_finit_callback(
0) { | | enumerable_thread_specific(const T &_exemplar) : my_finit_callback(
0) { | |
| my_exemplar_ptr = create_exemplar(_exemplar); | | my_exemplar_ptr = create_exemplar(_exemplar); | |
| my_tls_manager::create_key(my_key); | | my_tls_manager::create_key(my_key); | |
| } | | } | |
| | | | |
| //! Destructor | | //! Destructor | |
| ~enumerable_thread_specific() { | | ~enumerable_thread_specific() { | |
|
| | | unconstruct_locals(); | |
| my_tls_manager::destroy_key(my_key); | | my_tls_manager::destroy_key(my_key); | |
| if(my_finit_callback) { | | if(my_finit_callback) { | |
| my_finit_callback->destroy(); | | my_finit_callback->destroy(); | |
| } | | } | |
| if(my_exemplar_ptr) | | if(my_exemplar_ptr) | |
| { | | { | |
| free_exemplar(my_exemplar_ptr); | | free_exemplar(my_exemplar_ptr); | |
| } | | } | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 631 | | skipping to change at line 672 | |
| | | | |
| // see if the table entry can be found by accessor | | // see if the table entry can be found by accessor | |
| typename thread_to_index_type::accessor a; | | typename thread_to_index_type::accessor a; | |
| if(!my_hash_tbl.insert(a, my_t_key)) { | | if(!my_hash_tbl.insert(a, my_t_key)) { | |
| exists = true; | | exists = true; | |
| local_index = a->second; | | local_index = a->second; | |
| } | | } | |
| else { | | else { | |
| // create new entry | | // create new entry | |
| exists = false; | | exists = false; | |
|
| if(my_finit_callback) { | | | |
| // convert iterator to array index | | | |
| #if TBB_DEPRECATED | | #if TBB_DEPRECATED | |
|
| local_index = my_locals.push_back(my_finit_call
back->apply()); | | local_index = my_locals.push_back(padded_element())
; | |
| #else | | #else | |
|
| local_index = my_locals.push_back(my_finit_call
back->apply()) - my_locals.begin(); | | local_index = my_locals.push_back(padded_element())
- my_locals.begin(); | |
| #endif | | #endif | |
|
| | | pointer lref = reinterpret_cast<T*>((my_locals[loc | |
| | | al_index].value)); | |
| | | if(my_finit_callback) { | |
| | | new(lref) T(my_finit_callback->apply()); | |
| | | } | |
| | | else if(my_exemplar_ptr) { | |
| | | pointer t_exemp = reinterpret_cast<T *>(&(my_ex | |
| | | emplar_ptr->value)); | |
| | | new(lref) T(*t_exemp); | |
| } | | } | |
| else { | | else { | |
|
| // convert iterator to array index | | new(lref) T(); | |
| #if TBB_DEPRECATED | | | |
| local_index = my_locals.push_back(*my_exemplar_ | | | |
| ptr); | | | |
| #else | | | |
| local_index = my_locals.push_back(*my_exemplar_ | | | |
| ptr) - my_locals.begin(); | | | |
| #endif | | | |
| } | | } | |
| // insert into hash table | | // insert into hash table | |
| a->second = local_index; | | a->second = local_index; | |
| } | | } | |
| } | | } | |
| } | | } | |
| | | | |
|
| reference local_ref = (my_locals[local_index].value); | | pointer local_ref = reinterpret_cast<T*>((my_locals[local_index | |
| my_tls_manager::set_tls( my_key, static_cast<void *>(&local_ref | | ].value)); | |
| ) ); | | my_tls_manager::set_tls( my_key, static_cast<void *>(local_ref) | |
| return local_ref; | | ); | |
| | | return *local_ref; | |
| } // local | | } // local | |
| | | | |
| //! Get the number of local copies | | //! Get the number of local copies | |
| size_type size() const { return my_locals.size(); } | | size_type size() const { return my_locals.size(); } | |
| | | | |
| //! true if there have been no local copies created | | //! true if there have been no local copies created | |
| bool empty() const { return my_locals.empty(); } | | bool empty() const { return my_locals.empty(); } | |
| | | | |
| //! begin iterator | | //! begin iterator | |
| iterator begin() { return iterator( my_locals, 0 ); } | | iterator begin() { return iterator( my_locals, 0 ); } | |
| | | | |
| skipping to change at line 681 | | skipping to change at line 722 | |
| | | | |
| //! end const iterator | | //! end const iterator | |
| const_iterator end() const { return const_iterator(my_locals, my_lo
cals.size()); } | | const_iterator end() const { return const_iterator(my_locals, my_lo
cals.size()); } | |
| | | | |
| //! Get range for parallel algorithms | | //! Get range for parallel algorithms | |
| range_type range( size_t grainsize=1 ) { return range_type( begin()
, end(), grainsize ); } | | range_type range( size_t grainsize=1 ) { return range_type( begin()
, end(), grainsize ); } | |
| | | | |
| //! Get const range for parallel algorithms | | //! Get const range for parallel algorithms | |
| const_range_type range( size_t grainsize=1 ) const { return const_r
ange_type( begin(), end(), grainsize ); } | | const_range_type range( size_t grainsize=1 ) const { return const_r
ange_type( begin(), end(), grainsize ); } | |
| | | | |
|
| | | void unconstruct_locals() { | |
| | | for(typename internal_collection_type::iterator cvi = my_locals | |
| | | .begin(); cvi != my_locals.end(); ++cvi) { | |
| | | cvi->unconstruct(); | |
| | | } | |
| | | } | |
| | | | |
| //! Destroys local copies | | //! Destroys local copies | |
| void clear() { | | void clear() { | |
|
| | | unconstruct_locals(); | |
| my_locals.clear(); | | my_locals.clear(); | |
| my_hash_tbl.clear(); | | my_hash_tbl.clear(); | |
| reset_key(); | | reset_key(); | |
| // callback is not destroyed | | // callback is not destroyed | |
| // exemplar is not destroyed | | // exemplar is not destroyed | |
| } | | } | |
| | | | |
| // STL container methods | | // STL container methods | |
| // copy constructor | | // copy constructor | |
| | | | |
| private: | | private: | |
| | | | |
| template<typename U, typename A2, ets_key_usage_type C2> | | template<typename U, typename A2, ets_key_usage_type C2> | |
| void | | void | |
| internal_copy_construct( const enumerable_thread_specific<U, A2, C2
>& other) { | | internal_copy_construct( const enumerable_thread_specific<U, A2, C2
>& other) { | |
| typedef typename tbb::enumerable_thread_specific<U, A2, C2> oth
er_type; | | typedef typename tbb::enumerable_thread_specific<U, A2, C2> oth
er_type; | |
| for(typename other_type::const_iterator ci = other.begin(); ci
!= other.end(); ++ci) { | | for(typename other_type::const_iterator ci = other.begin(); ci
!= other.end(); ++ci) { | |
|
| my_locals.push_back(*ci); | | hash_table_index_type local_index; | |
| | | #if TBB_DEPRECATED | |
| | | local_index = my_locals.push_back(padded_element()); | |
| | | #else | |
| | | local_index = my_locals.push_back(padded_element()) - my_lo | |
| | | cals.begin(); | |
| | | #endif | |
| | | (void) new(my_locals[local_index].value) T(*ci); | |
| } | | } | |
| if(other.my_finit_callback) { | | if(other.my_finit_callback) { | |
| my_finit_callback = other.my_finit_callback->make_copy(); | | my_finit_callback = other.my_finit_callback->make_copy(); | |
| } | | } | |
| else { | | else { | |
| my_finit_callback = 0; | | my_finit_callback = 0; | |
| } | | } | |
| if(other.my_exemplar_ptr) { | | if(other.my_exemplar_ptr) { | |
|
| my_exemplar_ptr = create_exemplar(other.my_exemplar_ptr->va | | pointer local_ref = reinterpret_cast<T*>(other.my_exemplar_ | |
| lue); | | ptr->value); | |
| | | my_exemplar_ptr = create_exemplar(*local_ref); | |
| } | | } | |
| else { | | else { | |
| my_exemplar_ptr = 0; | | my_exemplar_ptr = 0; | |
| } | | } | |
| my_tls_manager::create_key(my_key); | | my_tls_manager::create_key(my_key); | |
| } | | } | |
| | | | |
| public: | | public: | |
| | | | |
| template<typename U, typename Alloc, ets_key_usage_type Cachetype> | | template<typename U, typename Alloc, ets_key_usage_type Cachetype> | |
| enumerable_thread_specific( const enumerable_thread_specific<U, All
oc, Cachetype>& other ) : my_hash_tbl(other.my_hash_tbl) | | enumerable_thread_specific( const enumerable_thread_specific<U, All
oc, Cachetype>& other ) : my_hash_tbl(other.my_hash_tbl) | |
|
| { // Have to do push_back because the contained elements are not
necessarily assignable. | | { | |
| internal_copy_construct(other); | | internal_copy_construct(other); | |
| } | | } | |
| | | | |
|
| // non-templatized version | | | |
| enumerable_thread_specific( const enumerable_thread_specific& other
) : my_hash_tbl(other.my_hash_tbl) | | enumerable_thread_specific( const enumerable_thread_specific& other
) : my_hash_tbl(other.my_hash_tbl) | |
| { | | { | |
| internal_copy_construct(other); | | internal_copy_construct(other); | |
| } | | } | |
| | | | |
| private: | | private: | |
| | | | |
| template<typename U, typename A2, ets_key_usage_type C2> | | template<typename U, typename A2, ets_key_usage_type C2> | |
| enumerable_thread_specific & | | enumerable_thread_specific & | |
| internal_assign(const enumerable_thread_specific<U, A2, C2>& other)
{ | | internal_assign(const enumerable_thread_specific<U, A2, C2>& other)
{ | |
| typedef typename tbb::enumerable_thread_specific<U, A2, C2> oth
er_type; | | typedef typename tbb::enumerable_thread_specific<U, A2, C2> oth
er_type; | |
| if(static_cast<void *>( this ) != static_cast<const void *>( &o
ther )) { | | if(static_cast<void *>( this ) != static_cast<const void *>( &o
ther )) { | |
| this->clear(); // resets TLS key | | this->clear(); // resets TLS key | |
| my_hash_tbl = other.my_hash_tbl; | | my_hash_tbl = other.my_hash_tbl; | |
|
| // cannot use assign because T may not be assignable. | | | |
| for(typename other_type::const_iterator ci = other.begin();
ci != other.end(); ++ci) { | | for(typename other_type::const_iterator ci = other.begin();
ci != other.end(); ++ci) { | |
|
| my_locals.push_back(*ci); | | hash_table_index_type local_index; | |
| | | #if TBB_DEPRECATED | |
| | | local_index = my_locals.push_back(padded_element()) | |
| | | ; | |
| | | #else | |
| | | local_index = my_locals.push_back(padded_element()) | |
| | | - my_locals.begin(); | |
| | | #endif | |
| | | (void) new(my_locals[local_index].value) T(*ci); | |
| } | | } | |
| | | | |
| if(my_finit_callback) { | | if(my_finit_callback) { | |
| my_finit_callback->destroy(); | | my_finit_callback->destroy(); | |
| my_finit_callback = 0; | | my_finit_callback = 0; | |
| } | | } | |
| if(my_exemplar_ptr) { | | if(my_exemplar_ptr) { | |
| free_exemplar(my_exemplar_ptr); | | free_exemplar(my_exemplar_ptr); | |
| my_exemplar_ptr = 0; | | my_exemplar_ptr = 0; | |
| } | | } | |
| if(other.my_finit_callback) { | | if(other.my_finit_callback) { | |
| my_finit_callback = other.my_finit_callback->make_copy(
); | | my_finit_callback = other.my_finit_callback->make_copy(
); | |
| } | | } | |
| | | | |
| if(other.my_exemplar_ptr) { | | if(other.my_exemplar_ptr) { | |
|
| my_exemplar_ptr = create_exemplar(other.my_exemplar_ptr | | pointer local_ref = reinterpret_cast<T*>(other.my_exemp | |
| ->value); | | lar_ptr->value); | |
| | | my_exemplar_ptr = create_exemplar(*local_ref); | |
| } | | } | |
| } | | } | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| public: | | public: | |
| | | | |
| // assignment | | // assignment | |
| enumerable_thread_specific& operator=(const enumerable_thread_speci
fic& other) { | | enumerable_thread_specific& operator=(const enumerable_thread_speci
fic& other) { | |
| return internal_assign(other); | | return internal_assign(other); | |
| } | | } | |
| | | | |
| template<typename U, typename Alloc, ets_key_usage_type Cachetype> | | template<typename U, typename Alloc, ets_key_usage_type Cachetype> | |
| enumerable_thread_specific& operator=(const enumerable_thread_speci
fic<U, Alloc, Cachetype>& other) | | enumerable_thread_specific& operator=(const enumerable_thread_speci
fic<U, Alloc, Cachetype>& other) | |
| { | | { | |
| return internal_assign(other); | | return internal_assign(other); | |
| } | | } | |
| | | | |
|
| private: | | | |
| | | | |
| // combine_func_t has signature T(T,T) or T(const T&, const T&) | | | |
| template <typename combine_func_t> | | | |
| T internal_combine(typename internal_collection_type::const_range_t | | | |
| ype r, combine_func_t f_combine) { | | | |
| if(r.is_divisible()) { | | | |
| typename internal_collection_type::const_range_type r2(r,sp | | | |
| lit()); | | | |
| return f_combine(internal_combine(r2, f_combine), internal_ | | | |
| combine(r, f_combine)); | | | |
| } | | | |
| if(r.size() == 1) { | | | |
| return r.begin()->value; | | | |
| } | | | |
| typename internal_collection_type::const_iterator i2 = r.begin( | | | |
| ); | | | |
| ++i2; | | | |
| return f_combine(r.begin()->value, i2->value); | | | |
| } | | | |
| | | | |
| public: | | | |
| | | | |
| // combine_func_t has signature T(T,T) or T(const T&, const T&) | | // combine_func_t has signature T(T,T) or T(const T&, const T&) | |
| template <typename combine_func_t> | | template <typename combine_func_t> | |
| T combine(combine_func_t f_combine) { | | T combine(combine_func_t f_combine) { | |
|
| if(my_locals.begin() == my_locals.end()) { | | if(begin() == end()) { | |
| if(my_finit_callback) { | | if(my_finit_callback) { | |
| return my_finit_callback->apply(); | | return my_finit_callback->apply(); | |
| } | | } | |
|
| return (*my_exemplar_ptr).value; | | pointer local_ref = reinterpret_cast<T*>((my_exemplar_ptr-> | |
| | | value)); | |
| | | return T(*local_ref); | |
| } | | } | |
|
| typename internal_collection_type::const_range_type r(my_locals | | const_iterator ci = begin(); | |
| .begin(), my_locals.end(), (size_t)2); | | T my_result = *ci; | |
| return internal_combine(r, f_combine); | | while(++ci != end()) | |
| | | my_result = f_combine( my_result, *ci ); | |
| | | return my_result; | |
| } | | } | |
| | | | |
| // combine_func_t has signature void(T) or void(const T&) | | // combine_func_t has signature void(T) or void(const T&) | |
| template <typename combine_func_t> | | template <typename combine_func_t> | |
| void combine_each(combine_func_t f_combine) { | | void combine_each(combine_func_t f_combine) { | |
| for(const_iterator ci = begin(); ci != end(); ++ci) { | | for(const_iterator ci = begin(); ci != end(); ++ci) { | |
| f_combine( *ci ); | | f_combine( *ci ); | |
| } | | } | |
| } | | } | |
|
| | | | |
| }; // enumerable_thread_specific | | }; // enumerable_thread_specific | |
| | | | |
| template< typename Container > | | template< typename Container > | |
| class flattened2d { | | class flattened2d { | |
| | | | |
| // This intermediate typedef is to address issues with VC7.1 compil
ers | | // This intermediate typedef is to address issues with VC7.1 compil
ers | |
| typedef typename Container::value_type conval_type; | | typedef typename Container::value_type conval_type; | |
| | | | |
| public: | | public: | |
| | | | |
| | | | |
End of changes. 36 change blocks. |
| 91 lines changed or deleted | | 152 lines changed or added | |
|
| parallel_for.h | | parallel_for.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 36 | | skipping to change at line 36 | |
| the GNU General Public License. | | the GNU General Public License. | |
| */ | | */ | |
| | | | |
| #ifndef __TBB_parallel_for_H | | #ifndef __TBB_parallel_for_H | |
| #define __TBB_parallel_for_H | | #define __TBB_parallel_for_H | |
| | | | |
| #include "task.h" | | #include "task.h" | |
| #include "partitioner.h" | | #include "partitioner.h" | |
| #include "blocked_range.h" | | #include "blocked_range.h" | |
| #include <new> | | #include <new> | |
|
| #include <stdexcept> // std::invalid_argument | | #include "tbb_exception.h" | |
| #include <string> // std::invalid_argument text | | | |
| | | | |
| namespace tbb { | | namespace tbb { | |
| | | | |
| //! @cond INTERNAL | | //! @cond INTERNAL | |
| namespace internal { | | namespace internal { | |
| | | | |
| //! Task type used in parallel_for | | //! Task type used in parallel_for | |
| /** @ingroup algorithms */ | | /** @ingroup algorithms */ | |
| template<typename Range, typename Body, typename Partitioner> | | template<typename Range, typename Body, typename Partitioner> | |
| class start_for: public task { | | class start_for: public task { | |
| | | | |
| skipping to change at line 62 | | skipping to change at line 61 | |
| | | | |
| //! Constructor for root task. | | //! Constructor for root task. | |
| start_for( const Range& range, const Body& body, Partitioner& parti
tioner ) : | | start_for( const Range& range, const Body& body, Partitioner& parti
tioner ) : | |
| my_range(range), | | my_range(range), | |
| my_body(body), | | my_body(body), | |
| my_partition(partitioner) | | my_partition(partitioner) | |
| { | | { | |
| } | | } | |
| //! Splitting constructor used to generate children. | | //! Splitting constructor used to generate children. | |
| /** this becomes left child. Newly constructed object is right chi
ld. */ | | /** this becomes left child. Newly constructed object is right chi
ld. */ | |
|
| start_for( start_for& parent, split ) : | | start_for( start_for& parent_, split ) : | |
| my_range(parent.my_range,split()), | | my_range(parent_.my_range,split()), | |
| my_body(parent.my_body), | | my_body(parent_.my_body), | |
| my_partition(parent.my_partition,split()) | | my_partition(parent_.my_partition,split()) | |
| { | | { | |
| my_partition.set_affinity(*this); | | my_partition.set_affinity(*this); | |
| } | | } | |
| //! Update affinity info, if any. | | //! Update affinity info, if any. | |
| /*override*/ void note_affinity( affinity_id id ) { | | /*override*/ void note_affinity( affinity_id id ) { | |
| my_partition.note_affinity( id ); | | my_partition.note_affinity( id ); | |
| } | | } | |
| public: | | public: | |
| static void run( const Range& range, const Body& body, const Parti
tioner& partitioner ) { | | static void run( const Range& range, const Body& body, const Parti
tioner& partitioner ) { | |
| if( !range.empty() ) { | | if( !range.empty() ) { | |
|
| #if !__TBB_EXCEPTIONS || TBB_JOIN_OUTER_TASK_GROUP | | #if !__TBB_TASK_GROUP_CONTEXT || TBB_JOIN_OUTER_TASK_GROUP | |
| start_for& a = *new(task::allocate_root()) start_for(range,
body,const_cast<Partitioner&>(partitioner)); | | start_for& a = *new(task::allocate_root()) start_for(range,
body,const_cast<Partitioner&>(partitioner)); | |
| #else | | #else | |
| // Bound context prevents exceptions from body to affect ne
sting or sibling algorithms, | | // Bound context prevents exceptions from body to affect ne
sting or sibling algorithms, | |
| // and allows users to handle exceptions safely by wrapping
parallel_for in the try-block. | | // and allows users to handle exceptions safely by wrapping
parallel_for in the try-block. | |
| task_group_context context; | | task_group_context context; | |
| start_for& a = *new(task::allocate_root(context)) start_for
(range,body,const_cast<Partitioner&>(partitioner)); | | start_for& a = *new(task::allocate_root(context)) start_for
(range,body,const_cast<Partitioner&>(partitioner)); | |
|
| #endif /* __TBB_EXCEPTIONS && !TBB_JOIN_OUTER_TASK_GROUP */ | | #endif /* __TBB_TASK_GROUP_CONTEXT && !TBB_JOIN_OUTER_TASK_GROUP */ | |
| task::spawn_root_and_wait(a); | | task::spawn_root_and_wait(a); | |
| } | | } | |
| } | | } | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| static void run( const Range& range, const Body& body, const Parti
tioner& partitioner, task_group_context& context ) { | | static void run( const Range& range, const Body& body, const Parti
tioner& partitioner, task_group_context& context ) { | |
| if( !range.empty() ) { | | if( !range.empty() ) { | |
| start_for& a = *new(task::allocate_root(context)) start_for
(range,body,const_cast<Partitioner&>(partitioner)); | | start_for& a = *new(task::allocate_root(context)) start_for
(range,body,const_cast<Partitioner&>(partitioner)); | |
| task::spawn_root_and_wait(a); | | task::spawn_root_and_wait(a); | |
| } | | } | |
| } | | } | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| }; | | }; | |
| | | | |
| template<typename Range, typename Body, typename Partitioner> | | template<typename Range, typename Body, typename Partitioner> | |
| task* start_for<Range,Body,Partitioner>::execute() { | | task* start_for<Range,Body,Partitioner>::execute() { | |
| if( !my_range.is_divisible() || my_partition.should_execute_range(*
this) ) { | | if( !my_range.is_divisible() || my_partition.should_execute_range(*
this) ) { | |
| my_body( my_range ); | | my_body( my_range ); | |
|
| return my_partition.continue_after_execute_range(*this); | | return my_partition.continue_after_execute_range(); | |
| } else { | | } else { | |
| empty_task& c = *new( this->allocate_continuation() ) empty_tas
k; | | empty_task& c = *new( this->allocate_continuation() ) empty_tas
k; | |
| recycle_as_child_of(c); | | recycle_as_child_of(c); | |
| c.set_ref_count(2); | | c.set_ref_count(2); | |
| bool delay = my_partition.decide_whether_to_delay(); | | bool delay = my_partition.decide_whether_to_delay(); | |
| start_for& b = *new( c.allocate_child() ) start_for(*this,split
()); | | start_for& b = *new( c.allocate_child() ) start_for(*this,split
()); | |
|
| my_partition.spawn_or_delay(delay,*this,b); | | my_partition.spawn_or_delay(delay,b); | |
| return this; | | return this; | |
| } | | } | |
| } | | } | |
| } // namespace internal | | } // namespace internal | |
| //! @endcond | | //! @endcond | |
| | | | |
| // Requirements on Range concept are documented in blocked_range.h | | // Requirements on Range concept are documented in blocked_range.h | |
| | | | |
| /** \page parallel_for_body_req Requirements on parallel_for body | | /** \page parallel_for_body_req Requirements on parallel_for body | |
| Class \c Body implementing the concept of parallel_for body must define
: | | Class \c Body implementing the concept of parallel_for body must define
: | |
| | | | |
| skipping to change at line 156 | | skipping to change at line 155 | |
| internal::start_for<Range,Body,auto_partitioner>::run(range,body,partit
ioner); | | internal::start_for<Range,Body,auto_partitioner>::run(range,body,partit
ioner); | |
| } | | } | |
| | | | |
| //! Parallel iteration over range with affinity_partitioner. | | //! Parallel iteration over range with affinity_partitioner. | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_for( const Range& range, const Body& body, affinity_partition
er& partitioner ) { | | void parallel_for( const Range& range, const Body& body, affinity_partition
er& partitioner ) { | |
| internal::start_for<Range,Body,affinity_partitioner>::run(range,body,pa
rtitioner); | | internal::start_for<Range,Body,affinity_partitioner>::run(range,body,pa
rtitioner); | |
| } | | } | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| //! Parallel iteration over range with simple partitioner and user-supplied
context. | | //! Parallel iteration over range with simple partitioner and user-supplied
context. | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_for( const Range& range, const Body& body, const simple_parti
tioner& partitioner, task_group_context& context ) { | | void parallel_for( const Range& range, const Body& body, const simple_parti
tioner& partitioner, task_group_context& context ) { | |
| internal::start_for<Range,Body,simple_partitioner>::run(range, body, pa
rtitioner, context); | | internal::start_for<Range,Body,simple_partitioner>::run(range, body, pa
rtitioner, context); | |
| } | | } | |
| | | | |
| //! Parallel iteration over range with auto_partitioner and user-supplied c
ontext. | | //! Parallel iteration over range with auto_partitioner and user-supplied c
ontext. | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_for( const Range& range, const Body& body, const auto_partiti
oner& partitioner, task_group_context& context ) { | | void parallel_for( const Range& range, const Body& body, const auto_partiti
oner& partitioner, task_group_context& context ) { | |
| internal::start_for<Range,Body,auto_partitioner>::run(range, body, part
itioner, context); | | internal::start_for<Range,Body,auto_partitioner>::run(range, body, part
itioner, context); | |
| } | | } | |
| | | | |
| //! Parallel iteration over range with affinity_partitioner and user-suppli
ed context. | | //! Parallel iteration over range with affinity_partitioner and user-suppli
ed context. | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_for( const Range& range, const Body& body, affinity_partition
er& partitioner, task_group_context& context ) { | | void parallel_for( const Range& range, const Body& body, affinity_partition
er& partitioner, task_group_context& context ) { | |
| internal::start_for<Range,Body,affinity_partitioner>::run(range,body,pa
rtitioner, context); | | internal::start_for<Range,Body,affinity_partitioner>::run(range,body,pa
rtitioner, context); | |
| } | | } | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| //@} | | //@} | |
| | | | |
| //! @cond INTERNAL | | //! @cond INTERNAL | |
| namespace internal { | | namespace internal { | |
| //! Calls the function with values from range [begin, end) with a step
provided | | //! Calls the function with values from range [begin, end) with a step
provided | |
| template<typename Function, typename Index> | | template<typename Function, typename Index> | |
| class parallel_for_body : internal::no_assign { | | class parallel_for_body : internal::no_assign { | |
|
| Function &my_func; | | const Function &my_func; | |
| const Index my_begin; | | const Index my_begin; | |
| const Index my_step; | | const Index my_step; | |
| public: | | public: | |
|
| parallel_for_body( Function& _func, Index& _begin, Index& _step) | | parallel_for_body( const Function& _func, Index& _begin, Index& _step) | |
| : my_func(_func), my_begin(_begin), my_step(_step) {} | | : my_func(_func), my_begin(_begin), my_step(_step) {} | |
| | | | |
| void operator()( tbb::blocked_range<Index>& r ) const { | | void operator()( tbb::blocked_range<Index>& r ) const { | |
| for( Index i = r.begin(), k = my_begin + i * my_step; i < r.end();
i++, k = k + my_step) | | for( Index i = r.begin(), k = my_begin + i * my_step; i < r.end();
i++, k = k + my_step) | |
| my_func( k ); | | my_func( k ); | |
| } | | } | |
| }; | | }; | |
| } // namespace internal | | } // namespace internal | |
| //! @endcond | | //! @endcond | |
| | | | |
| namespace strict_ppl { | | namespace strict_ppl { | |
| | | | |
| //@{ | | //@{ | |
| //! Parallel iteration over a range of integers with a step provided | | //! Parallel iteration over a range of integers with a step provided | |
| template <typename Index, typename Function> | | template <typename Index, typename Function> | |
|
| Function parallel_for(Index first, Index last, Index step, Function f) { | | void parallel_for(Index first, Index last, Index step, const Function& f) { | |
| tbb::task_group_context context; | | tbb::task_group_context context; | |
|
| return parallel_for(first, last, step, f, context); | | parallel_for(first, last, step, f, context); | |
| } | | } | |
| template <typename Index, typename Function> | | template <typename Index, typename Function> | |
|
| Function parallel_for(Index first, Index last, Index step, Function f, tbb: | | void parallel_for(Index first, Index last, Index step, const Function& f, t | |
| :task_group_context &context) { | | bb::task_group_context &context) { | |
| if (step <= 0 ) throw std::invalid_argument("step should be positive"); | | if (step <= 0 ) | |
| | | internal::throw_exception(internal::eid_nonpositive_step); // throw | |
| if (last > first) { | | s std::invalid_argument | |
| | | else if (last > first) { | |
| | | // Above "else" is necessary to prevent "potential divide by zero" | |
| | | warning | |
| Index end = (last - first) / step; | | Index end = (last - first) / step; | |
| if (first + end * step < last) end++; | | if (first + end * step < last) end++; | |
| tbb::blocked_range<Index> range(static_cast<Index>(0), end); | | tbb::blocked_range<Index> range(static_cast<Index>(0), end); | |
| internal::parallel_for_body<Function, Index> body(f, first, step); | | internal::parallel_for_body<Function, Index> body(f, first, step); | |
| tbb::parallel_for(range, body, tbb::auto_partitioner(), context); | | tbb::parallel_for(range, body, tbb::auto_partitioner(), context); | |
| } | | } | |
|
| return f; | | | |
| } | | } | |
|
| | | //! Parallel iteration over a range of integers with a default step value | |
| | | template <typename Index, typename Function> | |
| | | void parallel_for(Index first, Index last, const Function& f) { | |
| | | tbb::task_group_context context; | |
| | | parallel_for(first, last, static_cast<Index>(1), f, context); | |
| | | } | |
| | | template <typename Index, typename Function> | |
| | | void parallel_for(Index first, Index last, const Function& f, tbb::task_gro | |
| | | up_context &context) { | |
| | | parallel_for(first, last, static_cast<Index>(1), f, context); | |
| | | } | |
| | | | |
| //@} | | //@} | |
| | | | |
| } // namespace strict_ppl | | } // namespace strict_ppl | |
| | | | |
| using strict_ppl::parallel_for; | | using strict_ppl::parallel_for; | |
| | | | |
| } // namespace tbb | | } // namespace tbb | |
| | | | |
| #endif /* __TBB_parallel_for_H */ | | #endif /* __TBB_parallel_for_H */ | |
| | | | |
End of changes. 18 change blocks. |
| 25 lines changed or deleted | | 38 lines changed or added | |
|
| parallel_invoke.h | | parallel_invoke.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 42 | | skipping to change at line 42 | |
| #include "task.h" | | #include "task.h" | |
| | | | |
| namespace tbb { | | namespace tbb { | |
| | | | |
| //! @cond INTERNAL | | //! @cond INTERNAL | |
| namespace internal { | | namespace internal { | |
| // Simple task object, executing user method | | // Simple task object, executing user method | |
| template<typename function> | | template<typename function> | |
| class function_invoker : public task{ | | class function_invoker : public task{ | |
| public: | | public: | |
|
| function_invoker(function& _function) : my_function(_function) {} | | function_invoker(const function& _function) : my_function(_function
) {} | |
| private: | | private: | |
|
| function &my_function; | | const function &my_function; | |
| /*override*/ | | /*override*/ | |
| task* execute() | | task* execute() | |
| { | | { | |
| my_function(); | | my_function(); | |
| return NULL; | | return NULL; | |
| } | | } | |
| }; | | }; | |
| | | | |
| // The class spawns two or three child tasks | | // The class spawns two or three child tasks | |
| template <size_t N, typename function1, typename function2, typename fu
nction3> | | template <size_t N, typename function1, typename function2, typename fu
nction3> | |
| class spawner : public task { | | class spawner : public task { | |
| private: | | private: | |
|
| function1& my_func1; | | const function1& my_func1; | |
| function2& my_func2; | | const function2& my_func2; | |
| function3& my_func3; | | const function3& my_func3; | |
| bool is_recycled; | | bool is_recycled; | |
| | | | |
| task* execute (){ | | task* execute (){ | |
| if(is_recycled){ | | if(is_recycled){ | |
| return NULL; | | return NULL; | |
| }else{ | | }else{ | |
| __TBB_ASSERT(N==2 || N==3, "Number of arguments passed to s
pawner is wrong"); | | __TBB_ASSERT(N==2 || N==3, "Number of arguments passed to s
pawner is wrong"); | |
| set_ref_count(N); | | set_ref_count(N); | |
| recycle_as_safe_continuation(); | | recycle_as_safe_continuation(); | |
| internal::function_invoker<function2>* invoker2 = new (allo
cate_child()) internal::function_invoker<function2>(my_func2); | | internal::function_invoker<function2>* invoker2 = new (allo
cate_child()) internal::function_invoker<function2>(my_func2); | |
| | | | |
| skipping to change at line 85 | | skipping to change at line 85 | |
| __TBB_ASSERT(invoker3, "Child task allocation failed"); | | __TBB_ASSERT(invoker3, "Child task allocation failed"); | |
| spawn(*invoker3); | | spawn(*invoker3); | |
| } | | } | |
| my_func1(); | | my_func1(); | |
| is_recycled = true; | | is_recycled = true; | |
| return NULL; | | return NULL; | |
| } | | } | |
| } // execute | | } // execute | |
| | | | |
| public: | | public: | |
|
| spawner(function1& _func1, function2& _func2, function3& _func3) :
my_func1(_func1), my_func2(_func2), my_func3(_func3), is_recycled(false) {} | | spawner(const function1& _func1, const function2& _func2, const fun
ction3& _func3) : my_func1(_func1), my_func2(_func2), my_func3(_func3), is_
recycled(false) {} | |
| }; | | }; | |
| | | | |
| // Creates and spawns child tasks | | // Creates and spawns child tasks | |
| class parallel_invoke_helper : public empty_task { | | class parallel_invoke_helper : public empty_task { | |
| public: | | public: | |
| // Dummy functor class | | // Dummy functor class | |
| class parallel_invoke_noop { | | class parallel_invoke_noop { | |
| public: | | public: | |
| void operator() () const {} | | void operator() () const {} | |
| }; | | }; | |
| // Creates a helper object with user-defined number of children exp
ected | | // Creates a helper object with user-defined number of children exp
ected | |
| parallel_invoke_helper(int number_of_children) | | parallel_invoke_helper(int number_of_children) | |
| { | | { | |
| set_ref_count(number_of_children + 1); | | set_ref_count(number_of_children + 1); | |
| } | | } | |
| // Adds child task and spawns it | | // Adds child task and spawns it | |
| template <typename function> | | template <typename function> | |
|
| void add_child (function &_func) | | void add_child (const function &_func) | |
| { | | { | |
| internal::function_invoker<function>* invoker = new (allocate_c
hild()) internal::function_invoker<function>(_func); | | internal::function_invoker<function>* invoker = new (allocate_c
hild()) internal::function_invoker<function>(_func); | |
| __TBB_ASSERT(invoker, "Child task allocation failed"); | | __TBB_ASSERT(invoker, "Child task allocation failed"); | |
| spawn(*invoker); | | spawn(*invoker); | |
| } | | } | |
| | | | |
| // Adds a task with multiple child tasks and spawns it | | // Adds a task with multiple child tasks and spawns it | |
| // two arguments | | // two arguments | |
| template <typename function1, typename function2> | | template <typename function1, typename function2> | |
|
| void add_children (function1& _func1, function2& _func2) | | void add_children (const function1& _func1, const function2& _func2
) | |
| { | | { | |
| // The third argument is dummy, it is ignored actually. | | // The third argument is dummy, it is ignored actually. | |
| parallel_invoke_noop noop; | | parallel_invoke_noop noop; | |
| internal::spawner<2, function1, function2, parallel_invoke_noop
>& sub_root = *new(allocate_child())internal::spawner<2, function1, functio
n2, parallel_invoke_noop>(_func1, _func2, noop); | | internal::spawner<2, function1, function2, parallel_invoke_noop
>& sub_root = *new(allocate_child())internal::spawner<2, function1, functio
n2, parallel_invoke_noop>(_func1, _func2, noop); | |
| spawn(sub_root); | | spawn(sub_root); | |
| } | | } | |
| // three arguments | | // three arguments | |
| template <typename function1, typename function2, typename function
3> | | template <typename function1, typename function2, typename function
3> | |
|
| void add_children (function1& _func1, function2& _func2, function3&
_func3) | | void add_children (const function1& _func1, const function2& _func2
, const function3& _func3) | |
| { | | { | |
| internal::spawner<3, function1, function2, function3>& sub_root
= *new(allocate_child())internal::spawner<3, function1, function2, functio
n3>(_func1, _func2, _func3); | | internal::spawner<3, function1, function2, function3>& sub_root
= *new(allocate_child())internal::spawner<3, function1, function2, functio
n3>(_func1, _func2, _func3); | |
| spawn(sub_root); | | spawn(sub_root); | |
| } | | } | |
| | | | |
| // Waits for all child tasks | | // Waits for all child tasks | |
| template <typename F0> | | template <typename F0> | |
|
| void run_and_finish(F0& f0) | | void run_and_finish(const F0& f0) | |
| { | | { | |
| internal::function_invoker<F0>* invoker = new (allocate_child()
) internal::function_invoker<F0>(f0); | | internal::function_invoker<F0>* invoker = new (allocate_child()
) internal::function_invoker<F0>(f0); | |
| __TBB_ASSERT(invoker, "Child task allocation failed"); | | __TBB_ASSERT(invoker, "Child task allocation failed"); | |
| spawn_and_wait_for_all(*invoker); | | spawn_and_wait_for_all(*invoker); | |
| } | | } | |
| }; | | }; | |
| // The class destroys root if exception occured as well as in normal ca
se | | // The class destroys root if exception occured as well as in normal ca
se | |
| class parallel_invoke_cleaner: internal::no_copy { | | class parallel_invoke_cleaner: internal::no_copy { | |
| public: | | public: | |
| parallel_invoke_cleaner(int number_of_children, tbb::task_group_con
text& context) : root(*new(task::allocate_root(context)) internal::parallel
_invoke_helper(number_of_children)) | | parallel_invoke_cleaner(int number_of_children, tbb::task_group_con
text& context) : root(*new(task::allocate_root(context)) internal::parallel
_invoke_helper(number_of_children)) | |
| | | | |
| skipping to change at line 159 | | skipping to change at line 159 | |
| | | | |
| /** \name parallel_invoke | | /** \name parallel_invoke | |
| **/ | | **/ | |
| //@{ | | //@{ | |
| //! Executes a list of tasks in parallel and waits for all tasks to complet
e. | | //! Executes a list of tasks in parallel and waits for all tasks to complet
e. | |
| /** @ingroup algorithms */ | | /** @ingroup algorithms */ | |
| | | | |
| // parallel_invoke with user-defined context | | // parallel_invoke with user-defined context | |
| // two arguments | | // two arguments | |
| template<typename F0, typename F1 > | | template<typename F0, typename F1 > | |
|
| void parallel_invoke(F0 f0, F1 f1, tbb::task_group_context& context) { | | void parallel_invoke(const F0& f0, const F1& f1, tbb::task_group_context& c
ontext) { | |
| internal::parallel_invoke_cleaner cleaner(2, context); | | internal::parallel_invoke_cleaner cleaner(2, context); | |
| internal::parallel_invoke_helper& root = cleaner.root; | | internal::parallel_invoke_helper& root = cleaner.root; | |
| | | | |
| root.add_child(f1); | | root.add_child(f1); | |
| | | | |
| root.run_and_finish(f0); | | root.run_and_finish(f0); | |
| } | | } | |
| | | | |
| // three arguments | | // three arguments | |
| template<typename F0, typename F1, typename F2 > | | template<typename F0, typename F1, typename F2 > | |
|
| void parallel_invoke(F0 f0, F1 f1, F2 f2, tbb::task_group_context& context)
{ | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, tbb::task_gr
oup_context& context) { | |
| internal::parallel_invoke_cleaner cleaner(3, context); | | internal::parallel_invoke_cleaner cleaner(3, context); | |
| internal::parallel_invoke_helper& root = cleaner.root; | | internal::parallel_invoke_helper& root = cleaner.root; | |
| | | | |
| root.add_child(f2); | | root.add_child(f2); | |
| root.add_child(f1); | | root.add_child(f1); | |
| | | | |
| root.run_and_finish(f0); | | root.run_and_finish(f0); | |
| } | | } | |
| | | | |
| // four arguments | | // four arguments | |
| template<typename F0, typename F1, typename F2, typename F3> | | template<typename F0, typename F1, typename F2, typename F3> | |
|
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, tbb::task_group_context& c | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| ontext) { | | , | |
| | | tbb::task_group_context& context) | |
| | | { | |
| internal::parallel_invoke_cleaner cleaner(4, context); | | internal::parallel_invoke_cleaner cleaner(4, context); | |
| internal::parallel_invoke_helper& root = cleaner.root; | | internal::parallel_invoke_helper& root = cleaner.root; | |
| | | | |
| root.add_child(f3); | | root.add_child(f3); | |
| root.add_child(f2); | | root.add_child(f2); | |
| root.add_child(f1); | | root.add_child(f1); | |
| | | | |
| root.run_and_finish(f0); | | root.run_and_finish(f0); | |
| } | | } | |
| | | | |
| // five arguments | | // five arguments | |
| template<typename F0, typename F1, typename F2, typename F3, typename F4 > | | template<typename F0, typename F1, typename F2, typename F3, typename F4 > | |
|
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, tbb::task_group_con | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| text& context) { | | , const F4& f4, | |
| | | tbb::task_group_context& context) | |
| | | { | |
| internal::parallel_invoke_cleaner cleaner(3, context); | | internal::parallel_invoke_cleaner cleaner(3, context); | |
| internal::parallel_invoke_helper& root = cleaner.root; | | internal::parallel_invoke_helper& root = cleaner.root; | |
| | | | |
| root.add_children(f4, f3); | | root.add_children(f4, f3); | |
| root.add_children(f2, f1); | | root.add_children(f2, f1); | |
| | | | |
| root.run_and_finish(f0); | | root.run_and_finish(f0); | |
| } | | } | |
| | | | |
| // six arguments | | // six arguments | |
|
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t | | template<typename F0, typename F1, typename F2, typename F3, typename F4, t | |
| ypename F5 > | | ypename F5> | |
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, tbb::task_gr | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| oup_context& context) { | | , const F4& f4, const F5& f5, | |
| | | tbb::task_group_context& context) | |
| | | { | |
| internal::parallel_invoke_cleaner cleaner(3, context); | | internal::parallel_invoke_cleaner cleaner(3, context); | |
| internal::parallel_invoke_helper& root = cleaner.root; | | internal::parallel_invoke_helper& root = cleaner.root; | |
| | | | |
| root.add_children(f5, f4, f3); | | root.add_children(f5, f4, f3); | |
| root.add_children(f2, f1); | | root.add_children(f2, f1); | |
| | | | |
| root.run_and_finish(f0); | | root.run_and_finish(f0); | |
| } | | } | |
| | | | |
| // seven arguments | | // seven arguments | |
|
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t | | template<typename F0, typename F1, typename F2, typename F3, typename F4, t | |
| ypename F5, typename F6 > | | ypename F5, typename F6> | |
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, tbb:: | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| task_group_context& context) { | | , const F4& f4, | |
| | | const F5& f5, const F6& f6, | |
| | | tbb::task_group_context& context) | |
| | | { | |
| internal::parallel_invoke_cleaner cleaner(3, context); | | internal::parallel_invoke_cleaner cleaner(3, context); | |
| internal::parallel_invoke_helper& root = cleaner.root; | | internal::parallel_invoke_helper& root = cleaner.root; | |
| | | | |
| root.add_children(f6, f5, f4); | | root.add_children(f6, f5, f4); | |
| root.add_children(f3, f2, f1); | | root.add_children(f3, f2, f1); | |
| | | | |
| root.run_and_finish(f0); | | root.run_and_finish(f0); | |
| } | | } | |
| | | | |
| // eight arguments | | // eight arguments | |
|
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t | | template<typename F0, typename F1, typename F2, typename F3, typename F4, | |
| ypename F5, typename F6, | | typename F5, typename F6, typename F7> | |
| typename F7> | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7 | | , const F4& f4, | |
| , tbb::task_group_context& context) { | | const F5& f5, const F6& f6, const F7& f7, | |
| | | tbb::task_group_context& context) | |
| | | { | |
| internal::parallel_invoke_cleaner cleaner(4, context); | | internal::parallel_invoke_cleaner cleaner(4, context); | |
| internal::parallel_invoke_helper& root = cleaner.root; | | internal::parallel_invoke_helper& root = cleaner.root; | |
| | | | |
| root.add_children(f7, f6, f5); | | root.add_children(f7, f6, f5); | |
| root.add_children(f4, f3); | | root.add_children(f4, f3); | |
| root.add_children(f2, f1); | | root.add_children(f2, f1); | |
| | | | |
| root.run_and_finish(f0); | | root.run_and_finish(f0); | |
| } | | } | |
| | | | |
| // nine arguments | | // nine arguments | |
|
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t | | template<typename F0, typename F1, typename F2, typename F3, typename F4, | |
| ypename F5, typename F6, | | typename F5, typename F6, typename F7, typename F8> | |
| typename F7, typename F8> | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7 | | , const F4& f4, | |
| , F8 f8, tbb::task_group_context& context) { | | const F5& f5, const F6& f6, const F7& f7, const F8& f8 | |
| | | , | |
| | | tbb::task_group_context& context) | |
| | | { | |
| internal::parallel_invoke_cleaner cleaner(4, context); | | internal::parallel_invoke_cleaner cleaner(4, context); | |
| internal::parallel_invoke_helper& root = cleaner.root; | | internal::parallel_invoke_helper& root = cleaner.root; | |
| | | | |
| root.add_children(f8, f7, f6); | | root.add_children(f8, f7, f6); | |
| root.add_children(f5, f4, f3); | | root.add_children(f5, f4, f3); | |
| root.add_children(f2, f1); | | root.add_children(f2, f1); | |
| | | | |
| root.run_and_finish(f0); | | root.run_and_finish(f0); | |
| } | | } | |
| | | | |
| // ten arguments | | // ten arguments | |
|
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t | | template<typename F0, typename F1, typename F2, typename F3, typename F4, | |
| ypename F5, typename F6, | | typename F5, typename F6, typename F7, typename F8, typename F9> | |
| typename F7, typename F8, typename F9> | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7 | | , const F4& f4, | |
| , F8 f8, F9 f9, tbb::task_group_context& context) { | | const F5& f5, const F6& f6, const F7& f7, const F8& f8 | |
| | | , const F9& f9, | |
| | | tbb::task_group_context& context) | |
| | | { | |
| internal::parallel_invoke_cleaner cleaner(4, context); | | internal::parallel_invoke_cleaner cleaner(4, context); | |
| internal::parallel_invoke_helper& root = cleaner.root; | | internal::parallel_invoke_helper& root = cleaner.root; | |
| | | | |
| root.add_children(f9, f8, f7); | | root.add_children(f9, f8, f7); | |
| root.add_children(f6, f5, f4); | | root.add_children(f6, f5, f4); | |
| root.add_children(f3, f2, f1); | | root.add_children(f3, f2, f1); | |
| | | | |
| root.run_and_finish(f0); | | root.run_and_finish(f0); | |
| } | | } | |
| | | | |
| // two arguments | | // two arguments | |
| template<typename F0, typename F1> | | template<typename F0, typename F1> | |
|
| void parallel_invoke(F0 f0, F1 f1) { | | void parallel_invoke(const F0& f0, const F1& f1) { | |
| task_group_context context; | | task_group_context context; | |
| parallel_invoke<F0, F1>(f0, f1, context); | | parallel_invoke<F0, F1>(f0, f1, context); | |
| } | | } | |
| // three arguments | | // three arguments | |
| template<typename F0, typename F1, typename F2> | | template<typename F0, typename F1, typename F2> | |
|
| void parallel_invoke(F0 f0, F1 f1, F2 f2) { | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2) { | |
| task_group_context context; | | task_group_context context; | |
| parallel_invoke<F0, F1, F2>(f0, f1, f2, context); | | parallel_invoke<F0, F1, F2>(f0, f1, f2, context); | |
| } | | } | |
| // four arguments | | // four arguments | |
| template<typename F0, typename F1, typename F2, typename F3 > | | template<typename F0, typename F1, typename F2, typename F3 > | |
|
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3) { | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3
) { | |
| task_group_context context; | | task_group_context context; | |
| parallel_invoke<F0, F1, F2, F3>(f0, f1, f2, f3, context); | | parallel_invoke<F0, F1, F2, F3>(f0, f1, f2, f3, context); | |
| } | | } | |
| // five arguments | | // five arguments | |
| template<typename F0, typename F1, typename F2, typename F3, typename F4> | | template<typename F0, typename F1, typename F2, typename F3, typename F4> | |
|
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4) { | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3
, const F4& f4) { | |
| task_group_context context; | | task_group_context context; | |
| parallel_invoke<F0, F1, F2, F3, F4>(f0, f1, f2, f3, f4, context); | | parallel_invoke<F0, F1, F2, F3, F4>(f0, f1, f2, f3, f4, context); | |
| } | | } | |
| // six arguments | | // six arguments | |
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t
ypename F5> | | template<typename F0, typename F1, typename F2, typename F3, typename F4, t
ypename F5> | |
|
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5) { | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3
, const F4& f4, const F5& f5) { | |
| task_group_context context; | | task_group_context context; | |
| parallel_invoke<F0, F1, F2, F3, F4, F5>(f0, f1, f2, f3, f4, f5, context
); | | parallel_invoke<F0, F1, F2, F3, F4, F5>(f0, f1, f2, f3, f4, f5, context
); | |
| } | | } | |
| // seven arguments | | // seven arguments | |
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t
ypename F5, typename F6> | | template<typename F0, typename F1, typename F2, typename F3, typename F4, t
ypename F5, typename F6> | |
|
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6) { | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| | | , const F4& f4, | |
| | | const F5& f5, const F6& f6) | |
| | | { | |
| task_group_context context; | | task_group_context context; | |
| parallel_invoke<F0, F1, F2, F3, F4, F5, F6>(f0, f1, f2, f3, f4, f5, f6,
context); | | parallel_invoke<F0, F1, F2, F3, F4, F5, F6>(f0, f1, f2, f3, f4, f5, f6,
context); | |
| } | | } | |
| // eigth arguments | | // eigth arguments | |
|
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t | | template<typename F0, typename F1, typename F2, typename F3, typename F4, | |
| ypename F5, typename F6, | | typename F5, typename F6, typename F7> | |
| typename F7> | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7 | | , const F4& f4, | |
| ) { | | const F5& f5, const F6& f6, const F7& f7) | |
| | | { | |
| task_group_context context; | | task_group_context context; | |
| parallel_invoke<F0, F1, F2, F3, F4, F5, F6, F7>(f0, f1, f2, f3, f4, f5,
f6, f7, context); | | parallel_invoke<F0, F1, F2, F3, F4, F5, F6, F7>(f0, f1, f2, f3, f4, f5,
f6, f7, context); | |
| } | | } | |
| // nine arguments | | // nine arguments | |
|
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t | | template<typename F0, typename F1, typename F2, typename F3, typename F4, | |
| ypename F5, typename F6, | | typename F5, typename F6, typename F7, typename F8> | |
| typename F7, typename F8> | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7 | | , const F4& f4, | |
| , F8 f8) { | | const F5& f5, const F6& f6, const F7& f7, const F8& f8 | |
| | | ) | |
| | | { | |
| task_group_context context; | | task_group_context context; | |
| parallel_invoke<F0, F1, F2, F3, F4, F5, F6, F7, F8>(f0, f1, f2, f3, f4,
f5, f6, f7, f8, context); | | parallel_invoke<F0, F1, F2, F3, F4, F5, F6, F7, F8>(f0, f1, f2, f3, f4,
f5, f6, f7, f8, context); | |
| } | | } | |
| // ten arguments | | // ten arguments | |
|
| template<typename F0, typename F1, typename F2, typename F3, typename F4, t | | template<typename F0, typename F1, typename F2, typename F3, typename F4, | |
| ypename F5, typename F6, | | typename F5, typename F6, typename F7, typename F8, typename F9> | |
| typename F7, typename F8, typename F9> | | void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3 | |
| void parallel_invoke(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7 | | , const F4& f4, | |
| , F8 f8, F9 f9) { | | const F5& f5, const F6& f6, const F7& f7, const F8& f8 | |
| | | , const F9& f9) | |
| | | { | |
| task_group_context context; | | task_group_context context; | |
| parallel_invoke<F0, F1, F2, F3, F4, F5, F6, F7, F8, F9>(f0, f1, f2, f3,
f4, f5, f6, f7, f8, f9, context); | | parallel_invoke<F0, F1, F2, F3, F4, F5, F6, F7, F8, F9>(f0, f1, f2, f3,
f4, f5, f6, f7, f8, f9, context); | |
| } | | } | |
| | | | |
| //@} | | //@} | |
| | | | |
| } // namespace | | } // namespace | |
| | | | |
| #endif /* __TBB_parallel_invoke_H */ | | #endif /* __TBB_parallel_invoke_H */ | |
| | | | |
End of changes. 27 change blocks. |
| 61 lines changed or deleted | | 86 lines changed or added | |
|
| parallel_reduce.h | | parallel_reduce.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 68 | | skipping to change at line 68 | |
| return static_cast<T*>(itt_load_pointer_with_acquire_v3(&src)); | | return static_cast<T*>(itt_load_pointer_with_acquire_v3(&src)); | |
| #else | | #else | |
| return __TBB_load_with_acquire(src); | | return __TBB_load_with_acquire(src); | |
| #endif /* TBB_USE_THREADING_TOOLS */ | | #endif /* TBB_USE_THREADING_TOOLS */ | |
| } | | } | |
| | | | |
| //! 0 if root, 1 if a left child, 2 if a right child. | | //! 0 if root, 1 if a left child, 2 if a right child. | |
| /** Represented as a char, not enum, for compactness. */ | | /** Represented as a char, not enum, for compactness. */ | |
| typedef char reduction_context; | | typedef char reduction_context; | |
| | | | |
|
| //! Task type use to combine the partial results of parallel_reduce wit
h affinity_partitioner. | | //! Task type use to combine the partial results of parallel_reduce. | |
| /** @ingroup algorithms */ | | /** @ingroup algorithms */ | |
| template<typename Body> | | template<typename Body> | |
| class finish_reduce: public task { | | class finish_reduce: public task { | |
| //! Pointer to body, or NULL if the left child has not yet finished
. | | //! Pointer to body, or NULL if the left child has not yet finished
. | |
| Body* my_body; | | Body* my_body; | |
| bool has_right_zombie; | | bool has_right_zombie; | |
| const reduction_context my_context; | | const reduction_context my_context; | |
| aligned_space<Body,1> zombie_space; | | aligned_space<Body,1> zombie_space; | |
|
| finish_reduce( char context ) : | | finish_reduce( char context_ ) : | |
| my_body(NULL), | | my_body(NULL), | |
| has_right_zombie(false), | | has_right_zombie(false), | |
|
| my_context(context) | | my_context(context_) | |
| { | | { | |
| } | | } | |
| task* execute() { | | task* execute() { | |
| if( has_right_zombie ) { | | if( has_right_zombie ) { | |
| // Right child was stolen. | | // Right child was stolen. | |
| Body* s = zombie_space.begin(); | | Body* s = zombie_space.begin(); | |
| my_body->join( *s ); | | my_body->join( *s ); | |
| s->~Body(); | | s->~Body(); | |
| } | | } | |
| if( my_context==1 ) | | if( my_context==1 ) | |
| parallel_reduce_store_body( static_cast<finish_reduce*>(par
ent())->my_body, my_body ); | | parallel_reduce_store_body( static_cast<finish_reduce*>(par
ent())->my_body, my_body ); | |
| return NULL; | | return NULL; | |
| } | | } | |
| template<typename Range,typename Body_, typename Partitioner> | | template<typename Range,typename Body_, typename Partitioner> | |
| friend class start_reduce; | | friend class start_reduce; | |
| }; | | }; | |
| | | | |
|
| //! Task type used to split the work of parallel_reduce with affinity_p
artitioner. | | //! Task type used to split the work of parallel_reduce. | |
| /** @ingroup algorithms */ | | /** @ingroup algorithms */ | |
| template<typename Range, typename Body, typename Partitioner> | | template<typename Range, typename Body, typename Partitioner> | |
| class start_reduce: public task { | | class start_reduce: public task { | |
| typedef finish_reduce<Body> finish_type; | | typedef finish_reduce<Body> finish_type; | |
| Body* my_body; | | Body* my_body; | |
| Range my_range; | | Range my_range; | |
| typename Partitioner::partition_type my_partition; | | typename Partitioner::partition_type my_partition; | |
| reduction_context my_context; | | reduction_context my_context; | |
| /*override*/ task* execute(); | | /*override*/ task* execute(); | |
| template<typename Body_> | | template<typename Body_> | |
| | | | |
| skipping to change at line 121 | | skipping to change at line 121 | |
| //! Constructor used for root task | | //! Constructor used for root task | |
| start_reduce( const Range& range, Body* body, Partitioner& partitio
ner ) : | | start_reduce( const Range& range, Body* body, Partitioner& partitio
ner ) : | |
| my_body(body), | | my_body(body), | |
| my_range(range), | | my_range(range), | |
| my_partition(partitioner), | | my_partition(partitioner), | |
| my_context(0) | | my_context(0) | |
| { | | { | |
| } | | } | |
| //! Splitting constructor used to generate children. | | //! Splitting constructor used to generate children. | |
| /** this becomes left child. Newly constructed object is right chi
ld. */ | | /** this becomes left child. Newly constructed object is right chi
ld. */ | |
|
| start_reduce( start_reduce& parent, split ) : | | start_reduce( start_reduce& parent_, split ) : | |
| my_body(parent.my_body), | | my_body(parent_.my_body), | |
| my_range(parent.my_range,split()), | | my_range(parent_.my_range,split()), | |
| my_partition(parent.my_partition,split()), | | my_partition(parent_.my_partition,split()), | |
| my_context(2) | | my_context(2) | |
| { | | { | |
| my_partition.set_affinity(*this); | | my_partition.set_affinity(*this); | |
|
| parent.my_context = 1; | | parent_.my_context = 1; | |
| } | | } | |
| //! Update affinity info, if any | | //! Update affinity info, if any | |
| /*override*/ void note_affinity( affinity_id id ) { | | /*override*/ void note_affinity( affinity_id id ) { | |
| my_partition.note_affinity( id ); | | my_partition.note_affinity( id ); | |
| } | | } | |
| | | | |
| public: | | public: | |
| static void run( const Range& range, Body& body, Partitioner& parti
tioner ) { | | static void run( const Range& range, Body& body, Partitioner& parti
tioner ) { | |
| if( !range.empty() ) { | | if( !range.empty() ) { | |
|
| #if !__TBB_EXCEPTIONS || TBB_JOIN_OUTER_TASK_GROUP | | #if !__TBB_TASK_GROUP_CONTEXT || TBB_JOIN_OUTER_TASK_GROUP | |
| task::spawn_root_and_wait( *new(task::allocate_root()) star
t_reduce(range,&body,partitioner) ); | | task::spawn_root_and_wait( *new(task::allocate_root()) star
t_reduce(range,&body,partitioner) ); | |
| #else | | #else | |
| // Bound context prevents exceptions from body to affect ne
sting or sibling algorithms, | | // Bound context prevents exceptions from body to affect ne
sting or sibling algorithms, | |
| // and allows users to handle exceptions safely by wrapping
parallel_for in the try-block. | | // and allows users to handle exceptions safely by wrapping
parallel_for in the try-block. | |
| task_group_context context; | | task_group_context context; | |
| task::spawn_root_and_wait( *new(task::allocate_root(context
)) start_reduce(range,&body,partitioner) ); | | task::spawn_root_and_wait( *new(task::allocate_root(context
)) start_reduce(range,&body,partitioner) ); | |
|
| #endif /* __TBB_EXCEPTIONS && !TBB_JOIN_OUTER_TASK_GROUP */ | | #endif /* __TBB_TASK_GROUP_CONTEXT && !TBB_JOIN_OUTER_TASK_GROUP */ | |
| } | | } | |
| } | | } | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| static void run( const Range& range, Body& body, Partitioner& parti
tioner, task_group_context& context ) { | | static void run( const Range& range, Body& body, Partitioner& parti
tioner, task_group_context& context ) { | |
| if( !range.empty() ) | | if( !range.empty() ) | |
| task::spawn_root_and_wait( *new(task::allocate_root(context
)) start_reduce(range,&body,partitioner) ); | | task::spawn_root_and_wait( *new(task::allocate_root(context
)) start_reduce(range,&body,partitioner) ); | |
| } | | } | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| }; | | }; | |
| | | | |
| template<typename Range, typename Body, typename Partitioner> | | template<typename Range, typename Body, typename Partitioner> | |
| task* start_reduce<Range,Body,Partitioner>::execute() { | | task* start_reduce<Range,Body,Partitioner>::execute() { | |
| if( my_context==2 ) { | | if( my_context==2 ) { | |
| finish_type* p = static_cast<finish_type*>(parent() ); | | finish_type* p = static_cast<finish_type*>(parent() ); | |
| if( !parallel_reduce_load_body(p->my_body) ) { | | if( !parallel_reduce_load_body(p->my_body) ) { | |
| my_body = new( p->zombie_space.begin() ) Body(*my_body,spli
t()); | | my_body = new( p->zombie_space.begin() ) Body(*my_body,spli
t()); | |
| p->has_right_zombie = true; | | p->has_right_zombie = true; | |
| } | | } | |
| } | | } | |
| if( !my_range.is_divisible() || my_partition.should_execute_range(*
this) ) { | | if( !my_range.is_divisible() || my_partition.should_execute_range(*
this) ) { | |
| (*my_body)( my_range ); | | (*my_body)( my_range ); | |
| if( my_context==1 ) | | if( my_context==1 ) | |
| parallel_reduce_store_body(static_cast<finish_type*>(parent
())->my_body, my_body ); | | parallel_reduce_store_body(static_cast<finish_type*>(parent
())->my_body, my_body ); | |
|
| return my_partition.continue_after_execute_range(*this); | | return my_partition.continue_after_execute_range(); | |
| } else { | | } else { | |
| finish_type& c = *new( allocate_continuation()) finish_type(my_
context); | | finish_type& c = *new( allocate_continuation()) finish_type(my_
context); | |
| recycle_as_child_of(c); | | recycle_as_child_of(c); | |
| c.set_ref_count(2); | | c.set_ref_count(2); | |
| bool delay = my_partition.decide_whether_to_delay(); | | bool delay = my_partition.decide_whether_to_delay(); | |
| start_reduce& b = *new( c.allocate_child() ) start_reduce(*this
,split()); | | start_reduce& b = *new( c.allocate_child() ) start_reduce(*this
,split()); | |
|
| my_partition.spawn_or_delay(delay,*this,b); | | my_partition.spawn_or_delay(delay,b); | |
| return this; | | return this; | |
| } | | } | |
| } | | } | |
| | | | |
| //! Auxiliary class for parallel_reduce; for internal use only. | | //! Auxiliary class for parallel_reduce; for internal use only. | |
| /** The adaptor class that implements \ref parallel_reduce_body_req "pa
rallel_reduce Body" | | /** The adaptor class that implements \ref parallel_reduce_body_req "pa
rallel_reduce Body" | |
| using given \ref parallel_reduce_lambda_req "anonymous function obj
ects". | | using given \ref parallel_reduce_lambda_req "anonymous function obj
ects". | |
| **/ | | **/ | |
| /** @ingroup algorithms */ | | /** @ingroup algorithms */ | |
| template<typename Range, typename Value, typename RealBody, typename Re
duction> | | template<typename Range, typename Value, typename RealBody, typename Re
duction> | |
| | | | |
| skipping to change at line 279 | | skipping to change at line 279 | |
| internal::start_reduce<Range,Body,const auto_partitioner>::run( range,
body, partitioner ); | | internal::start_reduce<Range,Body,const auto_partitioner>::run( range,
body, partitioner ); | |
| } | | } | |
| | | | |
| //! Parallel iteration with reduction and affinity_partitioner | | //! Parallel iteration with reduction and affinity_partitioner | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, affinity_partitioner&
partitioner ) { | | void parallel_reduce( const Range& range, Body& body, affinity_partitioner&
partitioner ) { | |
| internal::start_reduce<Range,Body,affinity_partitioner>::run( range, bo
dy, partitioner ); | | internal::start_reduce<Range,Body,affinity_partitioner>::run( range, bo
dy, partitioner ); | |
| } | | } | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| //! Parallel iteration with reduction, simple partitioner and user-supplied
context. | | //! Parallel iteration with reduction, simple partitioner and user-supplied
context. | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, const simple_partitio
ner& partitioner, task_group_context& context ) { | | void parallel_reduce( const Range& range, Body& body, const simple_partitio
ner& partitioner, task_group_context& context ) { | |
| internal::start_reduce<Range,Body,const simple_partitioner>::run( range
, body, partitioner, context ); | | internal::start_reduce<Range,Body,const simple_partitioner>::run( range
, body, partitioner, context ); | |
| } | | } | |
| | | | |
| //! Parallel iteration with reduction, auto_partitioner and user-supplied c
ontext | | //! Parallel iteration with reduction, auto_partitioner and user-supplied c
ontext | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, const auto_partitione
r& partitioner, task_group_context& context ) { | | void parallel_reduce( const Range& range, Body& body, const auto_partitione
r& partitioner, task_group_context& context ) { | |
| internal::start_reduce<Range,Body,const auto_partitioner>::run( range,
body, partitioner, context ); | | internal::start_reduce<Range,Body,const auto_partitioner>::run( range,
body, partitioner, context ); | |
| } | | } | |
| | | | |
| //! Parallel iteration with reduction, affinity_partitioner and user-suppli
ed context | | //! Parallel iteration with reduction, affinity_partitioner and user-suppli
ed context | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, affinity_partitioner&
partitioner, task_group_context& context ) { | | void parallel_reduce( const Range& range, Body& body, affinity_partitioner&
partitioner, task_group_context& context ) { | |
| internal::start_reduce<Range,Body,affinity_partitioner>::run( range, bo
dy, partitioner, context ); | | internal::start_reduce<Range,Body,affinity_partitioner>::run( range, bo
dy, partitioner, context ); | |
| } | | } | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
| /** parallel_reduce overloads that work with anonymous function objects | | /** parallel_reduce overloads that work with anonymous function objects | |
| (see also \ref parallel_reduce_lambda_req "requirements on parallel_red
uce anonymous function objects"). **/ | | (see also \ref parallel_reduce_lambda_req "requirements on parallel_red
uce anonymous function objects"). **/ | |
| | | | |
| //! Parallel iteration with reduction and default partitioner. | | //! Parallel iteration with reduction and default partitioner. | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduct
ion> | | template<typename Range, typename Value, typename RealBody, typename Reduct
ion> | |
| Value parallel_reduce( const Range& range, const Value& identity, const Rea
lBody& real_body, const Reduction& reduction ) { | | Value parallel_reduce( const Range& range, const Value& identity, const Rea
lBody& real_body, const Reduction& reduction ) { | |
| internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(ident
ity, real_body, reduction); | | internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(ident
ity, real_body, reduction); | |
| internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,R
ealBody,Reduction>,const __TBB_DEFAULT_PARTITIONER> | | internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,R
ealBody,Reduction>,const __TBB_DEFAULT_PARTITIONER> | |
| | | | |
| skipping to change at line 348 | | skipping to change at line 348 | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduct
ion> | | template<typename Range, typename Value, typename RealBody, typename Reduct
ion> | |
| Value parallel_reduce( const Range& range, const Value& identity, const Rea
lBody& real_body, const Reduction& reduction, | | Value parallel_reduce( const Range& range, const Value& identity, const Rea
lBody& real_body, const Reduction& reduction, | |
| affinity_partitioner& partitioner ) { | | affinity_partitioner& partitioner ) { | |
| internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(ident
ity, real_body, reduction); | | internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(ident
ity, real_body, reduction); | |
| internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,R
ealBody,Reduction>,affinity_partitioner> | | internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,R
ealBody,Reduction>,affinity_partitioner> | |
| ::run( range, body, partitioner ); | | ::run( range, body, partitioner ); | |
| return body.result(); | | return body.result(); | |
| } | | } | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| //! Parallel iteration with reduction, simple partitioner and user-supplied
context. | | //! Parallel iteration with reduction, simple partitioner and user-supplied
context. | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduct
ion> | | template<typename Range, typename Value, typename RealBody, typename Reduct
ion> | |
| Value parallel_reduce( const Range& range, const Value& identity, const Rea
lBody& real_body, const Reduction& reduction, | | Value parallel_reduce( const Range& range, const Value& identity, const Rea
lBody& real_body, const Reduction& reduction, | |
| const simple_partitioner& partitioner, task_group_co
ntext& context ) { | | const simple_partitioner& partitioner, task_group_co
ntext& context ) { | |
| internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(ident
ity, real_body, reduction); | | internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(ident
ity, real_body, reduction); | |
| internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,R
ealBody,Reduction>,const simple_partitioner> | | internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,R
ealBody,Reduction>,const simple_partitioner> | |
| ::run( range, body, partitioner, context ); | | ::run( range, body, partitioner, context ); | |
| return body.result(); | | return body.result(); | |
| } | | } | |
| | | | |
| skipping to change at line 381 | | skipping to change at line 381 | |
| //! Parallel iteration with reduction, affinity_partitioner and user-suppli
ed context | | //! Parallel iteration with reduction, affinity_partitioner and user-suppli
ed context | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduct
ion> | | template<typename Range, typename Value, typename RealBody, typename Reduct
ion> | |
| Value parallel_reduce( const Range& range, const Value& identity, const Rea
lBody& real_body, const Reduction& reduction, | | Value parallel_reduce( const Range& range, const Value& identity, const Rea
lBody& real_body, const Reduction& reduction, | |
| affinity_partitioner& partitioner, task_group_contex
t& context ) { | | affinity_partitioner& partitioner, task_group_contex
t& context ) { | |
| internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(ident
ity, real_body, reduction); | | internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(ident
ity, real_body, reduction); | |
| internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,R
ealBody,Reduction>,affinity_partitioner> | | internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,R
ealBody,Reduction>,affinity_partitioner> | |
| ::run( range, body, partitioner, co
ntext ); | | ::run( range, body, partitioner, co
ntext ); | |
| return body.result(); | | return body.result(); | |
| } | | } | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| //@} | | //@} | |
| | | | |
| } // namespace tbb | | } // namespace tbb | |
| | | | |
| #endif /* __TBB_parallel_reduce_H */ | | #endif /* __TBB_parallel_reduce_H */ | |
| | | | |
End of changes. 17 change blocks. |
| 20 lines changed or deleted | | 20 lines changed or added | |
|
| parallel_scan.h | | parallel_scan.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 112 | | skipping to change at line 112 | |
| left_sum(NULL), | | left_sum(NULL), | |
| left(NULL), | | left(NULL), | |
| right(NULL), | | right(NULL), | |
| left_is_final(left_is_final_), | | left_is_final(left_is_final_), | |
| range(range_) | | range(range_) | |
| { | | { | |
| // Poison fields that will be set by second pass. | | // Poison fields that will be set by second pass. | |
| poison_pointer(body); | | poison_pointer(body); | |
| poison_pointer(incoming); | | poison_pointer(incoming); | |
| } | | } | |
|
| task* create_child( const Range& range, final_sum_type& f, sum_node
* n, final_sum_type* incoming, Body* stuff_last ) { | | task* create_child( const Range& range_, final_sum_type& f, sum_nod
e* n, final_sum_type* incoming_, Body* stuff_last_ ) { | |
| if( !n ) { | | if( !n ) { | |
| f.recycle_as_child_of( *this ); | | f.recycle_as_child_of( *this ); | |
|
| f.finish_construction( range, stuff_last ); | | f.finish_construction( range_, stuff_last_ ); | |
| return &f; | | return &f; | |
| } else { | | } else { | |
| n->body = &f; | | n->body = &f; | |
|
| n->incoming = incoming; | | n->incoming = incoming_; | |
| n->stuff_last = stuff_last; | | n->stuff_last = stuff_last_; | |
| return n; | | return n; | |
| } | | } | |
| } | | } | |
| /*override*/ task* execute() { | | /*override*/ task* execute() { | |
| if( body ) { | | if( body ) { | |
| if( incoming ) | | if( incoming ) | |
| left_sum->body.reverse_join( incoming->body ); | | left_sum->body.reverse_join( incoming->body ); | |
| recycle_as_continuation(); | | recycle_as_continuation(); | |
| sum_node& c = *this; | | sum_node& c = *this; | |
| task* b = c.create_child(Range(range,split()),*left_sum,rig
ht,left_sum,stuff_last); | | task* b = c.create_child(Range(range,split()),*left_sum,rig
ht,left_sum,stuff_last); | |
| | | | |
| skipping to change at line 188 | | skipping to change at line 188 | |
| | | | |
| finish_scan( sum_node_type*& return_slot_, final_sum_type** sum_, s
um_node_type& result_ ) : | | finish_scan( sum_node_type*& return_slot_, final_sum_type** sum_, s
um_node_type& result_ ) : | |
| sum(sum_), | | sum(sum_), | |
| return_slot(return_slot_), | | return_slot(return_slot_), | |
| right_zombie(NULL), | | right_zombie(NULL), | |
| result(result_) | | result(result_) | |
| { | | { | |
| __TBB_ASSERT( !return_slot, NULL ); | | __TBB_ASSERT( !return_slot, NULL ); | |
| } | | } | |
| ~finish_scan(){ | | ~finish_scan(){ | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| if (is_cancelled()) { | | if (is_cancelled()) { | |
| if (result.ref_count() == 0) destroy(result); | | if (result.ref_count() == 0) destroy(result); | |
| if (right_zombie) destroy(*right_zombie); | | if (right_zombie) destroy(*right_zombie); | |
| } | | } | |
| #endif | | #endif | |
| } | | } | |
| }; | | }; | |
| | | | |
| //! Initial task to split the work | | //! Initial task to split the work | |
| /** @ingroup algorithms */ | | /** @ingroup algorithms */ | |
| | | | |
| skipping to change at line 214 | | skipping to change at line 214 | |
| /** Non-null if caller is requesting total. */ | | /** Non-null if caller is requesting total. */ | |
| final_sum_type** sum; | | final_sum_type** sum; | |
| sum_node_type** return_slot; | | sum_node_type** return_slot; | |
| /** Null if computing root. */ | | /** Null if computing root. */ | |
| sum_node_type* parent_sum; | | sum_node_type* parent_sum; | |
| bool is_final; | | bool is_final; | |
| bool is_right_child; | | bool is_right_child; | |
| Range range; | | Range range; | |
| typename Partitioner::partition_type partition; | | typename Partitioner::partition_type partition; | |
| /*override*/ task* execute(); | | /*override*/ task* execute(); | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| tbb::task_group_context &my_context; | | tbb::task_group_context &my_context; | |
| #endif | | #endif | |
|
| | | | |
| | | // The class is intended to destroy allocated tasks if exception oc | |
| | | curs | |
| | | class task_cleaner: internal::no_copy { | |
| | | typedef internal::start_scan<Range,Body,Partitioner> start_pass | |
| | | 1_type; | |
| | | | |
| | | internal::sum_node<Range,Body>* my_root; | |
| | | final_sum_type* my_temp_body; | |
| | | const Range& my_range; | |
| | | Body& my_body; | |
| | | start_pass1_type* my_pass1; | |
| | | public: | |
| | | bool do_clean; // Set to true if cleanup is required. | |
| | | task_cleaner(internal::sum_node<Range,Body>* root, final_sum_ty | |
| | | pe* temp_body, const Range& range, Body& body, start_pass1_type* pass1) | |
| | | : my_root(root), my_temp_body(temp_body), my_range(range), | |
| | | my_body(body), my_pass1(pass1), do_clean(true) {} | |
| | | ~task_cleaner(){ | |
| | | if (do_clean) { | |
| | | my_body.assign(my_temp_body->body); | |
| | | my_temp_body->finish_construction( my_range, NULL ); | |
| | | my_temp_body->destroy(*my_temp_body); | |
| | | } | |
| | | } | |
| | | }; | |
| | | | |
| public: | | public: | |
|
| start_scan( sum_node_type*& return_slot_, start_scan& parent, sum_n | | start_scan( sum_node_type*& return_slot_, start_scan& parent_, sum_ | |
| ode_type* parent_sum_ | | node_type* parent_sum_ | |
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| , tbb::task_group_context &_context | | , tbb::task_group_context &context_ | |
| #endif | | #endif | |
| ) : | | ) : | |
|
| body(parent.body), | | body(parent_.body), | |
| sum(parent.sum), | | sum(parent_.sum), | |
| return_slot(&return_slot_), | | return_slot(&return_slot_), | |
| parent_sum(parent_sum_), | | parent_sum(parent_sum_), | |
|
| is_final(parent.is_final), | | is_final(parent_.is_final), | |
| is_right_child(false), | | is_right_child(false), | |
|
| range(parent.range,split()), | | range(parent_.range,split()), | |
| partition(parent.partition,split()) | | partition(parent_.partition,split()) | |
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| , my_context (_context) | | , my_context(context_) | |
| #endif | | #endif | |
| { | | { | |
| __TBB_ASSERT( !*return_slot, NULL ); | | __TBB_ASSERT( !*return_slot, NULL ); | |
| } | | } | |
| | | | |
| start_scan( sum_node_type*& return_slot_, const Range& range_, fina
l_sum_type& body_, const Partitioner& partitioner_ | | start_scan( sum_node_type*& return_slot_, const Range& range_, fina
l_sum_type& body_, const Partitioner& partitioner_ | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| , tbb::task_group_context &_context | | , tbb::task_group_context &_context | |
| #endif | | #endif | |
| ) : | | ) : | |
| body(&body_), | | body(&body_), | |
| sum(NULL), | | sum(NULL), | |
| return_slot(&return_slot_), | | return_slot(&return_slot_), | |
| parent_sum(NULL), | | parent_sum(NULL), | |
| is_final(true), | | is_final(true), | |
| is_right_child(false), | | is_right_child(false), | |
| range(range_), | | range(range_), | |
| partition(partitioner_) | | partition(partitioner_) | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| , my_context (_context) | | , my_context (_context) | |
| #endif | | #endif | |
| { | | { | |
| __TBB_ASSERT( !*return_slot, NULL ); | | __TBB_ASSERT( !*return_slot, NULL ); | |
| } | | } | |
| | | | |
| static void run( const Range& range, Body& body, const Partitioner
& partitioner | | static void run( const Range& range, Body& body, const Partitioner
& partitioner | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| , task_group_context& context | | , task_group_context& context | |
| #endif | | #endif | |
| ) { | | ) { | |
| if( !range.empty() ) { | | if( !range.empty() ) { | |
| typedef internal::start_scan<Range,Body,Partitioner> start_
pass1_type; | | typedef internal::start_scan<Range,Body,Partitioner> start_
pass1_type; | |
| internal::sum_node<Range,Body>* root = NULL; | | internal::sum_node<Range,Body>* root = NULL; | |
| typedef internal::final_sum<Range,Body> final_sum_type; | | typedef internal::final_sum<Range,Body> final_sum_type; | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| final_sum_type* temp_body = new(task::allocate_root(context
)) final_sum_type( body ); | | final_sum_type* temp_body = new(task::allocate_root(context
)) final_sum_type( body ); | |
| start_pass1_type& pass1 = *new(task::allocate_root(context)
) start_pass1_type( | | start_pass1_type& pass1 = *new(task::allocate_root(context)
) start_pass1_type( | |
| /*return_slot=*/root, | | /*return_slot=*/root, | |
| range, | | range, | |
| *temp_body, | | *temp_body, | |
| partitioner, | | partitioner, | |
| context | | context | |
| ); | | ); | |
| #else | | #else | |
| final_sum_type* temp_body = new(task::allocate_root()) fina
l_sum_type( body ); | | final_sum_type* temp_body = new(task::allocate_root()) fina
l_sum_type( body ); | |
| start_pass1_type& pass1 = *new(task::allocate_root()) start
_pass1_type( | | start_pass1_type& pass1 = *new(task::allocate_root()) start
_pass1_type( | |
| /*return_slot=*/root, | | /*return_slot=*/root, | |
| range, | | range, | |
| *temp_body, | | *temp_body, | |
| partitioner ); | | partitioner ); | |
| #endif | | #endif | |
|
| // The class is intended to destroy allocated tasks if exce | | | |
| ption occurs | | | |
| class task_cleaner: internal::no_copy { | | | |
| internal::sum_node<Range,Body>* my_root; | | | |
| final_sum_type* my_temp_body; | | | |
| const Range& my_range; | | | |
| Body& my_body; | | | |
| start_pass1_type* my_pass1; | | | |
| public: | | | |
| bool do_clean; // Set to true if cleanup is required. | | | |
| task_cleaner(internal::sum_node<Range,Body>* _root, fin | | | |
| al_sum_type* _temp_body, const Range& _range, Body& _body, start_pass1_type | | | |
| * _pass1) | | | |
| : my_root(_root), my_temp_body(_temp_body), my_rang | | | |
| e(_range), my_body(_body), my_pass1(_pass1), do_clean(true) {} | | | |
| ~task_cleaner(){ | | | |
| if (do_clean) { | | | |
| my_body.assign(my_temp_body->body); | | | |
| my_temp_body->finish_construction( my_range, NU | | | |
| LL ); | | | |
| my_temp_body->destroy(*my_temp_body); | | | |
| } | | | |
| } | | | |
| }; | | | |
| task_cleaner my_cleaner(root, temp_body, range, body, &pass
1); | | task_cleaner my_cleaner(root, temp_body, range, body, &pass
1); | |
| | | | |
| task::spawn_root_and_wait( pass1 ); | | task::spawn_root_and_wait( pass1 ); | |
|
| my_cleaner.do_clean = false; | | | |
| if( root ) { | | if( root ) { | |
|
| | | my_cleaner.do_clean = false; | |
| root->body = temp_body; | | root->body = temp_body; | |
| root->incoming = NULL; | | root->incoming = NULL; | |
| root->stuff_last = &body; | | root->stuff_last = &body; | |
| task::spawn_root_and_wait( *root ); | | task::spawn_root_and_wait( *root ); | |
|
| } else { | | | |
| my_cleaner.do_clean = true; | | | |
| } | | } | |
| } | | } | |
| } | | } | |
| }; | | }; | |
| | | | |
| template<typename Range, typename Body, typename Partitioner> | | template<typename Range, typename Body, typename Partitioner> | |
| task* start_scan<Range,Body,Partitioner>::execute() { | | task* start_scan<Range,Body,Partitioner>::execute() { | |
| typedef internal::finish_scan<Range,Body> finish_pass1_type; | | typedef internal::finish_scan<Range,Body> finish_pass1_type; | |
| finish_pass1_type* p = parent_sum ? static_cast<finish_pass1_type*>
( parent() ) : NULL; | | finish_pass1_type* p = parent_sum ? static_cast<finish_pass1_type*>
( parent() ) : NULL; | |
| // Inspecting p->result.left_sum would ordinarily be a race conditi
on. | | // Inspecting p->result.left_sum would ordinarily be a race conditi
on. | |
| // But we inspect it only if we are not a stolen task, in which cas
e we | | // But we inspect it only if we are not a stolen task, in which cas
e we | |
| // know that task assigning to p->result.left_sum has completed. | | // know that task assigning to p->result.left_sum has completed. | |
| bool treat_as_stolen = is_right_child && (is_stolen_task() || body!
=p->result.left_sum); | | bool treat_as_stolen = is_right_child && (is_stolen_task() || body!
=p->result.left_sum); | |
| if( treat_as_stolen ) { | | if( treat_as_stolen ) { | |
| // Invocation is for right child that has been really stolen or
needs to be virtually stolen | | // Invocation is for right child that has been really stolen or
needs to be virtually stolen | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| p->right_zombie = body = new( allocate_root(my_context) ) final
_sum_type(body->body); | | p->right_zombie = body = new( allocate_root(my_context) ) final
_sum_type(body->body); | |
| #else | | #else | |
| p->right_zombie = body = new( allocate_root() ) final_sum_type(
body->body); | | p->right_zombie = body = new( allocate_root() ) final_sum_type(
body->body); | |
| #endif | | #endif | |
| is_final = false; | | is_final = false; | |
| } | | } | |
| task* next_task = NULL; | | task* next_task = NULL; | |
| if( (is_right_child && !treat_as_stolen) || !range.is_divisible() |
| partition.should_execute_range(*this) ) { | | if( (is_right_child && !treat_as_stolen) || !range.is_divisible() |
| partition.should_execute_range(*this) ) { | |
| if( is_final ) | | if( is_final ) | |
| (body->body)( range, final_scan_tag() ); | | (body->body)( range, final_scan_tag() ); | |
| else if( sum ) | | else if( sum ) | |
| (body->body)( range, pre_scan_tag() ); | | (body->body)( range, pre_scan_tag() ); | |
| if( sum ) | | if( sum ) | |
| *sum = body; | | *sum = body; | |
| __TBB_ASSERT( !*return_slot, NULL ); | | __TBB_ASSERT( !*return_slot, NULL ); | |
| } else { | | } else { | |
| sum_node_type* result; | | sum_node_type* result; | |
| if( parent_sum ) | | if( parent_sum ) | |
| result = new(allocate_additional_child_of(*parent_sum)) sum
_node_type(range,/*left_is_final=*/is_final); | | result = new(allocate_additional_child_of(*parent_sum)) sum
_node_type(range,/*left_is_final=*/is_final); | |
| else | | else | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| result = new(task::allocate_root(my_context)) sum_node_type
(range,/*left_is_final=*/is_final); | | result = new(task::allocate_root(my_context)) sum_node_type
(range,/*left_is_final=*/is_final); | |
| #else | | #else | |
| result = new(task::allocate_root()) sum_node_type(range,/*l
eft_is_final=*/is_final); | | result = new(task::allocate_root()) sum_node_type(range,/*l
eft_is_final=*/is_final); | |
| #endif | | #endif | |
| finish_pass1_type& c = *new( allocate_continuation()) finish_pa
ss1_type(*return_slot,sum,*result); | | finish_pass1_type& c = *new( allocate_continuation()) finish_pa
ss1_type(*return_slot,sum,*result); | |
| // Split off right child | | // Split off right child | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| start_scan& b = *new( c.allocate_child() ) start_scan( /*return
_slot=*/result->right, *this, result, my_context ); | | start_scan& b = *new( c.allocate_child() ) start_scan( /*return
_slot=*/result->right, *this, result, my_context ); | |
| #else | | #else | |
| start_scan& b = *new( c.allocate_child() ) start_scan( /*return
_slot=*/result->right, *this, result ); | | start_scan& b = *new( c.allocate_child() ) start_scan( /*return
_slot=*/result->right, *this, result ); | |
| #endif | | #endif | |
| b.is_right_child = true; | | b.is_right_child = true; | |
| // Left child is recycling of *this. Must recycle this before
spawning b, | | // Left child is recycling of *this. Must recycle this before
spawning b, | |
| // otherwise b might complete and decrement c.ref_count() to ze
ro, which | | // otherwise b might complete and decrement c.ref_count() to ze
ro, which | |
| // would cause c.execute() to run prematurely. | | // would cause c.execute() to run prematurely. | |
| recycle_as_child_of(c); | | recycle_as_child_of(c); | |
| c.set_ref_count(2); | | c.set_ref_count(2); | |
| | | | |
| skipping to change at line 405 | | skipping to change at line 407 | |
| **/ | | **/ | |
| | | | |
| /** \name parallel_scan | | /** \name parallel_scan | |
| See also requirements on \ref range_req "Range" and \ref parallel_scan_
body_req "parallel_scan Body". **/ | | See also requirements on \ref range_req "Range" and \ref parallel_scan_
body_req "parallel_scan Body". **/ | |
| //@{ | | //@{ | |
| | | | |
| //! Parallel prefix with default partitioner | | //! Parallel prefix with default partitioner | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_scan( const Range& range, Body& body ) { | | void parallel_scan( const Range& range, Body& body ) { | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| task_group_context context; | | task_group_context context; | |
|
| #endif // __TBB_EXCEPTIONS | | #endif // __TBB_TASK_GROUP_CONTEXT | |
| internal::start_scan<Range,Body,__TBB_DEFAULT_PARTITIONER>::run(range,b
ody,__TBB_DEFAULT_PARTITIONER() | | internal::start_scan<Range,Body,__TBB_DEFAULT_PARTITIONER>::run(range,b
ody,__TBB_DEFAULT_PARTITIONER() | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| , context | | , context | |
| #endif | | #endif | |
| ); | | ); | |
| } | | } | |
| | | | |
| //! Parallel prefix with simple_partitioner | | //! Parallel prefix with simple_partitioner | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_scan( const Range& range, Body& body, const simple_partitione
r& partitioner ) { | | void parallel_scan( const Range& range, Body& body, const simple_partitione
r& partitioner ) { | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| task_group_context context; | | task_group_context context; | |
|
| #endif // __TBB_EXCEPTIONS | | #endif // __TBB_TASK_GROUP_CONTEXT | |
| internal::start_scan<Range,Body,simple_partitioner>::run(range,body,par
titioner | | internal::start_scan<Range,Body,simple_partitioner>::run(range,body,par
titioner | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| , context | | , context | |
| #endif | | #endif | |
| ); | | ); | |
| } | | } | |
| | | | |
| //! Parallel prefix with auto_partitioner | | //! Parallel prefix with auto_partitioner | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_scan( const Range& range, Body& body, const auto_partitioner&
partitioner ) { | | void parallel_scan( const Range& range, Body& body, const auto_partitioner&
partitioner ) { | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| task_group_context context; | | task_group_context context; | |
|
| #endif // __TBB_EXCEPTIONS | | #endif // __TBB_TASK_GROUP_CONTEXT | |
| internal::start_scan<Range,Body,auto_partitioner>::run(range,body,parti
tioner | | internal::start_scan<Range,Body,auto_partitioner>::run(range,body,parti
tioner | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| , context | | , context | |
| #endif | | #endif | |
| ); | | ); | |
| } | | } | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| //! Parallel prefix with simple_partitioner and user-supplied context | | //! Parallel prefix with simple_partitioner and user-supplied context | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| void parallel_scan( const Range& range, Body& body, const simple_partitione
r& partitioner, tbb::task_group_context & context ) { | | void parallel_scan( const Range& range, Body& body, const simple_partitione
r& partitioner, tbb::task_group_context & context ) { | |
| internal::start_scan<Range,Body,simple_partitioner>::run(range,body,par
titioner,context); | | internal::start_scan<Range,Body,simple_partitioner>::run(range,body,par
titioner,context); | |
| } | | } | |
| | | | |
| //! Parallel prefix with auto_partitioner and user-supplied context | | //! Parallel prefix with auto_partitioner and user-supplied context | |
| /** @ingroup algorithms **/ | | /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | | template<typename Range, typename Body> | |
| | | | |
End of changes. 32 change blocks. |
| 62 lines changed or deleted | | 63 lines changed or added | |
|
| task.h | | task.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 35 | | skipping to change at line 35 | |
| invalidate any other reasons why the executable file might be covered b
y | | invalidate any other reasons why the executable file might be covered b
y | |
| the GNU General Public License. | | the GNU General Public License. | |
| */ | | */ | |
| | | | |
| #ifndef __TBB_task_H | | #ifndef __TBB_task_H | |
| #define __TBB_task_H | | #define __TBB_task_H | |
| | | | |
| #include "tbb_stddef.h" | | #include "tbb_stddef.h" | |
| #include "tbb_machine.h" | | #include "tbb_machine.h" | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | typedef struct ___itt_caller *__itt_caller; | |
| #include "cache_aligned_allocator.h" | | | |
| #endif /* __TBB_EXCEPTIONS */ | | | |
| | | | |
| namespace tbb { | | namespace tbb { | |
| | | | |
| class task; | | class task; | |
| class task_list; | | class task_list; | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| class task_group_context; | | class task_group_context; | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
| | | // MSVC does not allow taking the address of a member that was defined | |
| | | // privately in task_base and made public in class task via a using declara | |
| | | tion. | |
| | | #if _MSC_VER || (__GNUC__==3 && __GNUC_MINOR__<3) | |
| | | #define __TBB_TASK_BASE_ACCESS public | |
| | | #else | |
| | | #define __TBB_TASK_BASE_ACCESS private | |
| | | #endif | |
| | | | |
| | | namespace interface5 { | |
| | | namespace internal { | |
| | | //! Base class for methods that became static in TBB 3.0. | |
| | | /** TBB's evolution caused the "this" argument for several methods | |
| | | to become obsolete. | |
| | | However, for backwards binary compatibility, the new methods ne | |
| | | ed distinct names, | |
| | | otherwise the One Definition Rule would be broken. Hence the n | |
| | | ew methods are | |
| | | defined in this private base class, and then exposed in class t | |
| | | ask via | |
| | | using declarations. */ | |
| | | class task_base: tbb::internal::no_copy { | |
| | | __TBB_TASK_BASE_ACCESS: | |
| | | friend class tbb::task; | |
| | | #if !TBB_DEPRECATED_TASK_INTERFACE | |
| | | //! Schedule task for execution when a worker becomes available | |
| | | . | |
| | | static void spawn( task& t ); | |
| | | | |
| | | //! Spawn multiple tasks and clear list. | |
| | | static void spawn( task_list& list ); | |
| | | | |
| | | #endif /* !TBB_DEPRECATED_TASK_INTERFACE */ | |
| | | #if !TBB_DEPRECATED_TASK_INTERFACE || __TBB_BUILD | |
| | | //! Destroy a task. | |
| | | /** Usually, calling this method is unnecessary, because a task | |
| | | is | |
| | | implicitly deleted after its execute() method runs. Howeve | |
| | | r, | |
| | | sometimes a task needs to be explicitly deallocated, such a | |
| | | s | |
| | | when a root task is used as the parent in spawn_and_wait_fo | |
| | | r_all. */ | |
| | | static void __TBB_EXPORTED_FUNC destroy( task& victim ); | |
| | | #endif /* TBB_DEPRECATED_TASK_INTERFACE || __TBB_BUILD */ | |
| | | }; | |
| | | } // internal | |
| | | } // interface5 | |
| | | | |
| //! @cond INTERNAL | | //! @cond INTERNAL | |
| namespace internal { | | namespace internal { | |
| | | | |
| class scheduler: no_copy { | | class scheduler: no_copy { | |
| public: | | public: | |
| //! For internal use only | | //! For internal use only | |
| virtual void spawn( task& first, task*& next ) = 0; | | virtual void spawn( task& first, task*& next ) = 0; | |
| | | | |
| //! For internal use only | | //! For internal use only | |
| virtual void wait_for_all( task& parent, task* child ) = 0; | | virtual void wait_for_all( task& parent, task* child ) = 0; | |
| | | | |
| //! For internal use only | | //! For internal use only | |
| virtual void spawn_root_and_wait( task& first, task*& next ) = 0; | | virtual void spawn_root_and_wait( task& first, task*& next ) = 0; | |
| | | | |
| //! Pure virtual destructor; | | //! Pure virtual destructor; | |
| // Have to have it just to shut up overzealous compilation warning
s | | // Have to have it just to shut up overzealous compilation warning
s | |
| virtual ~scheduler() = 0; | | virtual ~scheduler() = 0; | |
|
| | | #if __TBB_ARENA_PER_MASTER | |
| | | | |
| | | //! For internal use only | |
| | | virtual void enqueue( task& t, void* reserved ) = 0; | |
| | | #endif /* __TBB_ARENA_PER_MASTER */ | |
| }; | | }; | |
| | | | |
| //! A reference count | | //! A reference count | |
| /** Should always be non-negative. A signed type is used so that under
flow can be detected. */ | | /** Should always be non-negative. A signed type is used so that under
flow can be detected. */ | |
|
| typedef intptr reference_count; | | typedef intptr_t reference_count; | |
| | | | |
| //! An id as used for specifying affinity. | | //! An id as used for specifying affinity. | |
| typedef unsigned short affinity_id; | | typedef unsigned short affinity_id; | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| struct context_list_node_t { | | struct context_list_node_t { | |
| context_list_node_t *my_prev, | | context_list_node_t *my_prev, | |
| *my_next; | | *my_next; | |
| }; | | }; | |
| | | | |
| class allocate_root_with_context_proxy: no_assign { | | class allocate_root_with_context_proxy: no_assign { | |
| task_group_context& my_context; | | task_group_context& my_context; | |
| public: | | public: | |
| allocate_root_with_context_proxy ( task_group_context& ctx ) : my_c
ontext(ctx) {} | | allocate_root_with_context_proxy ( task_group_context& ctx ) : my_c
ontext(ctx) {} | |
| task& __TBB_EXPORTED_METHOD allocate( size_t size ) const; | | task& __TBB_EXPORTED_METHOD allocate( size_t size ) const; | |
| void __TBB_EXPORTED_METHOD free( task& ) const; | | void __TBB_EXPORTED_METHOD free( task& ) const; | |
| }; | | }; | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
| class allocate_root_proxy: no_assign { | | class allocate_root_proxy: no_assign { | |
| public: | | public: | |
| static task& __TBB_EXPORTED_FUNC allocate( size_t size ); | | static task& __TBB_EXPORTED_FUNC allocate( size_t size ); | |
| static void __TBB_EXPORTED_FUNC free( task& ); | | static void __TBB_EXPORTED_FUNC free( task& ); | |
| }; | | }; | |
| | | | |
| class allocate_continuation_proxy: no_assign { | | class allocate_continuation_proxy: no_assign { | |
| public: | | public: | |
| task& __TBB_EXPORTED_METHOD allocate( size_t size ) const; | | task& __TBB_EXPORTED_METHOD allocate( size_t size ) const; | |
| | | | |
| skipping to change at line 116 | | skipping to change at line 158 | |
| | | | |
| class allocate_additional_child_of_proxy: no_assign { | | class allocate_additional_child_of_proxy: no_assign { | |
| task& self; | | task& self; | |
| task& parent; | | task& parent; | |
| public: | | public: | |
| allocate_additional_child_of_proxy( task& self_, task& parent_ ) :
self(self_), parent(parent_) {} | | allocate_additional_child_of_proxy( task& self_, task& parent_ ) :
self(self_), parent(parent_) {} | |
| task& __TBB_EXPORTED_METHOD allocate( size_t size ) const; | | task& __TBB_EXPORTED_METHOD allocate( size_t size ) const; | |
| void __TBB_EXPORTED_METHOD free( task& ) const; | | void __TBB_EXPORTED_METHOD free( task& ) const; | |
| }; | | }; | |
| | | | |
|
| class task_group_base; | | | |
| | | | |
| //! Memory prefix to a task object. | | //! Memory prefix to a task object. | |
| /** This class is internal to the library. | | /** This class is internal to the library. | |
| Do not reference it directly, except within the library itself. | | Do not reference it directly, except within the library itself. | |
| Fields are ordered in way that preserves backwards compatibility an
d yields | | Fields are ordered in way that preserves backwards compatibility an
d yields | |
| good packing on typical 32-bit and 64-bit platforms. | | good packing on typical 32-bit and 64-bit platforms. | |
| @ingroup task_scheduling */ | | @ingroup task_scheduling */ | |
| class task_prefix { | | class task_prefix { | |
| private: | | private: | |
| friend class tbb::task; | | friend class tbb::task; | |
|
| | | friend class tbb::interface5::internal::task_base; | |
| friend class tbb::task_list; | | friend class tbb::task_list; | |
| friend class internal::scheduler; | | friend class internal::scheduler; | |
| friend class internal::allocate_root_proxy; | | friend class internal::allocate_root_proxy; | |
| friend class internal::allocate_child_proxy; | | friend class internal::allocate_child_proxy; | |
| friend class internal::allocate_continuation_proxy; | | friend class internal::allocate_continuation_proxy; | |
| friend class internal::allocate_additional_child_of_proxy; | | friend class internal::allocate_additional_child_of_proxy; | |
|
| friend class internal::task_group_base; | | | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| //! Shared context that is used to communicate asynchronous state c
hanges | | //! Shared context that is used to communicate asynchronous state c
hanges | |
| /** Currently it is used to broadcast cancellation requests generat
ed both | | /** Currently it is used to broadcast cancellation requests generat
ed both | |
| by users and as the result of unhandled exceptions in the task:
:execute() | | by users and as the result of unhandled exceptions in the task:
:execute() | |
| methods. */ | | methods. */ | |
| task_group_context *context; | | task_group_context *context; | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
| //! The scheduler that allocated the task, or NULL if the task is b
ig. | | //! The scheduler that allocated the task, or NULL if the task is b
ig. | |
| /** Small tasks are pooled by the scheduler that allocated the task
. | | /** Small tasks are pooled by the scheduler that allocated the task
. | |
| If a scheduler needs to free a small task allocated by another
scheduler, | | If a scheduler needs to free a small task allocated by another
scheduler, | |
| it returns the task to that other scheduler. This policy avoid
s | | it returns the task to that other scheduler. This policy avoid
s | |
| memory space blowup issues for memory allocators that allocate
from | | memory space blowup issues for memory allocators that allocate
from | |
| thread-specific pools. */ | | thread-specific pools. */ | |
| scheduler* origin; | | scheduler* origin; | |
| | | | |
| //! The scheduler that owns the task. | | //! The scheduler that owns the task. | |
| | | | |
| skipping to change at line 167 | | skipping to change at line 207 | |
| continuation of the parent. */ | | continuation of the parent. */ | |
| tbb::task* parent; | | tbb::task* parent; | |
| | | | |
| //! Reference count used for synchronization. | | //! Reference count used for synchronization. | |
| /** In the "continuation-passing style" of programming, this field
is | | /** In the "continuation-passing style" of programming, this field
is | |
| the difference of the number of allocated children minus the | | the difference of the number of allocated children minus the | |
| number of children that have completed. | | number of children that have completed. | |
| In the "blocking style" of programming, this field is one more
than the difference. */ | | In the "blocking style" of programming, this field is one more
than the difference. */ | |
| reference_count ref_count; | | reference_count ref_count; | |
| | | | |
|
| //! Scheduling depth | | //! Obsolete. Used to be scheduling depth before TBB 2.2 | |
| | | /** Retained only for the sake of backward binary compatibility. ** | |
| | | / | |
| int depth; | | int depth; | |
| | | | |
| //! A task::state_type, stored as a byte for compactness. | | //! A task::state_type, stored as a byte for compactness. | |
| /** This state is exposed to users via method task::state(). */ | | /** This state is exposed to users via method task::state(). */ | |
| unsigned char state; | | unsigned char state; | |
| | | | |
| //! Miscellaneous state that is not directly visible to users, stor
ed as a byte for compactness. | | //! Miscellaneous state that is not directly visible to users, stor
ed as a byte for compactness. | |
| /** 0x0 -> version 1.0 task | | /** 0x0 -> version 1.0 task | |
|
| 0x1 -> version 3.0 task | | 0x1 -> version >=2.1 task | |
| 0x2 -> task_proxy | | 0x20 -> task_proxy | |
| 0x40 -> task has live ref_count */ | | 0x40 -> task has live ref_count | |
| | | 0x80 -> a stolen task */ | |
| unsigned char extra_state; | | unsigned char extra_state; | |
| | | | |
| affinity_id affinity; | | affinity_id affinity; | |
| | | | |
| //! "next" field for list of task | | //! "next" field for list of task | |
| tbb::task* next; | | tbb::task* next; | |
| | | | |
| //! The task corresponding to this task_prefix. | | //! The task corresponding to this task_prefix. | |
| tbb::task& task() {return *reinterpret_cast<tbb::task*>(this+1);} | | tbb::task& task() {return *reinterpret_cast<tbb::task*>(this+1);} | |
| }; | | }; | |
| | | | |
| } // namespace internal | | } // namespace internal | |
| //! @endcond | | //! @endcond | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| | | | |
| #if TBB_USE_CAPTURED_EXCEPTION | | #if TBB_USE_CAPTURED_EXCEPTION | |
| class tbb_exception; | | class tbb_exception; | |
| #else | | #else | |
| namespace internal { | | namespace internal { | |
| class tbb_exception_ptr; | | class tbb_exception_ptr; | |
| } | | } | |
| #endif /* !TBB_USE_CAPTURED_EXCEPTION */ | | #endif /* !TBB_USE_CAPTURED_EXCEPTION */ | |
| | | | |
| //! Used to form groups of tasks | | //! Used to form groups of tasks | |
| | | | |
| skipping to change at line 224 | | skipping to change at line 266 | |
| | | | |
| The context can be bound to another one, and other contexts can be boun
d to it, | | The context can be bound to another one, and other contexts can be boun
d to it, | |
| forming a tree-like structure: parent -> this -> children. Arrows here
designate | | forming a tree-like structure: parent -> this -> children. Arrows here
designate | |
| cancellation propagation direction. If a task in a cancellation group i
s canceled | | cancellation propagation direction. If a task in a cancellation group i
s canceled | |
| all the other tasks in this group and groups bound to it (as children)
get canceled too. | | all the other tasks in this group and groups bound to it (as children)
get canceled too. | |
| | | | |
| IMPLEMENTATION NOTE: | | IMPLEMENTATION NOTE: | |
| When adding new members to task_group_context or changing types of exis
ting ones, | | When adding new members to task_group_context or changing types of exis
ting ones, | |
| update the size of both padding buffers (_leading_padding and _trailing
_padding) | | update the size of both padding buffers (_leading_padding and _trailing
_padding) | |
| appropriately. See also VERSIONING NOTE at the constructor definition b
elow. **/ | | appropriately. See also VERSIONING NOTE at the constructor definition b
elow. **/ | |
|
| class task_group_context : internal::no_copy | | class task_group_context : internal::no_copy { | |
| { | | | |
| private: | | private: | |
| #if TBB_USE_CAPTURED_EXCEPTION | | #if TBB_USE_CAPTURED_EXCEPTION | |
| typedef tbb_exception exception_container_type; | | typedef tbb_exception exception_container_type; | |
| #else | | #else | |
| typedef internal::tbb_exception_ptr exception_container_type; | | typedef internal::tbb_exception_ptr exception_container_type; | |
| #endif | | #endif | |
| | | | |
| enum version_traits_word_layout { | | enum version_traits_word_layout { | |
| traits_offset = 16, | | traits_offset = 16, | |
| version_mask = 0xFFFF, | | version_mask = 0xFFFF, | |
| | | | |
| skipping to change at line 271 | | skipping to change at line 312 | |
| }; | | }; | |
| | | | |
| //! Pointer to the context of the parent cancellation group. NULL for i
solated contexts. | | //! Pointer to the context of the parent cancellation group. NULL for i
solated contexts. | |
| task_group_context *my_parent; | | task_group_context *my_parent; | |
| | | | |
| //! Used to form the thread specific list of contexts without additiona
l memory allocation. | | //! Used to form the thread specific list of contexts without additiona
l memory allocation. | |
| /** A context is included into the list of the current thread when its
binding to | | /** A context is included into the list of the current thread when its
binding to | |
| its parent happens. Any context can be present in the list of one t
hread only. **/ | | its parent happens. Any context can be present in the list of one t
hread only. **/ | |
| internal::context_list_node_t my_node; | | internal::context_list_node_t my_node; | |
| | | | |
|
| | | //! Used to set and maintain stack stitching point for Intel Performanc | |
| | | e Tools. | |
| | | __itt_caller itt_caller; | |
| | | | |
| //! Leading padding protecting accesses to frequently used members from
false sharing. | | //! Leading padding protecting accesses to frequently used members from
false sharing. | |
| /** Read accesses to the field my_cancellation_requested are on the hot
path inside | | /** Read accesses to the field my_cancellation_requested are on the hot
path inside | |
| the scheduler. This padding ensures that this field never shares th
e same cache | | the scheduler. This padding ensures that this field never shares th
e same cache | |
| line with a local variable that is frequently written to. **/ | | line with a local variable that is frequently written to. **/ | |
| char _leading_padding[internal::NFS_MaxLineSize - | | char _leading_padding[internal::NFS_MaxLineSize - | |
|
| 2 * sizeof(uintptr_t)- sizeof(void*) - sizeof(internal: | | 2 * sizeof(uintptr_t)- sizeof(void*) - sizeof(internal: | |
| :context_list_node_t)]; | | :context_list_node_t) | |
| | | - sizeof(__itt_caller)]; | |
| | | | |
| //! Specifies whether cancellation was request for this task group. | | //! Specifies whether cancellation was request for this task group. | |
| uintptr_t my_cancellation_requested; | | uintptr_t my_cancellation_requested; | |
| | | | |
| //! Version for run-time checks and behavioral traits of the context. | | //! Version for run-time checks and behavioral traits of the context. | |
| /** Version occupies low 16 bits, and traits (zero or more ORed enumera
tors | | /** Version occupies low 16 bits, and traits (zero or more ORed enumera
tors | |
| from the traits_type enumerations) take the next 16 bits. | | from the traits_type enumerations) take the next 16 bits. | |
| Original (zeroth) version of the context did not support any traits
. **/ | | Original (zeroth) version of the context did not support any traits
. **/ | |
| uintptr_t my_version_and_traits; | | uintptr_t my_version_and_traits; | |
| | | | |
| | | | |
| skipping to change at line 382 | | skipping to change at line 427 | |
| //! Out-of-line part of the constructor. | | //! Out-of-line part of the constructor. | |
| /** Singled out to ensure backward binary compatibility of the future v
ersions. **/ | | /** Singled out to ensure backward binary compatibility of the future v
ersions. **/ | |
| void __TBB_EXPORTED_METHOD init (); | | void __TBB_EXPORTED_METHOD init (); | |
| | | | |
| private: | | private: | |
| friend class task; | | friend class task; | |
| friend class internal::allocate_root_with_context_proxy; | | friend class internal::allocate_root_with_context_proxy; | |
| | | | |
| static const kind_type binding_required = bound; | | static const kind_type binding_required = bound; | |
| static const kind_type binding_completed = kind_type(bound+1); | | static const kind_type binding_completed = kind_type(bound+1); | |
|
| | | static const kind_type detached = kind_type(binding_completed+1); | |
| | | static const kind_type dying = kind_type(detached+1); | |
| | | | |
| //! Checks if any of the ancestors has a cancellation request outstandi
ng, | | //! Checks if any of the ancestors has a cancellation request outstandi
ng, | |
| //! and propagates it back to descendants. | | //! and propagates it back to descendants. | |
| void propagate_cancellation_from_ancestors (); | | void propagate_cancellation_from_ancestors (); | |
| | | | |
| //! For debugging purposes only. | | //! For debugging purposes only. | |
| bool is_alive () { | | bool is_alive () { | |
| #if TBB_USE_DEBUG | | #if TBB_USE_DEBUG | |
| return my_version_and_traits != 0xDeadBeef; | | return my_version_and_traits != 0xDeadBeef; | |
| #else | | #else | |
| return true; | | return true; | |
| #endif /* TBB_USE_DEBUG */ | | #endif /* TBB_USE_DEBUG */ | |
| } | | } | |
| }; // class task_group_context | | }; // class task_group_context | |
| | | | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
| //! Base class for user-defined tasks. | | //! Base class for user-defined tasks. | |
| /** @ingroup task_scheduling */ | | /** @ingroup task_scheduling */ | |
|
| class task: internal::no_copy { | | class task: __TBB_TASK_BASE_ACCESS interface5::internal::task_base { | |
| | | | |
| //! Set reference count | | //! Set reference count | |
| void __TBB_EXPORTED_METHOD internal_set_ref_count( int count ); | | void __TBB_EXPORTED_METHOD internal_set_ref_count( int count ); | |
| | | | |
|
| //! Decrement reference count and return true if non-zero. | | //! Decrement reference count and return its new value. | |
| internal::reference_count __TBB_EXPORTED_METHOD internal_decrement_ref_
count(); | | internal::reference_count __TBB_EXPORTED_METHOD internal_decrement_ref_
count(); | |
| | | | |
| protected: | | protected: | |
| //! Default constructor. | | //! Default constructor. | |
| task() {prefix().extra_state=1;} | | task() {prefix().extra_state=1;} | |
| | | | |
| public: | | public: | |
| //! Destructor. | | //! Destructor. | |
| virtual ~task() {} | | virtual ~task() {} | |
| | | | |
| | | | |
| skipping to change at line 432 | | skipping to change at line 480 | |
| executing, | | executing, | |
| //! task to be rescheduled. | | //! task to be rescheduled. | |
| reexecute, | | reexecute, | |
| //! task is in ready pool, or is going to be put there, or was just
taken off. | | //! task is in ready pool, or is going to be put there, or was just
taken off. | |
| ready, | | ready, | |
| //! task object is freshly allocated or recycled. | | //! task object is freshly allocated or recycled. | |
| allocated, | | allocated, | |
| //! task object is on free list, or is going to be put there, or wa
s just taken off. | | //! task object is on free list, or is going to be put there, or wa
s just taken off. | |
| freed, | | freed, | |
| //! task to be recycled as continuation | | //! task to be recycled as continuation | |
|
| recycle | | recycle, | |
| | | //! task to be scheduled for starvation-resistant execution | |
| | | to_enqueue | |
| }; | | }; | |
| | | | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| // Allocating tasks | | // Allocating tasks | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| | | | |
| //! Returns proxy for overloaded new that allocates a root task. | | //! Returns proxy for overloaded new that allocates a root task. | |
| static internal::allocate_root_proxy allocate_root() { | | static internal::allocate_root_proxy allocate_root() { | |
| return internal::allocate_root_proxy(); | | return internal::allocate_root_proxy(); | |
| } | | } | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| //! Returns proxy for overloaded new that allocates a root task associa
ted with user supplied context. | | //! Returns proxy for overloaded new that allocates a root task associa
ted with user supplied context. | |
| static internal::allocate_root_with_context_proxy allocate_root( task_g
roup_context& ctx ) { | | static internal::allocate_root_with_context_proxy allocate_root( task_g
roup_context& ctx ) { | |
| return internal::allocate_root_with_context_proxy(ctx); | | return internal::allocate_root_with_context_proxy(ctx); | |
| } | | } | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
| //! Returns proxy for overloaded new that allocates a continuation task
of *this. | | //! Returns proxy for overloaded new that allocates a continuation task
of *this. | |
| /** The continuation's parent becomes the parent of *this. */ | | /** The continuation's parent becomes the parent of *this. */ | |
| internal::allocate_continuation_proxy& allocate_continuation() { | | internal::allocate_continuation_proxy& allocate_continuation() { | |
| return *reinterpret_cast<internal::allocate_continuation_proxy*>(th
is); | | return *reinterpret_cast<internal::allocate_continuation_proxy*>(th
is); | |
| } | | } | |
| | | | |
| //! Returns proxy for overloaded new that allocates a child task of *th
is. | | //! Returns proxy for overloaded new that allocates a child task of *th
is. | |
| internal::allocate_child_proxy& allocate_child() { | | internal::allocate_child_proxy& allocate_child() { | |
| return *reinterpret_cast<internal::allocate_child_proxy*>(this); | | return *reinterpret_cast<internal::allocate_child_proxy*>(this); | |
| } | | } | |
| | | | |
| //! Like allocate_child, except that task's parent becomes "t", not thi
s. | | //! Like allocate_child, except that task's parent becomes "t", not thi
s. | |
| /** Typically used in conjunction with schedule_to_reexecute to impleme
nt while loops. | | /** Typically used in conjunction with schedule_to_reexecute to impleme
nt while loops. | |
| Atomically increments the reference count of t.parent() */ | | Atomically increments the reference count of t.parent() */ | |
| internal::allocate_additional_child_of_proxy allocate_additional_child_
of( task& t ) { | | internal::allocate_additional_child_of_proxy allocate_additional_child_
of( task& t ) { | |
| return internal::allocate_additional_child_of_proxy(*this,t); | | return internal::allocate_additional_child_of_proxy(*this,t); | |
| } | | } | |
| | | | |
|
| | | #if TBB_DEPRECATED_TASK_INTERFACE | |
| //! Destroy a task. | | //! Destroy a task. | |
| /** Usually, calling this method is unnecessary, because a task is | | /** Usually, calling this method is unnecessary, because a task is | |
| implicitly deleted after its execute() method runs. However, | | implicitly deleted after its execute() method runs. However, | |
| sometimes a task needs to be explicitly deallocated, such as | | sometimes a task needs to be explicitly deallocated, such as | |
| when a root task is used as the parent in spawn_and_wait_for_all. *
/ | | when a root task is used as the parent in spawn_and_wait_for_all. *
/ | |
|
| void __TBB_EXPORTED_METHOD destroy( task& victim ); | | void __TBB_EXPORTED_METHOD destroy( task& t ); | |
| | | #else | |
| | | //! Define recommended static form via import from base class. | |
| | | using task_base::destroy; | |
| | | #endif /* TBB_DEPRECATED_TASK_INTERFACE */ | |
| | | | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| // Recycling of tasks | | // Recycling of tasks | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| | | | |
| //! Change this to be a continuation of its former self. | | //! Change this to be a continuation of its former self. | |
| /** The caller must guarantee that the task's refcount does not become
zero until | | /** The caller must guarantee that the task's refcount does not become
zero until | |
| after the method execute() returns. Typically, this is done by hav
ing | | after the method execute() returns. Typically, this is done by hav
ing | |
| method execute() return a pointer to a child of the task. If the g
uarantee | | method execute() return a pointer to a child of the task. If the g
uarantee | |
| cannot be made, use method recycle_as_safe_continuation instead. | | cannot be made, use method recycle_as_safe_continuation instead. | |
| | | | |
| Because of the hazard, this method may be deprecated in the future.
*/ | | Because of the hazard, this method may be deprecated in the future.
*/ | |
| void recycle_as_continuation() { | | void recycle_as_continuation() { | |
| __TBB_ASSERT( prefix().state==executing, "execute not running?" ); | | __TBB_ASSERT( prefix().state==executing, "execute not running?" ); | |
| prefix().state = allocated; | | prefix().state = allocated; | |
| } | | } | |
| | | | |
| //! Recommended to use, safe variant of recycle_as_continuation | | //! Recommended to use, safe variant of recycle_as_continuation | |
|
| /** For safety, it requires additional increment of ref_count. */ | | /** For safety, it requires additional increment of ref_count. | |
| | | With no decendants and ref_count of 1, it has the semantics of recy | |
| | | cle_to_reexecute. */ | |
| void recycle_as_safe_continuation() { | | void recycle_as_safe_continuation() { | |
| __TBB_ASSERT( prefix().state==executing, "execute not running?" ); | | __TBB_ASSERT( prefix().state==executing, "execute not running?" ); | |
| prefix().state = recycle; | | prefix().state = recycle; | |
| } | | } | |
| | | | |
| //! Change this to be a child of new_parent. | | //! Change this to be a child of new_parent. | |
| void recycle_as_child_of( task& new_parent ) { | | void recycle_as_child_of( task& new_parent ) { | |
| internal::task_prefix& p = prefix(); | | internal::task_prefix& p = prefix(); | |
| __TBB_ASSERT( prefix().state==executing||prefix().state==allocated,
"execute not running, or already recycled" ); | | __TBB_ASSERT( prefix().state==executing||prefix().state==allocated,
"execute not running, or already recycled" ); | |
| __TBB_ASSERT( prefix().ref_count==0, "no child tasks allowed when r
ecycled as a child" ); | | __TBB_ASSERT( prefix().ref_count==0, "no child tasks allowed when r
ecycled as a child" ); | |
| __TBB_ASSERT( p.parent==NULL, "parent must be null" ); | | __TBB_ASSERT( p.parent==NULL, "parent must be null" ); | |
| __TBB_ASSERT( new_parent.prefix().state<=recycle, "corrupt parent's
state" ); | | __TBB_ASSERT( new_parent.prefix().state<=recycle, "corrupt parent's
state" ); | |
| __TBB_ASSERT( new_parent.prefix().state!=freed, "parent already fre
ed" ); | | __TBB_ASSERT( new_parent.prefix().state!=freed, "parent already fre
ed" ); | |
| p.state = allocated; | | p.state = allocated; | |
| p.parent = &new_parent; | | p.parent = &new_parent; | |
|
| p.depth = new_parent.prefix().depth+1; | | #if __TBB_TASK_GROUP_CONTEXT | |
| #if __TBB_EXCEPTIONS | | | |
| p.context = new_parent.prefix().context; | | p.context = new_parent.prefix().context; | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| } | | } | |
| | | | |
| //! Schedule this for reexecution after current execute() returns. | | //! Schedule this for reexecution after current execute() returns. | |
|
| /** Requires that this.execute() be running. */ | | /** Made obsolete by recycle_as_safe_continuation; may become deprecate
d. */ | |
| void recycle_to_reexecute() { | | void recycle_to_reexecute() { | |
| __TBB_ASSERT( prefix().state==executing, "execute not running, or a
lready recycled" ); | | __TBB_ASSERT( prefix().state==executing, "execute not running, or a
lready recycled" ); | |
| __TBB_ASSERT( prefix().ref_count==0, "no child tasks allowed when r
ecycled for reexecution" ); | | __TBB_ASSERT( prefix().ref_count==0, "no child tasks allowed when r
ecycled for reexecution" ); | |
| prefix().state = reexecute; | | prefix().state = reexecute; | |
| } | | } | |
| | | | |
|
| #if __TBB_TASK_DEQUE | | | |
| // All depth-related methods are obsolete, and are retained for the sak
e | | // All depth-related methods are obsolete, and are retained for the sak
e | |
| // of backward source compatibility only | | // of backward source compatibility only | |
| intptr_t depth() const {return 0;} | | intptr_t depth() const {return 0;} | |
| void set_depth( intptr_t ) {} | | void set_depth( intptr_t ) {} | |
| void add_to_depth( int ) {} | | void add_to_depth( int ) {} | |
| | | | |
|
| #else /* !__TBB_TASK_DEQUE */ | | | |
| //! A scheduling depth. | | | |
| /** Guaranteed to be a signed integral type. */ | | | |
| typedef internal::intptr depth_type; | | | |
| | | | |
| //! Scheduling depth | | | |
| depth_type depth() const {return prefix().depth;} | | | |
| | | | |
| //! Set scheduling depth to given value. | | | |
| /** The depth must be non-negative */ | | | |
| void set_depth( depth_type new_depth ) { | | | |
| __TBB_ASSERT( state()!=ready, "cannot change depth of ready task" ) | | | |
| ; | | | |
| __TBB_ASSERT( new_depth>=0, "depth cannot be negative" ); | | | |
| __TBB_ASSERT( new_depth==int(new_depth), "integer overflow error"); | | | |
| prefix().depth = int(new_depth); | | | |
| } | | | |
| | | | |
| //! Change scheduling depth by given amount. | | | |
| /** The resulting depth must be non-negative. */ | | | |
| void add_to_depth( int delta ) { | | | |
| __TBB_ASSERT( state()!=ready, "cannot change depth of ready task" ) | | | |
| ; | | | |
| __TBB_ASSERT( prefix().depth>=-delta, "depth cannot be negative" ); | | | |
| prefix().depth+=delta; | | | |
| } | | | |
| #endif /* !__TBB_TASK_DEQUE */ | | | |
| | | | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| // Spawning and blocking | | // Spawning and blocking | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| | | | |
| //! Set reference count | | //! Set reference count | |
| void set_ref_count( int count ) { | | void set_ref_count( int count ) { | |
| #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT | | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT | |
| internal_set_ref_count(count); | | internal_set_ref_count(count); | |
| #else | | #else | |
| prefix().ref_count = count; | | prefix().ref_count = count; | |
| #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ | | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ | |
| } | | } | |
| | | | |
| //! Atomically increment reference count. | | //! Atomically increment reference count. | |
| /** Has acquire semantics */ | | /** Has acquire semantics */ | |
| void increment_ref_count() { | | void increment_ref_count() { | |
| __TBB_FetchAndIncrementWacquire( &prefix().ref_count ); | | __TBB_FetchAndIncrementWacquire( &prefix().ref_count ); | |
| } | | } | |
| | | | |
| //! Atomically decrement reference count. | | //! Atomically decrement reference count. | |
|
| /** Has release semanics. */ | | /** Has release semantics. */ | |
| int decrement_ref_count() { | | int decrement_ref_count() { | |
| #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT | | #if TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT | |
| return int(internal_decrement_ref_count()); | | return int(internal_decrement_ref_count()); | |
| #else | | #else | |
| return int(__TBB_FetchAndDecrementWrelease( &prefix().ref_count ))-
1; | | return int(__TBB_FetchAndDecrementWrelease( &prefix().ref_count ))-
1; | |
| #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ | | #endif /* TBB_USE_THREADING_TOOLS||TBB_USE_ASSERT */ | |
| } | | } | |
| | | | |
|
| | | #if TBB_DEPRECATED_TASK_INTERFACE | |
| //! Schedule task for execution when a worker becomes available. | | //! Schedule task for execution when a worker becomes available. | |
| /** After all children spawned so far finish their method task::execute
, | | /** After all children spawned so far finish their method task::execute
, | |
| their parent's method task::execute may start running. Therefore,
it | | their parent's method task::execute may start running. Therefore,
it | |
| is important to ensure that at least one child has not completed un
til | | is important to ensure that at least one child has not completed un
til | |
| the parent is ready to run. */ | | the parent is ready to run. */ | |
|
| void spawn( task& child ) { | | void spawn( task& t ); | |
| #if !__TBB_RELAXED_OWNERSHIP | | | |
| __TBB_ASSERT( is_owned_by_current_thread(), "'this' not owned by cu | | | |
| rrent thread" ); | | | |
| #endif /* !__TBB_RELAXED_OWNERSHIP */ | | | |
| prefix().owner->spawn( child, child.prefix().next ); | | | |
| } | | | |
| | | | |
| //! Spawn multiple tasks and clear list. | | //! Spawn multiple tasks and clear list. | |
|
| /** All of the tasks must be at the same depth. */ | | | |
| void spawn( task_list& list ); | | void spawn( task_list& list ); | |
|
| | | #else | |
| | | //! Define recommended static forms via import from base class. | |
| | | using task_base::spawn; | |
| | | #endif /* TBB_DEPRECATED_TASK_INTERFACE */ | |
| | | | |
| //! Similar to spawn followed by wait_for_all, but more efficient. | | //! Similar to spawn followed by wait_for_all, but more efficient. | |
| void spawn_and_wait_for_all( task& child ) { | | void spawn_and_wait_for_all( task& child ) { | |
|
| #if !__TBB_RELAXED_OWNERSHIP | | | |
| __TBB_ASSERT( is_owned_by_current_thread(), "'this' not owned by cu | | | |
| rrent thread" ); | | | |
| #endif /* !__TBB_RELAXED_OWNERSHIP */ | | | |
| prefix().owner->wait_for_all( *this, &child ); | | prefix().owner->wait_for_all( *this, &child ); | |
| } | | } | |
| | | | |
| //! Similar to spawn followed by wait_for_all, but more efficient. | | //! Similar to spawn followed by wait_for_all, but more efficient. | |
| void __TBB_EXPORTED_METHOD spawn_and_wait_for_all( task_list& list ); | | void __TBB_EXPORTED_METHOD spawn_and_wait_for_all( task_list& list ); | |
| | | | |
| //! Spawn task allocated by allocate_root, wait for it to complete, and
deallocate it. | | //! Spawn task allocated by allocate_root, wait for it to complete, and
deallocate it. | |
|
| /** The thread that calls spawn_root_and_wait must be the same thread | | | |
| that allocated the task. */ | | | |
| static void spawn_root_and_wait( task& root ) { | | static void spawn_root_and_wait( task& root ) { | |
|
| #if !__TBB_RELAXED_OWNERSHIP | | | |
| __TBB_ASSERT( root.is_owned_by_current_thread(), "root not owned by | | | |
| current thread" ); | | | |
| #endif /* !__TBB_RELAXED_OWNERSHIP */ | | | |
| root.prefix().owner->spawn_root_and_wait( root, root.prefix().next
); | | root.prefix().owner->spawn_root_and_wait( root, root.prefix().next
); | |
| } | | } | |
| | | | |
| //! Spawn root tasks on list and wait for all of them to finish. | | //! Spawn root tasks on list and wait for all of them to finish. | |
| /** If there are more tasks than worker threads, the tasks are spawned
in | | /** If there are more tasks than worker threads, the tasks are spawned
in | |
| order of front to back. */ | | order of front to back. */ | |
| static void spawn_root_and_wait( task_list& root_list ); | | static void spawn_root_and_wait( task_list& root_list ); | |
| | | | |
| //! Wait for reference count to become one, and set reference count to
zero. | | //! Wait for reference count to become one, and set reference count to
zero. | |
| /** Works on tasks while waiting. */ | | /** Works on tasks while waiting. */ | |
| void wait_for_all() { | | void wait_for_all() { | |
|
| #if !__TBB_RELAXED_OWNERSHIP | | | |
| __TBB_ASSERT( is_owned_by_current_thread(), "'this' not owned by cu | | | |
| rrent thread" ); | | | |
| #endif /* !__TBB_RELAXED_OWNERSHIP */ | | | |
| prefix().owner->wait_for_all( *this, NULL ); | | prefix().owner->wait_for_all( *this, NULL ); | |
| } | | } | |
| | | | |
|
| | | #if __TBB_ARENA_PER_MASTER | |
| | | //! Enqueue task for starvation-resistant execution. | |
| | | static void enqueue( task& t ) { | |
| | | t.prefix().owner->enqueue( t, NULL ); | |
| | | } | |
| | | | |
| | | #endif /* __TBB_ARENA_PER_MASTER */ | |
| //! The innermost task being executed or destroyed by the current threa
d at the moment. | | //! The innermost task being executed or destroyed by the current threa
d at the moment. | |
| static task& __TBB_EXPORTED_FUNC self(); | | static task& __TBB_EXPORTED_FUNC self(); | |
| | | | |
| //! task on whose behalf this task is working, or NULL if this is a roo
t. | | //! task on whose behalf this task is working, or NULL if this is a roo
t. | |
| task* parent() const {return prefix().parent;} | | task* parent() const {return prefix().parent;} | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| //! Shared context that is used to communicate asynchronous state chang
es | | //! Shared context that is used to communicate asynchronous state chang
es | |
| task_group_context* context() {return prefix().context;} | | task_group_context* context() {return prefix().context;} | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
|
| //! True if task is owned by different thread than thread that owns its
parent. | | //! True if task was stolen from the task pool of another thread. | |
| bool is_stolen_task() const { | | bool is_stolen_task() const { | |
|
| #if __TBB_PROVIDE_VIRTUAL_SCHEDULER | | return (prefix().extra_state & 0x80)!=0; | |
| // The virtual scheduler directly identifies stolen tasks. | | | |
| int es_virtual_steal = 4; | | | |
| if(prefix().extra_state & es_virtual_steal) | | | |
| return true; | | | |
| #endif /* TBB_PROVIDE_VIRTUAL_SCHEDULER */ | | | |
| internal::task_prefix& p = prefix(); | | | |
| internal::task_prefix& q = parent()->prefix(); | | | |
| return p.owner!=q.owner; | | | |
| } | | } | |
| | | | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| // Debugging | | // Debugging | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| | | | |
| //! Current execution state | | //! Current execution state | |
| state_type state() const {return state_type(prefix().state);} | | state_type state() const {return state_type(prefix().state);} | |
| | | | |
| //! The internal reference count. | | //! The internal reference count. | |
| int ref_count() const { | | int ref_count() const { | |
| #if TBB_USE_ASSERT | | #if TBB_USE_ASSERT | |
|
| internal::reference_count ref_count = prefix().ref_count; | | internal::reference_count ref_count_ = prefix().ref_count; | |
| __TBB_ASSERT( ref_count==int(ref_count), "integer overflow error"); | | __TBB_ASSERT( ref_count_==int(ref_count_), "integer overflow error" | |
| | | ); | |
| #endif | | #endif | |
| return int(prefix().ref_count); | | return int(prefix().ref_count); | |
| } | | } | |
| | | | |
|
| //! True if this task is owned by the calling thread; false otherwise. | | //! Obsolete, and only retained for the sake of backward compatibility.
Always returns true. | |
| bool __TBB_EXPORTED_METHOD is_owned_by_current_thread() const; | | bool __TBB_EXPORTED_METHOD is_owned_by_current_thread() const; | |
| | | | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| // Affinity | | // Affinity | |
| //---------------------------------------------------------------------
--- | | //---------------------------------------------------------------------
--- | |
| | | | |
| //! An id as used for specifying affinity. | | //! An id as used for specifying affinity. | |
| /** Guaranteed to be integral type. Value of 0 means no affinity. */ | | /** Guaranteed to be integral type. Value of 0 means no affinity. */ | |
| typedef internal::affinity_id affinity_id; | | typedef internal::affinity_id affinity_id; | |
| | | | |
| | | | |
| skipping to change at line 700 | | skipping to change at line 715 | |
| //! Current affinity of this task | | //! Current affinity of this task | |
| affinity_id affinity() const {return prefix().affinity;} | | affinity_id affinity() const {return prefix().affinity;} | |
| | | | |
| //! Invoked by scheduler to notify task that it ran on unexpected threa
d. | | //! Invoked by scheduler to notify task that it ran on unexpected threa
d. | |
| /** Invoked before method execute() runs, if task is stolen, or task ha
s | | /** Invoked before method execute() runs, if task is stolen, or task ha
s | |
| affinity but will be executed on another thread. | | affinity but will be executed on another thread. | |
| | | | |
| The default action does nothing. */ | | The default action does nothing. */ | |
| virtual void __TBB_EXPORTED_METHOD note_affinity( affinity_id id ); | | virtual void __TBB_EXPORTED_METHOD note_affinity( affinity_id id ); | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| //! Initiates cancellation of all tasks in this cancellation group and
its subordinate groups. | | //! Initiates cancellation of all tasks in this cancellation group and
its subordinate groups. | |
| /** \return false if cancellation has already been requested, true othe
rwise. **/ | | /** \return false if cancellation has already been requested, true othe
rwise. **/ | |
| bool cancel_group_execution () { return prefix().context->cancel_group_
execution(); } | | bool cancel_group_execution () { return prefix().context->cancel_group_
execution(); } | |
| | | | |
| //! Returns true if the context received cancellation request. | | //! Returns true if the context received cancellation request. | |
| bool is_cancelled () const { return prefix().context->is_group_executio
n_cancelled(); } | | bool is_cancelled () const { return prefix().context->is_group_executio
n_cancelled(); } | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
| private: | | private: | |
|
| | | friend class interface5::internal::task_base; | |
| friend class task_list; | | friend class task_list; | |
| friend class internal::scheduler; | | friend class internal::scheduler; | |
| friend class internal::allocate_root_proxy; | | friend class internal::allocate_root_proxy; | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| friend class internal::allocate_root_with_context_proxy; | | friend class internal::allocate_root_with_context_proxy; | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| friend class internal::allocate_continuation_proxy; | | friend class internal::allocate_continuation_proxy; | |
| friend class internal::allocate_child_proxy; | | friend class internal::allocate_child_proxy; | |
| friend class internal::allocate_additional_child_of_proxy; | | friend class internal::allocate_additional_child_of_proxy; | |
| | | | |
|
| friend class internal::task_group_base; | | | |
| | | | |
| //! Get reference to corresponding task_prefix. | | //! Get reference to corresponding task_prefix. | |
| /** Version tag prevents loader on Linux from using the wrong symbol in
debug builds. **/ | | /** Version tag prevents loader on Linux from using the wrong symbol in
debug builds. **/ | |
| internal::task_prefix& prefix( internal::version_tag* = NULL ) const { | | internal::task_prefix& prefix( internal::version_tag* = NULL ) const { | |
| return reinterpret_cast<internal::task_prefix*>(const_cast<task*>(t
his))[-1]; | | return reinterpret_cast<internal::task_prefix*>(const_cast<task*>(t
his))[-1]; | |
| } | | } | |
| }; // class task | | }; // class task | |
| | | | |
| //! task that does nothing. Useful for synchronization. | | //! task that does nothing. Useful for synchronization. | |
| /** @ingroup task_scheduling */ | | /** @ingroup task_scheduling */ | |
| class empty_task: public task { | | class empty_task: public task { | |
| | | | |
| skipping to change at line 745 | | skipping to change at line 759 | |
| }; | | }; | |
| | | | |
| //! A list of children. | | //! A list of children. | |
| /** Used for method task::spawn_children | | /** Used for method task::spawn_children | |
| @ingroup task_scheduling */ | | @ingroup task_scheduling */ | |
| class task_list: internal::no_copy { | | class task_list: internal::no_copy { | |
| private: | | private: | |
| task* first; | | task* first; | |
| task** next_ptr; | | task** next_ptr; | |
| friend class task; | | friend class task; | |
|
| | | friend class interface5::internal::task_base; | |
| public: | | public: | |
| //! Construct empty list | | //! Construct empty list | |
| task_list() : first(NULL), next_ptr(&first) {} | | task_list() : first(NULL), next_ptr(&first) {} | |
| | | | |
| //! Destroys the list, but does not destroy the task objects. | | //! Destroys the list, but does not destroy the task objects. | |
| ~task_list() {} | | ~task_list() {} | |
| | | | |
| //! True if list if empty; false otherwise. | | //! True if list if empty; false otherwise. | |
| bool empty() const {return !first;} | | bool empty() const {return !first;} | |
| | | | |
| | | | |
| skipping to change at line 778 | | skipping to change at line 793 | |
| return *result; | | return *result; | |
| } | | } | |
| | | | |
| //! Clear the list | | //! Clear the list | |
| void clear() { | | void clear() { | |
| first=NULL; | | first=NULL; | |
| next_ptr=&first; | | next_ptr=&first; | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| inline void task::spawn( task_list& list ) { | | #if TBB_DEPRECATED_TASK_INTERFACE | |
| #if !__TBB_RELAXED_OWNERSHIP | | inline void task::spawn( task& t ) | |
| __TBB_ASSERT( is_owned_by_current_thread(), "'this' not owned by curren | | #else | |
| t thread" ); | | inline void interface5::internal::task_base::spawn( task& t ) | |
| #endif /* !__TBB_RELAXED_OWNERSHIP */ | | #endif | |
| | | { | |
| | | t.prefix().owner->spawn( t, t.prefix().next ); | |
| | | } | |
| | | | |
| | | #if TBB_DEPRECATED_TASK_INTERFACE | |
| | | inline void task::spawn( task_list& list ) | |
| | | #else | |
| | | inline void interface5::internal::task_base::spawn( task_list& list ) | |
| | | #endif | |
| | | { | |
| if( task* t = list.first ) { | | if( task* t = list.first ) { | |
|
| prefix().owner->spawn( *t, *list.next_ptr ); | | t->prefix().owner->spawn( *t, *list.next_ptr ); | |
| list.clear(); | | list.clear(); | |
| } | | } | |
| } | | } | |
| | | | |
| inline void task::spawn_root_and_wait( task_list& root_list ) { | | inline void task::spawn_root_and_wait( task_list& root_list ) { | |
| if( task* t = root_list.first ) { | | if( task* t = root_list.first ) { | |
|
| #if !__TBB_RELAXED_OWNERSHIP | | | |
| __TBB_ASSERT( t->is_owned_by_current_thread(), "'this' not owned by | | | |
| current thread" ); | | | |
| #endif /* !__TBB_RELAXED_OWNERSHIP */ | | | |
| t->prefix().owner->spawn_root_and_wait( *t, *root_list.next_ptr ); | | t->prefix().owner->spawn_root_and_wait( *t, *root_list.next_ptr ); | |
| root_list.clear(); | | root_list.clear(); | |
| } | | } | |
| } | | } | |
| | | | |
| } // namespace tbb | | } // namespace tbb | |
| | | | |
| inline void *operator new( size_t bytes, const tbb::internal::allocate_root
_proxy& ) { | | inline void *operator new( size_t bytes, const tbb::internal::allocate_root
_proxy& ) { | |
| return &tbb::internal::allocate_root_proxy::allocate(bytes); | | return &tbb::internal::allocate_root_proxy::allocate(bytes); | |
| } | | } | |
| | | | |
| inline void operator delete( void* task, const tbb::internal::allocate_root
_proxy& ) { | | inline void operator delete( void* task, const tbb::internal::allocate_root
_proxy& ) { | |
| tbb::internal::allocate_root_proxy::free( *static_cast<tbb::task*>(task
) ); | | tbb::internal::allocate_root_proxy::free( *static_cast<tbb::task*>(task
) ); | |
| } | | } | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| inline void *operator new( size_t bytes, const tbb::internal::allocate_root
_with_context_proxy& p ) { | | inline void *operator new( size_t bytes, const tbb::internal::allocate_root
_with_context_proxy& p ) { | |
| return &p.allocate(bytes); | | return &p.allocate(bytes); | |
| } | | } | |
| | | | |
| inline void operator delete( void* task, const tbb::internal::allocate_root
_with_context_proxy& p ) { | | inline void operator delete( void* task, const tbb::internal::allocate_root
_with_context_proxy& p ) { | |
| p.free( *static_cast<tbb::task*>(task) ); | | p.free( *static_cast<tbb::task*>(task) ); | |
| } | | } | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
| inline void *operator new( size_t bytes, const tbb::internal::allocate_cont
inuation_proxy& p ) { | | inline void *operator new( size_t bytes, const tbb::internal::allocate_cont
inuation_proxy& p ) { | |
| return &p.allocate(bytes); | | return &p.allocate(bytes); | |
| } | | } | |
| | | | |
| inline void operator delete( void* task, const tbb::internal::allocate_cont
inuation_proxy& p ) { | | inline void operator delete( void* task, const tbb::internal::allocate_cont
inuation_proxy& p ) { | |
| p.free( *static_cast<tbb::task*>(task) ); | | p.free( *static_cast<tbb::task*>(task) ); | |
| } | | } | |
| | | | |
| inline void *operator new( size_t bytes, const tbb::internal::allocate_chil
d_proxy& p ) { | | inline void *operator new( size_t bytes, const tbb::internal::allocate_chil
d_proxy& p ) { | |
| | | | |
End of changes. 62 change blocks. |
| 120 lines changed or deleted | | 149 lines changed or added | |
|
| tbb_exception.h | | tbb_exception.h | |
| /* | | /* | |
|
| Copyright 2005-2009 Intel Corporation. All Rights Reserved. | | Copyright 2005-2010 Intel Corporation. All Rights Reserved. | |
| | | | |
| This file is part of Threading Building Blocks. | | This file is part of Threading Building Blocks. | |
| | | | |
| Threading Building Blocks is free software; you can redistribute it | | Threading Building Blocks is free software; you can redistribute it | |
| and/or modify it under the terms of the GNU General Public License | | and/or modify it under the terms of the GNU General Public License | |
| version 2 as published by the Free Software Foundation. | | version 2 as published by the Free Software Foundation. | |
| | | | |
| Threading Building Blocks is distributed in the hope that it will be | | Threading Building Blocks is distributed in the hope that it will be | |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty | | useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| | | | |
| skipping to change at line 33 | | skipping to change at line 33 | |
| file does not by itself cause the resulting executable to be covered by | | file does not by itself cause the resulting executable to be covered by | |
| the GNU General Public License. This exception does not however | | the GNU General Public License. This exception does not however | |
| invalidate any other reasons why the executable file might be covered b
y | | invalidate any other reasons why the executable file might be covered b
y | |
| the GNU General Public License. | | the GNU General Public License. | |
| */ | | */ | |
| | | | |
| #ifndef __TBB_exception_H | | #ifndef __TBB_exception_H | |
| #define __TBB_exception_H | | #define __TBB_exception_H | |
| | | | |
| #include "tbb_stddef.h" | | #include "tbb_stddef.h" | |
|
| | | | |
| | | #if !TBB_USE_EXCEPTIONS && _MSC_VER | |
| | | // Suppress "C++ exception handler used, but unwind semantics are not e | |
| | | nabled" warning in STL headers | |
| | | #pragma warning (push) | |
| | | #pragma warning (disable: 4530) | |
| | | #endif | |
| | | | |
| #include <stdexcept> | | #include <stdexcept> | |
| | | | |
|
| #if __TBB_EXCEPTIONS && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) && ! | | #if !TBB_USE_EXCEPTIONS && _MSC_VER | |
| defined(__SUNPRO_CC) | | #pragma warning (pop) | |
| #error The current compilation environment does not support exception handl | | #endif | |
| ing. Please set __TBB_EXCEPTIONS to 0 in tbb_config.h | | | |
| | | #if __SUNPRO_CC | |
| | | #include <string> // required to construct std exception classes | |
| #endif | | #endif | |
| | | | |
| namespace tbb { | | namespace tbb { | |
| | | | |
| //! Exception for concurrent containers | | //! Exception for concurrent containers | |
| class bad_last_alloc : public std::bad_alloc { | | class bad_last_alloc : public std::bad_alloc { | |
| public: | | public: | |
|
| virtual const char* what() const throw() { return "bad allocation in pr | | /*override*/ const char* what() const throw(); | |
| evious or concurrent attempt"; } | | #if __TBB_DEFAULT_DTOR_THROW_SPEC_BROKEN | |
| virtual ~bad_last_alloc() throw() {} | | /*override*/ ~bad_last_alloc() throw() {} | |
| | | #endif | |
| | | }; | |
| | | | |
| | | //! Exception for PPL locks | |
| | | class improper_lock : public std::exception { | |
| | | public: | |
| | | /*override*/ const char* what() const throw(); | |
| | | }; | |
| | | | |
| | | //! Exception for missing wait on structured_task_group | |
| | | class missing_wait : public std::exception { | |
| | | public: | |
| | | /*override*/ const char* what() const throw(); | |
| | | }; | |
| | | | |
| | | //! Exception for repeated scheduling of the same task_handle | |
| | | class invalid_multiple_scheduling : public std::exception { | |
| | | public: | |
| | | /*override*/ const char* what() const throw(); | |
| }; | | }; | |
| | | | |
| namespace internal { | | namespace internal { | |
|
| void __TBB_EXPORTED_FUNC throw_bad_last_alloc_exception_v4() ; | | //! Obsolete | |
| } // namespace internal | | void __TBB_EXPORTED_FUNC throw_bad_last_alloc_exception_v4(); | |
| | | | |
|
| | | enum exception_id { | |
| | | eid_bad_alloc = 1, | |
| | | eid_bad_last_alloc, | |
| | | eid_nonpositive_step, | |
| | | eid_out_of_range, | |
| | | eid_segment_range_error, | |
| | | eid_index_range_error, | |
| | | eid_missing_wait, | |
| | | eid_invalid_multiple_scheduling, | |
| | | eid_improper_lock, | |
| | | eid_possible_deadlock, | |
| | | eid_operation_not_permitted, | |
| | | eid_condvar_wait_failed, | |
| | | eid_invalid_load_factor, | |
| | | eid_invalid_buckets_number, | |
| | | eid_invalid_swap, | |
| | | eid_reservation_length_error, | |
| | | eid_invalid_key, | |
| | | //! The last enumerator tracks the number of defined IDs. It must remai | |
| | | n the last one. | |
| | | /** When adding new IDs, place them immediately _before_ this comment ( | |
| | | that is | |
| | | _after_ all the existing IDs. NEVER insert new IDs between the exis | |
| | | ting ones. **/ | |
| | | eid_max | |
| | | }; | |
| | | | |
| | | //! Gathers all throw operators in one place. | |
| | | /** Its purpose is to minimize code bloat that can be caused by throw opera | |
| | | tors | |
| | | scattered in multiple places, especially in templates. **/ | |
| | | void __TBB_EXPORTED_FUNC throw_exception_v4 ( exception_id ); | |
| | | | |
| | | //! Versionless convenience wrapper for throw_exception_v4() | |
| | | inline void throw_exception ( exception_id eid ) { throw_exception_v4(eid); | |
| | | } | |
| | | | |
| | | } // namespace internal | |
| } // namespace tbb | | } // namespace tbb | |
| | | | |
|
| #if __TBB_EXCEPTIONS | | #if __TBB_TASK_GROUP_CONTEXT | |
| #include "tbb_allocator.h" | | #include "tbb_allocator.h" | |
| #include <exception> | | #include <exception> | |
| #include <typeinfo> | | #include <typeinfo> | |
| #include <new> | | #include <new> | |
| | | | |
| namespace tbb { | | namespace tbb { | |
| | | | |
| //! Interface to be implemented by all exceptions TBB recognizes and propag
ates across the threads. | | //! Interface to be implemented by all exceptions TBB recognizes and propag
ates across the threads. | |
| /** If an unhandled exception of the type derived from tbb::tbb_exception i
s intercepted | | /** If an unhandled exception of the type derived from tbb::tbb_exception i
s intercepted | |
| by the TBB scheduler in one of the worker threads, it is delivered to a
nd re-thrown in | | by the TBB scheduler in one of the worker threads, it is delivered to a
nd re-thrown in | |
| | | | |
| skipping to change at line 83 | | skipping to change at line 147 | |
| NOTE: In case of nested algorithms or complex task hierarchies when the
nested | | NOTE: In case of nested algorithms or complex task hierarchies when the
nested | |
| levels share (explicitly or by means of implicit inheritance) the task
group | | levels share (explicitly or by means of implicit inheritance) the task
group | |
| context of the outermost level, the exception may be (re-)thrown multip
le times | | context of the outermost level, the exception may be (re-)thrown multip
le times | |
| (ultimately - in each worker on each nesting level) before reaching the
root | | (ultimately - in each worker on each nesting level) before reaching the
root | |
| thread at the outermost level. IMPORTANT: if you intercept an exception
derived | | thread at the outermost level. IMPORTANT: if you intercept an exception
derived | |
| from this class on a nested level, you must re-throw it in the catch bl
ock by means | | from this class on a nested level, you must re-throw it in the catch bl
ock by means | |
| of the "throw;" operator. | | of the "throw;" operator. | |
| | | | |
| TBB provides two implementations of this interface: tbb::captured_excep
tion and | | TBB provides two implementations of this interface: tbb::captured_excep
tion and | |
| template class tbb::movable_exception. See their declarations for more
info. **/ | | template class tbb::movable_exception. See their declarations for more
info. **/ | |
|
| class tbb_exception : public std::exception { | | class tbb_exception : public std::exception | |
| | | { | |
| | | /** No operator new is provided because the TBB usage model assumes dyn | |
| | | amic | |
| | | creation of the TBB exception objects only by means of applying mov | |
| | | e() | |
| | | operation on an exception thrown out of TBB scheduler. **/ | |
| | | void* operator new ( size_t ); | |
| | | | |
| public: | | public: | |
| //! Creates and returns pointer to the deep copy of this exception obje
ct. | | //! Creates and returns pointer to the deep copy of this exception obje
ct. | |
| /** Move semantics is allowed. **/ | | /** Move semantics is allowed. **/ | |
| virtual tbb_exception* move () throw() = 0; | | virtual tbb_exception* move () throw() = 0; | |
| | | | |
| //! Destroys objects created by the move() method. | | //! Destroys objects created by the move() method. | |
| /** Frees memory and calls destructor for this exception object. | | /** Frees memory and calls destructor for this exception object. | |
| Can and must be used only on objects created by the move method. **
/ | | Can and must be used only on objects created by the move method. **
/ | |
| virtual void destroy () throw() = 0; | | virtual void destroy () throw() = 0; | |
| | | | |
| | | | |
| skipping to change at line 106 | | skipping to change at line 176 | |
| you implement or override this method on the most derived level. Th
e implementation | | you implement or override this method on the most derived level. Th
e implementation | |
| is as simple as "throw *this;". Failure to do this will result in e
xception | | is as simple as "throw *this;". Failure to do this will result in e
xception | |
| of a base class type being thrown. **/ | | of a base class type being thrown. **/ | |
| virtual void throw_self () = 0; | | virtual void throw_self () = 0; | |
| | | | |
| //! Returns RTTI name of the originally intercepted exception | | //! Returns RTTI name of the originally intercepted exception | |
| virtual const char* name() const throw() = 0; | | virtual const char* name() const throw() = 0; | |
| | | | |
| //! Returns the result of originally intercepted exception's what() met
hod. | | //! Returns the result of originally intercepted exception's what() met
hod. | |
| virtual const char* what() const throw() = 0; | | virtual const char* what() const throw() = 0; | |
|
| | | | |
| | | /** Operator delete is provided only to allow using existing smart poin | |
| | | ters | |
| | | with TBB exception objects obtained as the result of applying move( | |
| | | ) | |
| | | operation on an exception thrown out of TBB scheduler. | |
| | | | |
| | | When overriding method move() make sure to override operator delete | |
| | | as well | |
| | | if memory is allocated not by TBB's scalable allocator. **/ | |
| | | void operator delete ( void* p ) { | |
| | | internal::deallocate_via_handler_v3(p); | |
| | | } | |
| }; | | }; | |
| | | | |
| //! This class is used by TBB to propagate information about unhandled exce
ptions into the root thread. | | //! This class is used by TBB to propagate information about unhandled exce
ptions into the root thread. | |
| /** Exception of this type is thrown by TBB in the root thread (thread that
started a parallel | | /** Exception of this type is thrown by TBB in the root thread (thread that
started a parallel | |
| algorithm ) if an unhandled exception was intercepted during the algori
thm execution in one | | algorithm ) if an unhandled exception was intercepted during the algori
thm execution in one | |
| of the workers. | | of the workers. | |
| \sa tbb::tbb_exception **/ | | \sa tbb::tbb_exception **/ | |
| class captured_exception : public tbb_exception | | class captured_exception : public tbb_exception | |
| { | | { | |
| public: | | public: | |
| captured_exception ( const captured_exception& src ) | | captured_exception ( const captured_exception& src ) | |
| : tbb_exception(src), my_dynamic(false) | | : tbb_exception(src), my_dynamic(false) | |
| { | | { | |
| set(src.my_exception_name, src.my_exception_info); | | set(src.my_exception_name, src.my_exception_info); | |
| } | | } | |
| | | | |
|
| captured_exception ( const char* name, const char* info ) | | captured_exception ( const char* name_, const char* info ) | |
| : my_dynamic(false) | | : my_dynamic(false) | |
| { | | { | |
|
| set(name, info); | | set(name_, info); | |
| } | | } | |
| | | | |
| __TBB_EXPORTED_METHOD ~captured_exception () throw() { | | __TBB_EXPORTED_METHOD ~captured_exception () throw() { | |
| clear(); | | clear(); | |
| } | | } | |
| | | | |
| captured_exception& operator= ( const captured_exception& src ) { | | captured_exception& operator= ( const captured_exception& src ) { | |
| if ( this != &src ) { | | if ( this != &src ) { | |
| clear(); | | clear(); | |
| set(src.my_exception_name, src.my_exception_info); | | set(src.my_exception_name, src.my_exception_info); | |
| } | | } | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| /*override*/ | | /*override*/ | |
|
| captured_exception* move () throw(); | | captured_exception* __TBB_EXPORTED_METHOD move () throw(); | |
| | | | |
| /*override*/ | | /*override*/ | |
|
| void destroy () throw(); | | void __TBB_EXPORTED_METHOD destroy () throw(); | |
| | | | |
| /*override*/ | | /*override*/ | |
|
| void throw_self () { throw *this; } | | void throw_self () { __TBB_THROW(*this); } | |
| | | | |
| /*override*/ | | /*override*/ | |
| const char* __TBB_EXPORTED_METHOD name() const throw(); | | const char* __TBB_EXPORTED_METHOD name() const throw(); | |
| | | | |
| /*override*/ | | /*override*/ | |
| const char* __TBB_EXPORTED_METHOD what() const throw(); | | const char* __TBB_EXPORTED_METHOD what() const throw(); | |
| | | | |
|
| | | void __TBB_EXPORTED_METHOD set ( const char* name, const char* info ) t | |
| | | hrow(); | |
| | | void __TBB_EXPORTED_METHOD clear () throw(); | |
| | | | |
| private: | | private: | |
| //! Used only by method clone(). | | //! Used only by method clone(). | |
| captured_exception() {} | | captured_exception() {} | |
| | | | |
|
| //! Functionally equivalent to {captured_exception e(name,info); return
c.clone();} | | //! Functionally equivalent to {captured_exception e(name,info); return
e.clone();} | |
| static captured_exception* allocate ( const char* name, const char* inf
o ); | | static captured_exception* allocate ( const char* name, const char* inf
o ); | |
| | | | |
|
| void set ( const char* name, const char* info ) throw(); | | | |
| void clear () throw(); | | | |
| | | | |
| bool my_dynamic; | | bool my_dynamic; | |
| const char* my_exception_name; | | const char* my_exception_name; | |
| const char* my_exception_info; | | const char* my_exception_info; | |
| }; | | }; | |
| | | | |
| //! Template that can be used to implement exception that transfers arbitra
ry ExceptionData to the root thread | | //! Template that can be used to implement exception that transfers arbitra
ry ExceptionData to the root thread | |
| /** Code using TBB can instantiate this template with an arbitrary Exceptio
nData type | | /** Code using TBB can instantiate this template with an arbitrary Exceptio
nData type | |
| and throw this exception object. Such exceptions are intercepted by the
TBB scheduler | | and throw this exception object. Such exceptions are intercepted by the
TBB scheduler | |
| and delivered to the root thread (). | | and delivered to the root thread (). | |
| \sa tbb::tbb_exception **/ | | \sa tbb::tbb_exception **/ | |
| template<typename ExceptionData> | | template<typename ExceptionData> | |
| class movable_exception : public tbb_exception | | class movable_exception : public tbb_exception | |
| { | | { | |
| typedef movable_exception<ExceptionData> self_type; | | typedef movable_exception<ExceptionData> self_type; | |
| | | | |
| public: | | public: | |
|
| movable_exception ( const ExceptionData& data ) | | movable_exception ( const ExceptionData& data_ ) | |
| : my_exception_data(data) | | : my_exception_data(data_) | |
| , my_dynamic(false) | | , my_dynamic(false) | |
|
| , my_exception_name(typeid(self_type).name()) | | , my_exception_name( | |
| | | #if TBB_USE_EXCEPTIONS | |
| | | typeid(self_type).name() | |
| | | #else /* !TBB_USE_EXCEPTIONS */ | |
| | | "movable_exception" | |
| | | #endif /* !TBB_USE_EXCEPTIONS */ | |
| | | ) | |
| {} | | {} | |
| | | | |
| movable_exception ( const movable_exception& src ) throw () | | movable_exception ( const movable_exception& src ) throw () | |
| : tbb_exception(src) | | : tbb_exception(src) | |
| , my_exception_data(src.my_exception_data) | | , my_exception_data(src.my_exception_data) | |
| , my_dynamic(false) | | , my_dynamic(false) | |
| , my_exception_name(src.my_exception_name) | | , my_exception_name(src.my_exception_name) | |
| {} | | {} | |
| | | | |
| ~movable_exception () throw() {} | | ~movable_exception () throw() {} | |
| | | | |
| skipping to change at line 216 | | skipping to change at line 302 | |
| const ExceptionData& data () const throw() { return my_exception_data;
} | | const ExceptionData& data () const throw() { return my_exception_data;
} | |
| | | | |
| /*override*/ const char* name () const throw() { return my_exception_na
me; } | | /*override*/ const char* name () const throw() { return my_exception_na
me; } | |
| | | | |
| /*override*/ const char* what () const throw() { return "tbb::movable_e
xception"; } | | /*override*/ const char* what () const throw() { return "tbb::movable_e
xception"; } | |
| | | | |
| /*override*/ | | /*override*/ | |
| movable_exception* move () throw() { | | movable_exception* move () throw() { | |
| void* e = internal::allocate_via_handler_v3(sizeof(movable_exceptio
n)); | | void* e = internal::allocate_via_handler_v3(sizeof(movable_exceptio
n)); | |
| if ( e ) { | | if ( e ) { | |
|
| new (e) movable_exception(*this); | | ::new (e) movable_exception(*this); | |
| ((movable_exception*)e)->my_dynamic = true; | | ((movable_exception*)e)->my_dynamic = true; | |
| } | | } | |
| return (movable_exception*)e; | | return (movable_exception*)e; | |
| } | | } | |
| /*override*/ | | /*override*/ | |
| void destroy () throw() { | | void destroy () throw() { | |
| __TBB_ASSERT ( my_dynamic, "Method destroy can be called only on dy
namically allocated movable_exceptions" ); | | __TBB_ASSERT ( my_dynamic, "Method destroy can be called only on dy
namically allocated movable_exceptions" ); | |
| if ( my_dynamic ) { | | if ( my_dynamic ) { | |
| this->~movable_exception(); | | this->~movable_exception(); | |
| internal::deallocate_via_handler_v3(this); | | internal::deallocate_via_handler_v3(this); | |
| } | | } | |
| } | | } | |
| /*override*/ | | /*override*/ | |
|
| void throw_self () { | | void throw_self () { __TBB_THROW( *this ); } | |
| throw *this; | | | |
| } | | | |
| | | | |
| protected: | | protected: | |
| //! User data | | //! User data | |
| ExceptionData my_exception_data; | | ExceptionData my_exception_data; | |
| | | | |
| private: | | private: | |
| //! Flag specifying whether this object has been dynamically allocated
(by the move method) | | //! Flag specifying whether this object has been dynamically allocated
(by the move method) | |
| bool my_dynamic; | | bool my_dynamic; | |
| | | | |
| //! RTTI name of this class | | //! RTTI name of this class | |
| | | | |
| skipping to change at line 258 | | skipping to change at line 342 | |
| namespace internal { | | namespace internal { | |
| | | | |
| //! Exception container that preserves the exact copy of the original excep
tion | | //! Exception container that preserves the exact copy of the original excep
tion | |
| /** This class can be used only when the appropriate runtime support (manda
ted | | /** This class can be used only when the appropriate runtime support (manda
ted | |
| by C++0x) is present **/ | | by C++0x) is present **/ | |
| class tbb_exception_ptr { | | class tbb_exception_ptr { | |
| std::exception_ptr my_ptr; | | std::exception_ptr my_ptr; | |
| | | | |
| public: | | public: | |
| static tbb_exception_ptr* allocate (); | | static tbb_exception_ptr* allocate (); | |
|
| static tbb_exception_ptr* allocate ( const tbb_exception& ); | | static tbb_exception_ptr* allocate ( const tbb_exception& tag ); | |
| static tbb_exception_ptr* allocate ( const captured_exception& ); | | //! This overload uses move semantics (i.e. it empties src) | |
| | | static tbb_exception_ptr* allocate ( captured_exception& src ); | |
| | | | |
| //! Destroys this objects | | //! Destroys this objects | |
| /** Note that objects of this type can be created only by the allocate(
) method. **/ | | /** Note that objects of this type can be created only by the allocate(
) method. **/ | |
| void destroy () throw(); | | void destroy () throw(); | |
| | | | |
| //! Throws the contained exception . | | //! Throws the contained exception . | |
| void throw_self () { std::rethrow_exception(my_ptr); } | | void throw_self () { std::rethrow_exception(my_ptr); } | |
| | | | |
| private: | | private: | |
| tbb_exception_ptr ( const std::exception_ptr& src ) : my_ptr(src) {} | | tbb_exception_ptr ( const std::exception_ptr& src ) : my_ptr(src) {} | |
| tbb_exception_ptr ( const captured_exception& src ) : my_ptr(std::copy_
exception(src)) {} | | tbb_exception_ptr ( const captured_exception& src ) : my_ptr(std::copy_
exception(src)) {} | |
| }; // class tbb::internal::tbb_exception_ptr | | }; // class tbb::internal::tbb_exception_ptr | |
| | | | |
| } // namespace internal | | } // namespace internal | |
| #endif /* !TBB_USE_CAPTURED_EXCEPTION */ | | #endif /* !TBB_USE_CAPTURED_EXCEPTION */ | |
| | | | |
| } // namespace tbb | | } // namespace tbb | |
| | | | |
|
| #endif /* __TBB_EXCEPTIONS */ | | #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| | | | |
| #endif /* __TBB_exception_H */ | | #endif /* __TBB_exception_H */ | |
| | | | |
End of changes. 23 change blocks. |
| 31 lines changed or deleted | | 125 lines changed or added | |
|