From ed29a8a9d1bf7e0916c10032e7253d8065100fc4 Mon Sep 17 00:00:00 2001 From: rvidye Date: Mon, 22 Jun 2026 13:39:07 -0600 Subject: [PATCH 1/7] remove vendored TBB binaries and headers --- include/oneapi/tbb.h | 74 - include/oneapi/tbb/blocked_range.h | 170 - include/oneapi/tbb/blocked_range2d.h | 111 - include/oneapi/tbb/blocked_range3d.h | 130 - include/oneapi/tbb/blocked_rangeNd.h | 147 - include/oneapi/tbb/cache_aligned_allocator.h | 189 - include/oneapi/tbb/collaborative_call_once.h | 235 -- include/oneapi/tbb/combinable.h | 69 - include/oneapi/tbb/concurrent_hash_map.h | 1664 -------- include/oneapi/tbb/concurrent_lru_cache.h | 374 -- include/oneapi/tbb/concurrent_map.h | 350 -- .../oneapi/tbb/concurrent_priority_queue.h | 490 --- include/oneapi/tbb/concurrent_queue.h | 700 ---- include/oneapi/tbb/concurrent_set.h | 267 -- include/oneapi/tbb/concurrent_unordered_map.h | 414 -- include/oneapi/tbb/concurrent_unordered_set.h | 333 -- include/oneapi/tbb/concurrent_vector.h | 1129 ------ include/oneapi/tbb/detail/_aggregator.h | 176 - include/oneapi/tbb/detail/_aligned_space.h | 46 - include/oneapi/tbb/detail/_allocator_traits.h | 107 - include/oneapi/tbb/detail/_assert.h | 64 - include/oneapi/tbb/detail/_attach.h | 32 - .../tbb/detail/_concurrent_queue_base.h | 650 ---- .../oneapi/tbb/detail/_concurrent_skip_list.h | 1290 ------- .../tbb/detail/_concurrent_unordered_base.h | 1514 -------- include/oneapi/tbb/detail/_config.h | 529 --- .../oneapi/tbb/detail/_containers_helpers.h | 67 - include/oneapi/tbb/detail/_exception.h | 88 - include/oneapi/tbb/detail/_export.h | 46 - .../oneapi/tbb/detail/_flow_graph_body_impl.h | 385 -- .../tbb/detail/_flow_graph_cache_impl.h | 434 --- include/oneapi/tbb/detail/_flow_graph_impl.h | 476 --- .../tbb/detail/_flow_graph_indexer_impl.h | 351 -- .../tbb/detail/_flow_graph_item_buffer_impl.h | 279 -- .../oneapi/tbb/detail/_flow_graph_join_impl.h | 1708 --------- .../oneapi/tbb/detail/_flow_graph_node_impl.h | 774 ---- .../tbb/detail/_flow_graph_node_set_impl.h | 265 -- .../tbb/detail/_flow_graph_nodes_deduction.h | 277 -- .../detail/_flow_graph_tagged_buffer_impl.h | 257 -- .../tbb/detail/_flow_graph_trace_impl.h | 364 -- .../tbb/detail/_flow_graph_types_impl.h | 407 -- include/oneapi/tbb/detail/_hash_compare.h | 147 - .../oneapi/tbb/detail/_intrusive_list_node.h | 41 - include/oneapi/tbb/detail/_machine.h | 373 -- include/oneapi/tbb/detail/_mutex_common.h | 61 - .../oneapi/tbb/detail/_namespace_injection.h | 24 - include/oneapi/tbb/detail/_node_handle.h | 162 - include/oneapi/tbb/detail/_pipeline_filters.h | 455 --- .../tbb/detail/_pipeline_filters_deduction.h | 46 - include/oneapi/tbb/detail/_range_common.h | 130 - include/oneapi/tbb/detail/_rtm_mutex.h | 162 - include/oneapi/tbb/detail/_rtm_rw_mutex.h | 215 -- include/oneapi/tbb/detail/_scoped_lock.h | 174 - include/oneapi/tbb/detail/_segment_table.h | 566 --- .../oneapi/tbb/detail/_small_object_pool.h | 108 - include/oneapi/tbb/detail/_string_resource.h | 78 - include/oneapi/tbb/detail/_task.h | 232 -- include/oneapi/tbb/detail/_task_handle.h | 122 - include/oneapi/tbb/detail/_template_helpers.h | 404 -- include/oneapi/tbb/detail/_utils.h | 387 -- include/oneapi/tbb/detail/_waitable_atomic.h | 104 - .../oneapi/tbb/enumerable_thread_specific.h | 1113 ------ include/oneapi/tbb/flow_graph.h | 3376 ----------------- include/oneapi/tbb/flow_graph_abstractions.h | 51 - include/oneapi/tbb/global_control.h | 200 - include/oneapi/tbb/info.h | 125 - include/oneapi/tbb/memory_pool.h | 272 -- include/oneapi/tbb/mutex.h | 95 - include/oneapi/tbb/null_mutex.h | 80 - include/oneapi/tbb/null_rw_mutex.h | 87 - include/oneapi/tbb/parallel_for.h | 469 --- include/oneapi/tbb/parallel_for_each.h | 681 ---- include/oneapi/tbb/parallel_invoke.h | 227 -- include/oneapi/tbb/parallel_pipeline.h | 153 - include/oneapi/tbb/parallel_reduce.h | 771 ---- include/oneapi/tbb/parallel_scan.h | 630 --- include/oneapi/tbb/parallel_sort.h | 288 -- include/oneapi/tbb/partitioner.h | 681 ---- include/oneapi/tbb/profiling.h | 243 -- include/oneapi/tbb/queuing_mutex.h | 192 - include/oneapi/tbb/queuing_rw_mutex.h | 207 - include/oneapi/tbb/rw_mutex.h | 216 -- include/oneapi/tbb/scalable_allocator.h | 334 -- include/oneapi/tbb/spin_mutex.h | 134 - include/oneapi/tbb/spin_rw_mutex.h | 229 -- include/oneapi/tbb/task.h | 37 - include/oneapi/tbb/task_arena.h | 498 --- include/oneapi/tbb/task_group.h | 746 ---- include/oneapi/tbb/task_scheduler_observer.h | 116 - include/oneapi/tbb/tbb_allocator.h | 126 - include/oneapi/tbb/tbbmalloc_proxy.h | 65 - include/oneapi/tbb/tick_count.h | 99 - include/oneapi/tbb/version.h | 114 - libraries/tbb/tbb.def | 150 - libraries/tbb/tbb12.dll | Bin 290944 -> 0 bytes libraries/tbb/tbb12.lib | Bin 44060 -> 0 bytes libraries/tbb/tbb12_debug.dll | Bin 759424 -> 0 bytes libraries/tbb/tbbbind.dll | Bin 47232 -> 0 bytes libraries/tbb/tbbbind_2_0.dll | Bin 47232 -> 0 bytes libraries/tbb/tbbbind_2_0_debug.dll | Bin 125056 -> 0 bytes libraries/tbb/tbbbind_2_5.dll | Bin 47744 -> 0 bytes libraries/tbb/tbbbind_2_5_debug.dll | Bin 128640 -> 0 bytes libraries/tbb/tbbbind_debug.dll | Bin 125568 -> 0 bytes libraries/tbb/tbbmalloc.def | 47 - libraries/tbb/tbbmalloc.dll | Bin 133760 -> 0 bytes libraries/tbb/tbbmalloc_debug.dll | Bin 336512 -> 0 bytes libraries/tbb/tbbmalloc_proxy.dll | Bin 83584 -> 0 bytes libraries/tbb/tbbmalloc_proxy_debug.dll | Bin 120448 -> 0 bytes 108 files changed, 34275 deletions(-) delete mode 100644 include/oneapi/tbb.h delete mode 100644 include/oneapi/tbb/blocked_range.h delete mode 100644 include/oneapi/tbb/blocked_range2d.h delete mode 100644 include/oneapi/tbb/blocked_range3d.h delete mode 100644 include/oneapi/tbb/blocked_rangeNd.h delete mode 100644 include/oneapi/tbb/cache_aligned_allocator.h delete mode 100644 include/oneapi/tbb/collaborative_call_once.h delete mode 100644 include/oneapi/tbb/combinable.h delete mode 100644 include/oneapi/tbb/concurrent_hash_map.h delete mode 100644 include/oneapi/tbb/concurrent_lru_cache.h delete mode 100644 include/oneapi/tbb/concurrent_map.h delete mode 100644 include/oneapi/tbb/concurrent_priority_queue.h delete mode 100644 include/oneapi/tbb/concurrent_queue.h delete mode 100644 include/oneapi/tbb/concurrent_set.h delete mode 100644 include/oneapi/tbb/concurrent_unordered_map.h delete mode 100644 include/oneapi/tbb/concurrent_unordered_set.h delete mode 100644 include/oneapi/tbb/concurrent_vector.h delete mode 100644 include/oneapi/tbb/detail/_aggregator.h delete mode 100644 include/oneapi/tbb/detail/_aligned_space.h delete mode 100644 include/oneapi/tbb/detail/_allocator_traits.h delete mode 100644 include/oneapi/tbb/detail/_assert.h delete mode 100644 include/oneapi/tbb/detail/_attach.h delete mode 100644 include/oneapi/tbb/detail/_concurrent_queue_base.h delete mode 100644 include/oneapi/tbb/detail/_concurrent_skip_list.h delete mode 100644 include/oneapi/tbb/detail/_concurrent_unordered_base.h delete mode 100644 include/oneapi/tbb/detail/_config.h delete mode 100644 include/oneapi/tbb/detail/_containers_helpers.h delete mode 100644 include/oneapi/tbb/detail/_exception.h delete mode 100644 include/oneapi/tbb/detail/_export.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_body_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_cache_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_indexer_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_item_buffer_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_join_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_node_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_node_set_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_nodes_deduction.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_tagged_buffer_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_trace_impl.h delete mode 100644 include/oneapi/tbb/detail/_flow_graph_types_impl.h delete mode 100644 include/oneapi/tbb/detail/_hash_compare.h delete mode 100644 include/oneapi/tbb/detail/_intrusive_list_node.h delete mode 100644 include/oneapi/tbb/detail/_machine.h delete mode 100644 include/oneapi/tbb/detail/_mutex_common.h delete mode 100644 include/oneapi/tbb/detail/_namespace_injection.h delete mode 100644 include/oneapi/tbb/detail/_node_handle.h delete mode 100644 include/oneapi/tbb/detail/_pipeline_filters.h delete mode 100644 include/oneapi/tbb/detail/_pipeline_filters_deduction.h delete mode 100644 include/oneapi/tbb/detail/_range_common.h delete mode 100644 include/oneapi/tbb/detail/_rtm_mutex.h delete mode 100644 include/oneapi/tbb/detail/_rtm_rw_mutex.h delete mode 100644 include/oneapi/tbb/detail/_scoped_lock.h delete mode 100644 include/oneapi/tbb/detail/_segment_table.h delete mode 100644 include/oneapi/tbb/detail/_small_object_pool.h delete mode 100644 include/oneapi/tbb/detail/_string_resource.h delete mode 100644 include/oneapi/tbb/detail/_task.h delete mode 100644 include/oneapi/tbb/detail/_task_handle.h delete mode 100644 include/oneapi/tbb/detail/_template_helpers.h delete mode 100644 include/oneapi/tbb/detail/_utils.h delete mode 100644 include/oneapi/tbb/detail/_waitable_atomic.h delete mode 100644 include/oneapi/tbb/enumerable_thread_specific.h delete mode 100644 include/oneapi/tbb/flow_graph.h delete mode 100644 include/oneapi/tbb/flow_graph_abstractions.h delete mode 100644 include/oneapi/tbb/global_control.h delete mode 100644 include/oneapi/tbb/info.h delete mode 100644 include/oneapi/tbb/memory_pool.h delete mode 100644 include/oneapi/tbb/mutex.h delete mode 100644 include/oneapi/tbb/null_mutex.h delete mode 100644 include/oneapi/tbb/null_rw_mutex.h delete mode 100644 include/oneapi/tbb/parallel_for.h delete mode 100644 include/oneapi/tbb/parallel_for_each.h delete mode 100644 include/oneapi/tbb/parallel_invoke.h delete mode 100644 include/oneapi/tbb/parallel_pipeline.h delete mode 100644 include/oneapi/tbb/parallel_reduce.h delete mode 100644 include/oneapi/tbb/parallel_scan.h delete mode 100644 include/oneapi/tbb/parallel_sort.h delete mode 100644 include/oneapi/tbb/partitioner.h delete mode 100644 include/oneapi/tbb/profiling.h delete mode 100644 include/oneapi/tbb/queuing_mutex.h delete mode 100644 include/oneapi/tbb/queuing_rw_mutex.h delete mode 100644 include/oneapi/tbb/rw_mutex.h delete mode 100644 include/oneapi/tbb/scalable_allocator.h delete mode 100644 include/oneapi/tbb/spin_mutex.h delete mode 100644 include/oneapi/tbb/spin_rw_mutex.h delete mode 100644 include/oneapi/tbb/task.h delete mode 100644 include/oneapi/tbb/task_arena.h delete mode 100644 include/oneapi/tbb/task_group.h delete mode 100644 include/oneapi/tbb/task_scheduler_observer.h delete mode 100644 include/oneapi/tbb/tbb_allocator.h delete mode 100644 include/oneapi/tbb/tbbmalloc_proxy.h delete mode 100644 include/oneapi/tbb/tick_count.h delete mode 100644 include/oneapi/tbb/version.h delete mode 100644 libraries/tbb/tbb.def delete mode 100644 libraries/tbb/tbb12.dll delete mode 100644 libraries/tbb/tbb12.lib delete mode 100644 libraries/tbb/tbb12_debug.dll delete mode 100644 libraries/tbb/tbbbind.dll delete mode 100644 libraries/tbb/tbbbind_2_0.dll delete mode 100644 libraries/tbb/tbbbind_2_0_debug.dll delete mode 100644 libraries/tbb/tbbbind_2_5.dll delete mode 100644 libraries/tbb/tbbbind_2_5_debug.dll delete mode 100644 libraries/tbb/tbbbind_debug.dll delete mode 100644 libraries/tbb/tbbmalloc.def delete mode 100644 libraries/tbb/tbbmalloc.dll delete mode 100644 libraries/tbb/tbbmalloc_debug.dll delete mode 100644 libraries/tbb/tbbmalloc_proxy.dll delete mode 100644 libraries/tbb/tbbmalloc_proxy_debug.dll diff --git a/include/oneapi/tbb.h b/include/oneapi/tbb.h deleted file mode 100644 index 3782c74..0000000 --- a/include/oneapi/tbb.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_tbb_H -#define __TBB_tbb_H - -/** - This header bulk-includes declarations or definitions of all the functionality - provided by TBB (save for tbbmalloc and 3rd party dependent headers). - - If you use only a few TBB constructs, consider including specific headers only. - Any header listed below can be included independently of others. -**/ - -#include "oneapi/tbb/blocked_range.h" -#include "oneapi/tbb/blocked_range2d.h" -#include "oneapi/tbb/blocked_range3d.h" -#if TBB_PREVIEW_BLOCKED_RANGE_ND -#include "tbb/blocked_rangeNd.h" -#endif -#include "oneapi/tbb/cache_aligned_allocator.h" -#include "oneapi/tbb/combinable.h" -#include "oneapi/tbb/concurrent_hash_map.h" -#if TBB_PREVIEW_CONCURRENT_LRU_CACHE -#include "tbb/concurrent_lru_cache.h" -#endif -#include "oneapi/tbb/collaborative_call_once.h" -#include "oneapi/tbb/concurrent_priority_queue.h" -#include "oneapi/tbb/concurrent_queue.h" -#include "oneapi/tbb/concurrent_unordered_map.h" -#include "oneapi/tbb/concurrent_unordered_set.h" -#include "oneapi/tbb/concurrent_map.h" -#include "oneapi/tbb/concurrent_set.h" -#include "oneapi/tbb/concurrent_vector.h" -#include "oneapi/tbb/enumerable_thread_specific.h" -#include "oneapi/tbb/flow_graph.h" -#include "oneapi/tbb/global_control.h" -#include "oneapi/tbb/info.h" -#include "oneapi/tbb/null_mutex.h" -#include "oneapi/tbb/null_rw_mutex.h" -#include "oneapi/tbb/parallel_for.h" -#include "oneapi/tbb/parallel_for_each.h" -#include "oneapi/tbb/parallel_invoke.h" -#include "oneapi/tbb/parallel_pipeline.h" -#include "oneapi/tbb/parallel_reduce.h" -#include "oneapi/tbb/parallel_scan.h" -#include "oneapi/tbb/parallel_sort.h" -#include "oneapi/tbb/partitioner.h" -#include "oneapi/tbb/queuing_mutex.h" -#include "oneapi/tbb/queuing_rw_mutex.h" -#include "oneapi/tbb/spin_mutex.h" -#include "oneapi/tbb/spin_rw_mutex.h" -#include "oneapi/tbb/task.h" -#include "oneapi/tbb/task_arena.h" -#include "oneapi/tbb/task_group.h" -#include "oneapi/tbb/task_scheduler_observer.h" -#include "oneapi/tbb/tbb_allocator.h" -#include "oneapi/tbb/tick_count.h" -#include "oneapi/tbb/version.h" - -#endif /* __TBB_tbb_H */ diff --git a/include/oneapi/tbb/blocked_range.h b/include/oneapi/tbb/blocked_range.h deleted file mode 100644 index 12862fa..0000000 --- a/include/oneapi/tbb/blocked_range.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_blocked_range_H -#define __TBB_blocked_range_H - -#include - -#include "detail/_range_common.h" -#include "detail/_namespace_injection.h" - -#include "version.h" - -namespace tbb { -namespace detail { -namespace d1 { - -/** \page range_req Requirements on range concept - Class \c R implementing the concept of range must define: - - \code R::R( const R& ); \endcode Copy constructor - - \code R::~R(); \endcode Destructor - - \code bool R::is_divisible() const; \endcode True if range can be partitioned into two subranges - - \code bool R::empty() const; \endcode True if range is empty - - \code R::R( R& r, split ); \endcode Split range \c r into two subranges. -**/ - -//! A range over which to iterate. -/** @ingroup algorithms */ -template - __TBB_requires(blocked_range_value) -class blocked_range { -public: - //! Type of a value - /** Called a const_iterator for sake of algorithms that need to treat a blocked_range - as an STL container. */ - using const_iterator = Value; - - //! Type for size of a range - using size_type = std::size_t; - - //! Construct range over half-open interval [begin,end), with the given grainsize. - blocked_range( Value begin_, Value end_, size_type grainsize_=1 ) : - my_end(end_), my_begin(begin_), my_grainsize(grainsize_) - { - __TBB_ASSERT( my_grainsize>0, "grainsize must be positive" ); - } - - //! Beginning of range. - const_iterator begin() const { return my_begin; } - - //! One past last value in range. - const_iterator end() const { return my_end; } - - //! Size of the range - /** Unspecified if end() - __TBB_requires(blocked_range_value && - blocked_range_value) - friend class blocked_range2d; - - template - __TBB_requires(blocked_range_value && - blocked_range_value && - blocked_range_value) - friend class blocked_range3d; - - template - __TBB_requires(blocked_range_value) - friend class blocked_rangeNd_impl; -}; - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::blocked_range; -// Split types -using detail::split; -using detail::proportional_split; -} // namespace v1 - -} // namespace tbb - -#endif /* __TBB_blocked_range_H */ diff --git a/include/oneapi/tbb/blocked_range2d.h b/include/oneapi/tbb/blocked_range2d.h deleted file mode 100644 index 41385db..0000000 --- a/include/oneapi/tbb/blocked_range2d.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_blocked_range2d_H -#define __TBB_blocked_range2d_H - -#include - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_range_common.h" - -#include "blocked_range.h" - -namespace tbb { -namespace detail { -namespace d1 { - -//! A 2-dimensional range that models the Range concept. -/** @ingroup algorithms */ -template - __TBB_requires(blocked_range_value && - blocked_range_value) -class blocked_range2d { -public: - //! Type for size of an iteration range - using row_range_type = blocked_range; - using col_range_type = blocked_range; - -private: - row_range_type my_rows; - col_range_type my_cols; - -public: - blocked_range2d( RowValue row_begin, RowValue row_end, typename row_range_type::size_type row_grainsize, - ColValue col_begin, ColValue col_end, typename col_range_type::size_type col_grainsize ) : - my_rows(row_begin,row_end,row_grainsize), - my_cols(col_begin,col_end,col_grainsize) - {} - - blocked_range2d( RowValue row_begin, RowValue row_end, - ColValue col_begin, ColValue col_end ) : - my_rows(row_begin,row_end), - my_cols(col_begin,col_end) - {} - - //! True if range is empty - bool empty() const { - // Range is empty if at least one dimension is empty. - return my_rows.empty() || my_cols.empty(); - } - - //! True if range is divisible into two pieces. - bool is_divisible() const { - return my_rows.is_divisible() || my_cols.is_divisible(); - } - - blocked_range2d( blocked_range2d& r, split ) : - my_rows(r.my_rows), - my_cols(r.my_cols) - { - split split_obj; - do_split(r, split_obj); - } - - blocked_range2d( blocked_range2d& r, proportional_split& proportion ) : - my_rows(r.my_rows), - my_cols(r.my_cols) - { - do_split(r, proportion); - } - - //! The rows of the iteration space - const row_range_type& rows() const { return my_rows; } - - //! The columns of the iteration space - const col_range_type& cols() const { return my_cols; } - -private: - template - void do_split( blocked_range2d& r, Split& split_obj ) { - if ( my_rows.size()*double(my_cols.grainsize()) < my_cols.size()*double(my_rows.grainsize()) ) { - my_cols.my_begin = col_range_type::do_split(r.my_cols, split_obj); - } else { - my_rows.my_begin = row_range_type::do_split(r.my_rows, split_obj); - } - } -}; - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::blocked_range2d; -} // namespace v1 -} // namespace tbb - -#endif /* __TBB_blocked_range2d_H */ diff --git a/include/oneapi/tbb/blocked_range3d.h b/include/oneapi/tbb/blocked_range3d.h deleted file mode 100644 index d893219..0000000 --- a/include/oneapi/tbb/blocked_range3d.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_blocked_range3d_H -#define __TBB_blocked_range3d_H - -#include - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" - -#include "blocked_range.h" - -namespace tbb { -namespace detail { -namespace d1 { - -//! A 3-dimensional range that models the Range concept. -/** @ingroup algorithms */ -template - __TBB_requires(blocked_range_value && - blocked_range_value && - blocked_range_value) -class blocked_range3d { -public: - //! Type for size of an iteration range - using page_range_type = blocked_range; - using row_range_type = blocked_range; - using col_range_type = blocked_range; - -private: - page_range_type my_pages; - row_range_type my_rows; - col_range_type my_cols; - -public: - - blocked_range3d( PageValue page_begin, PageValue page_end, - RowValue row_begin, RowValue row_end, - ColValue col_begin, ColValue col_end ) : - my_pages(page_begin,page_end), - my_rows(row_begin,row_end), - my_cols(col_begin,col_end) - {} - - blocked_range3d( PageValue page_begin, PageValue page_end, typename page_range_type::size_type page_grainsize, - RowValue row_begin, RowValue row_end, typename row_range_type::size_type row_grainsize, - ColValue col_begin, ColValue col_end, typename col_range_type::size_type col_grainsize ) : - my_pages(page_begin,page_end,page_grainsize), - my_rows(row_begin,row_end,row_grainsize), - my_cols(col_begin,col_end,col_grainsize) - {} - - //! True if range is empty - bool empty() const { - // Range is empty if at least one dimension is empty. - return my_pages.empty() || my_rows.empty() || my_cols.empty(); - } - - //! True if range is divisible into two pieces. - bool is_divisible() const { - return my_pages.is_divisible() || my_rows.is_divisible() || my_cols.is_divisible(); - } - - blocked_range3d( blocked_range3d& r, split split_obj ) : - my_pages(r.my_pages), - my_rows(r.my_rows), - my_cols(r.my_cols) - { - do_split(r, split_obj); - } - - blocked_range3d( blocked_range3d& r, proportional_split& proportion ) : - my_pages(r.my_pages), - my_rows(r.my_rows), - my_cols(r.my_cols) - { - do_split(r, proportion); - } - - //! The pages of the iteration space - const page_range_type& pages() const { return my_pages; } - - //! The rows of the iteration space - const row_range_type& rows() const { return my_rows; } - - //! The columns of the iteration space - const col_range_type& cols() const { return my_cols; } - -private: - template - void do_split( blocked_range3d& r, Split& split_obj) { - if ( my_pages.size()*double(my_rows.grainsize()) < my_rows.size()*double(my_pages.grainsize()) ) { - if ( my_rows.size()*double(my_cols.grainsize()) < my_cols.size()*double(my_rows.grainsize()) ) { - my_cols.my_begin = col_range_type::do_split(r.my_cols, split_obj); - } else { - my_rows.my_begin = row_range_type::do_split(r.my_rows, split_obj); - } - } else { - if ( my_pages.size()*double(my_cols.grainsize()) < my_cols.size()*double(my_pages.grainsize()) ) { - my_cols.my_begin = col_range_type::do_split(r.my_cols, split_obj); - } else { - my_pages.my_begin = page_range_type::do_split(r.my_pages, split_obj); - } - } - } -}; - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::blocked_range3d; -} // namespace v1 -} // namespace tbb - -#endif /* __TBB_blocked_range3d_H */ diff --git a/include/oneapi/tbb/blocked_rangeNd.h b/include/oneapi/tbb/blocked_rangeNd.h deleted file mode 100644 index a7ba137..0000000 --- a/include/oneapi/tbb/blocked_rangeNd.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - Copyright (c) 2017-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_blocked_rangeNd_H -#define __TBB_blocked_rangeNd_H - -#if !TBB_PREVIEW_BLOCKED_RANGE_ND - #error Set TBB_PREVIEW_BLOCKED_RANGE_ND to include blocked_rangeNd.h -#endif - -#include // std::any_of -#include -#include -#include // std::is_same, std::enable_if - -#include "detail/_config.h" -#include "detail/_template_helpers.h" // index_sequence, make_index_sequence -#include "detail/_range_common.h" - -#include "blocked_range.h" - -namespace tbb { -namespace detail { -namespace d1 { - -/* - The blocked_rangeNd_impl uses make_index_sequence to automatically generate a ctor with - exactly N arguments of the type tbb::blocked_range. Such ctor provides an opportunity - to use braced-init-list parameters to initialize each dimension. - Use of parameters, whose representation is a braced-init-list, but they're not - std::initializer_list or a reference to one, produces a non-deduced context - within template argument deduction. - - NOTE: blocked_rangeNd must be exactly a templated alias to the blocked_rangeNd_impl - (and not e.g. a derived class), otherwise it would need to declare its own ctor - facing the same problem that the impl class solves. -*/ - -template> - __TBB_requires(blocked_range_value) -class blocked_rangeNd_impl; - -template - __TBB_requires(blocked_range_value) -class blocked_rangeNd_impl> { -public: - //! Type of a value. - using value_type = Value; - -private: - //! Helper type to construct range with N tbb::blocked_range objects. - template - using dim_type_helper = tbb::blocked_range; - -public: - blocked_rangeNd_impl() = delete; - - //! Constructs N-dimensional range over N half-open intervals each represented as tbb::blocked_range. - blocked_rangeNd_impl(const dim_type_helper&... args) : my_dims{ {args...} } {} - - //! Dimensionality of a range. - static constexpr unsigned int ndims() { return N; } - - //! Range in certain dimension. - const tbb::blocked_range& dim(unsigned int dimension) const { - __TBB_ASSERT(dimension < N, "out of bound"); - return my_dims[dimension]; - } - - //------------------------------------------------------------------------ - // Methods that implement Range concept - //------------------------------------------------------------------------ - - //! True if at least one dimension is empty. - bool empty() const { - return std::any_of(my_dims.begin(), my_dims.end(), [](const tbb::blocked_range& d) { - return d.empty(); - }); - } - - //! True if at least one dimension is divisible. - bool is_divisible() const { - return std::any_of(my_dims.begin(), my_dims.end(), [](const tbb::blocked_range& d) { - return d.is_divisible(); - }); - } - - blocked_rangeNd_impl(blocked_rangeNd_impl& r, proportional_split proportion) : my_dims(r.my_dims) { - do_split(r, proportion); - } - - blocked_rangeNd_impl(blocked_rangeNd_impl& r, split proportion) : my_dims(r.my_dims) { - do_split(r, proportion); - } - -private: - static_assert(N != 0, "zero dimensional blocked_rangeNd can't be constructed"); - - //! Ranges in each dimension. - std::array, N> my_dims; - - template - void do_split(blocked_rangeNd_impl& r, split_type proportion) { - static_assert((std::is_same::value || std::is_same::value), "type of split object is incorrect"); - __TBB_ASSERT(r.is_divisible(), "can't split not divisible range"); - - auto my_it = std::max_element(my_dims.begin(), my_dims.end(), [](const tbb::blocked_range& first, const tbb::blocked_range& second) { - return (first.size() * second.grainsize() < second.size() * first.grainsize()); - }); - - auto r_it = r.my_dims.begin() + (my_it - my_dims.begin()); - - my_it->my_begin = tbb::blocked_range::do_split(*r_it, proportion); - - // (!(my_it->my_begin < r_it->my_end) && !(r_it->my_end < my_it->my_begin)) equals to - // (my_it->my_begin == r_it->my_end), but we can't use operator== due to Value concept - __TBB_ASSERT(!(my_it->my_begin < r_it->my_end) && !(r_it->my_end < my_it->my_begin), - "blocked_range has been split incorrectly"); - } -}; - -template -using blocked_rangeNd = blocked_rangeNd_impl; - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::blocked_rangeNd; -} // namespace v1 -} // namespace tbb - -#endif /* __TBB_blocked_rangeNd_H */ - diff --git a/include/oneapi/tbb/cache_aligned_allocator.h b/include/oneapi/tbb/cache_aligned_allocator.h deleted file mode 100644 index 0ff3972..0000000 --- a/include/oneapi/tbb/cache_aligned_allocator.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_cache_aligned_allocator_H -#define __TBB_cache_aligned_allocator_H - -#include "detail/_utils.h" -#include "detail/_namespace_injection.h" -#include -#include - -#if __TBB_CPP17_MEMORY_RESOURCE_PRESENT -#include -#endif - -namespace tbb { -namespace detail { - -namespace r1 { -TBB_EXPORT void* __TBB_EXPORTED_FUNC cache_aligned_allocate(std::size_t size); -TBB_EXPORT void __TBB_EXPORTED_FUNC cache_aligned_deallocate(void* p); -TBB_EXPORT std::size_t __TBB_EXPORTED_FUNC cache_line_size(); -} - -namespace d1 { - -template -class cache_aligned_allocator { -public: - using value_type = T; - using propagate_on_container_move_assignment = std::true_type; - - //! Always defined for TBB containers (supported since C++17 for std containers) - using is_always_equal = std::true_type; - - cache_aligned_allocator() = default; - template cache_aligned_allocator(const cache_aligned_allocator&) noexcept {} - - //! Allocate space for n objects, starting on a cache/sector line. - __TBB_nodiscard T* allocate(std::size_t n) { - return static_cast(r1::cache_aligned_allocate(n * sizeof(value_type))); - } - - //! Free block of memory that starts on a cache line - void deallocate(T* p, std::size_t) { - r1::cache_aligned_deallocate(p); - } - - //! Largest value for which method allocate might succeed. - std::size_t max_size() const noexcept { - return (~std::size_t(0) - r1::cache_line_size()) / sizeof(value_type); - } - -#if TBB_ALLOCATOR_TRAITS_BROKEN - using pointer = value_type*; - using const_pointer = const value_type*; - using reference = value_type&; - using const_reference = const value_type&; - using difference_type = std::ptrdiff_t; - using size_type = std::size_t; - template struct rebind { - using other = cache_aligned_allocator; - }; - template - void construct(U *p, Args&&... args) - { ::new (p) U(std::forward(args)...); } - void destroy(pointer p) { p->~value_type(); } - pointer address(reference x) const { return &x; } - const_pointer address(const_reference x) const { return &x; } -#endif // TBB_ALLOCATOR_TRAITS_BROKEN -}; - -#if TBB_ALLOCATOR_TRAITS_BROKEN - template<> - class cache_aligned_allocator { - public: - using pointer = void*; - using const_pointer = const void*; - using value_type = void; - template struct rebind { - using other = cache_aligned_allocator; - }; - }; -#endif - -template -bool operator==(const cache_aligned_allocator&, const cache_aligned_allocator&) noexcept { return true; } - -#if !__TBB_CPP20_COMPARISONS_PRESENT -template -bool operator!=(const cache_aligned_allocator&, const cache_aligned_allocator&) noexcept { return false; } -#endif - -#if __TBB_CPP17_MEMORY_RESOURCE_PRESENT - -//! C++17 memory resource wrapper to ensure cache line size alignment -class cache_aligned_resource : public std::pmr::memory_resource { -public: - cache_aligned_resource() : cache_aligned_resource(std::pmr::get_default_resource()) {} - explicit cache_aligned_resource(std::pmr::memory_resource* upstream) : m_upstream(upstream) {} - - std::pmr::memory_resource* upstream_resource() const { - return m_upstream; - } - -private: - //! We don't know what memory resource set. Use padding to guarantee alignment - void* do_allocate(std::size_t bytes, std::size_t alignment) override { - // TODO: make it common with tbb_allocator.cpp - std::size_t cache_line_alignment = correct_alignment(alignment); - std::size_t space = correct_size(bytes) + cache_line_alignment; - std::uintptr_t base = reinterpret_cast(m_upstream->allocate(space)); - __TBB_ASSERT(base != 0, "Upstream resource returned nullptr."); - - // Round up to the next cache line (align the base address) - std::uintptr_t result = (base + cache_line_alignment) & ~(cache_line_alignment - 1); - __TBB_ASSERT((result - base) >= sizeof(std::uintptr_t), "Can`t store a base pointer to the header"); - __TBB_ASSERT(space - (result - base) >= bytes, "Not enough space for the storage"); - - // Record where block actually starts. - (reinterpret_cast(result))[-1] = base; - return reinterpret_cast(result); - } - - void do_deallocate(void* ptr, std::size_t bytes, std::size_t alignment) override { - if (ptr) { - // Recover where block actually starts - std::uintptr_t base = (reinterpret_cast(ptr))[-1]; - m_upstream->deallocate(reinterpret_cast(base), correct_size(bytes) + correct_alignment(alignment)); - } - } - - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { - if (this == &other) { return true; } -#if __TBB_USE_OPTIONAL_RTTI - const cache_aligned_resource* other_res = dynamic_cast(&other); - return other_res && (upstream_resource() == other_res->upstream_resource()); -#else - return false; -#endif - } - - std::size_t correct_alignment(std::size_t alignment) { - __TBB_ASSERT(tbb::detail::is_power_of_two(alignment), "Alignment is not a power of 2"); -#if __TBB_CPP17_HW_INTERFERENCE_SIZE_PRESENT - std::size_t cache_line_size = std::hardware_destructive_interference_size; -#else - std::size_t cache_line_size = r1::cache_line_size(); -#endif - return alignment < cache_line_size ? cache_line_size : alignment; - } - - std::size_t correct_size(std::size_t bytes) { - // To handle the case, when small size requested. There could be not - // enough space to store the original pointer. - return bytes < sizeof(std::uintptr_t) ? sizeof(std::uintptr_t) : bytes; - } - - std::pmr::memory_resource* m_upstream; -}; - -#endif // __TBB_CPP17_MEMORY_RESOURCE_PRESENT - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::cache_aligned_allocator; -#if __TBB_CPP17_MEMORY_RESOURCE_PRESENT -using detail::d1::cache_aligned_resource; -#endif -} // namespace v1 -} // namespace tbb - -#endif /* __TBB_cache_aligned_allocator_H */ - diff --git a/include/oneapi/tbb/collaborative_call_once.h b/include/oneapi/tbb/collaborative_call_once.h deleted file mode 100644 index db082f8..0000000 --- a/include/oneapi/tbb/collaborative_call_once.h +++ /dev/null @@ -1,235 +0,0 @@ -/* - Copyright (c) 2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_collaborative_call_once_H -#define __TBB_collaborative_call_once_H - -#include "task_arena.h" -#include "task_group.h" - -#include - -namespace tbb { -namespace detail { -namespace d1 { - -#if _MSC_VER && !defined(__INTEL_COMPILER) - // Suppress warning: structure was padded due to alignment specifier - #pragma warning (push) - #pragma warning (disable: 4324) -#endif - -constexpr std::uintptr_t collaborative_once_max_references = max_nfs_size; -constexpr std::uintptr_t collaborative_once_references_mask = collaborative_once_max_references-1; - -class alignas(max_nfs_size) collaborative_once_runner : no_copy { - - struct storage_t { - task_arena m_arena{ task_arena::attach{} }; - wait_context m_wait_context{1}; - }; - - std::atomic m_ref_count{0}; - std::atomic m_is_ready{false}; - - // Storage with task_arena and wait_context must be initialized only by winner thread - union { - storage_t m_storage; - }; - - template - void isolated_execute(Fn f) { - auto func = [f] { - f(); - // delegate_base requires bool returning functor while isolate_within_arena ignores the result - return true; - }; - - delegated_function delegate(func); - - r1::isolate_within_arena(delegate, reinterpret_cast(this)); - } - -public: - class lifetime_guard : no_copy { - collaborative_once_runner& m_runner; - public: - lifetime_guard(collaborative_once_runner& r) : m_runner(r) { - m_runner.m_ref_count++; - } - ~lifetime_guard() { - m_runner.m_ref_count--; - } - }; - - collaborative_once_runner() {} - - ~collaborative_once_runner() { - spin_wait_until_eq(m_ref_count, 0, std::memory_order_acquire); - if (m_is_ready.load(std::memory_order_relaxed)) { - m_storage.~storage_t(); - } - } - - std::uintptr_t to_bits() { - return reinterpret_cast(this); - } - - static collaborative_once_runner* from_bits(std::uintptr_t bits) { - __TBB_ASSERT( (bits & collaborative_once_references_mask) == 0, "invalid pointer, last log2(max_nfs_size) bits must be zero" ); - return reinterpret_cast(bits); - } - - template - void run_once(F&& f) { - __TBB_ASSERT(!m_is_ready.load(std::memory_order_relaxed), "storage with task_arena and wait_context is already initialized"); - // Initialize internal state - new(&m_storage) storage_t(); - m_storage.m_arena.execute([&] { - isolated_execute([&] { - task_group_context context{ task_group_context::bound, - task_group_context::default_traits | task_group_context::concurrent_wait }; - - function_stack_task t{ std::forward(f), m_storage.m_wait_context }; - - // Set the ready flag after entering the execute body to prevent - // moonlighting threads from occupying all slots inside the arena. - m_is_ready.store(true, std::memory_order_release); - execute_and_wait(t, context, m_storage.m_wait_context, context); - }); - }); - } - - void assist() noexcept { - // Do not join the arena until the winner thread takes the slot - spin_wait_while_eq(m_is_ready, false); - m_storage.m_arena.execute([&] { - isolated_execute([&] { - // We do not want to get an exception from user functor on moonlighting threads. - // The exception is handled with the winner thread - task_group_context stub_context; - wait(m_storage.m_wait_context, stub_context); - }); - }); - } - -}; - -class collaborative_once_flag : no_copy { - enum state : std::uintptr_t { - uninitialized, - done, -#if TBB_USE_ASSERT - dead -#endif - }; - std::atomic m_state{ state::uninitialized }; - - template - friend void collaborative_call_once(collaborative_once_flag& flag, Fn&& f, Args&&... args); - - void set_completion_state(std::uintptr_t runner_bits, std::uintptr_t desired) { - std::uintptr_t expected = runner_bits; - do { - expected = runner_bits; - // Possible inefficiency: when we start waiting, - // some moonlighting threads might continue coming that will prolong our waiting. - // Fortunately, there are limited number of threads on the system so wait time is limited. - spin_wait_until_eq(m_state, expected); - } while (!m_state.compare_exchange_strong(expected, desired)); - } - - template - void do_collaborative_call_once(Fn&& f) { - std::uintptr_t expected = m_state.load(std::memory_order_acquire); - collaborative_once_runner runner; - - do { - if (expected == state::uninitialized && m_state.compare_exchange_strong(expected, runner.to_bits())) { - // Winner thread - runner.run_once([&] { - try_call([&] { - std::forward(f)(); - }).on_exception([&] { - // Reset the state to uninitialized to allow other threads to try initialization again - set_completion_state(runner.to_bits(), state::uninitialized); - }); - // We successfully executed functor - set_completion_state(runner.to_bits(), state::done); - }); - break; - } else { - // Moonlighting thread: we need to add a reference to the state to prolong runner lifetime. - // However, the maximum number of references are limited with runner alignment. - // So, we use CAS loop and spin_wait to guarantee that references never exceed "max_value". - do { - auto max_value = expected | collaborative_once_references_mask; - expected = spin_wait_while_eq(m_state, max_value); - // "expected > state::done" prevents storing values, when state is uninitialized or done - } while (expected > state::done && !m_state.compare_exchange_strong(expected, expected + 1)); - - if (auto shared_runner = collaborative_once_runner::from_bits(expected & ~collaborative_once_references_mask)) { - collaborative_once_runner::lifetime_guard guard{*shared_runner}; - m_state.fetch_sub(1); - - // The moonlighting threads are not expected to handle exceptions from user functor. - // Therefore, no exception is expected from assist(). - shared_runner->assist(); - } - } - __TBB_ASSERT(m_state.load(std::memory_order_relaxed) != state::dead, - "collaborative_once_flag has been prematurely destroyed"); - } while (expected != state::done); - } - -#if TBB_USE_ASSERT -public: - ~collaborative_once_flag() { - m_state.store(state::dead, std::memory_order_relaxed); - } -#endif -}; - - -template -void collaborative_call_once(collaborative_once_flag& flag, Fn&& fn, Args&&... args) { - __TBB_ASSERT(flag.m_state.load(std::memory_order_relaxed) != collaborative_once_flag::dead, - "collaborative_once_flag has been prematurely destroyed"); - if (flag.m_state.load(std::memory_order_acquire) != collaborative_once_flag::done) { - #if __TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN - // Using stored_pack to suppress bug in GCC 4.8 - // with parameter pack expansion in lambda - auto stored_pack = save_pack(std::forward(args)...); - auto func = [&] { call(std::forward(fn), std::move(stored_pack)); }; - #else - auto func = [&] { fn(std::forward(args)...); }; - #endif - flag.do_collaborative_call_once(func); - } -} - -#if _MSC_VER && !defined(__INTEL_COMPILER) - #pragma warning (pop) // 4324 warning -#endif - -} // namespace d1 -} // namespace detail - -using detail::d1::collaborative_call_once; -using detail::d1::collaborative_once_flag; -} // namespace tbb - -#endif // __TBB_collaborative_call_once_H diff --git a/include/oneapi/tbb/combinable.h b/include/oneapi/tbb/combinable.h deleted file mode 100644 index b676a30..0000000 --- a/include/oneapi/tbb/combinable.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_combinable_H -#define __TBB_combinable_H - -#include "detail/_namespace_injection.h" - -#include "enumerable_thread_specific.h" -#include "cache_aligned_allocator.h" - -namespace tbb { -namespace detail { -namespace d1 { -/** \name combinable **/ -//@{ -//! Thread-local storage with optional reduction -/** @ingroup containers */ -template -class combinable { - using my_alloc = typename tbb::cache_aligned_allocator; - using my_ets_type = typename tbb::enumerable_thread_specific; - my_ets_type my_ets; - -public: - combinable() = default; - - template - explicit combinable(Finit _finit) : my_ets(_finit) { } - - void clear() { my_ets.clear(); } - - T& local() { return my_ets.local(); } - - T& local(bool& exists) { return my_ets.local(exists); } - - // combine_func_t has signature T(T,T) or T(const T&, const T&) - template - T combine(CombineFunc f_combine) { return my_ets.combine(f_combine); } - - // combine_func_t has signature void(T) or void(const T&) - template - void combine_each(CombineFunc f_combine) { my_ets.combine_each(f_combine); } -}; - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::combinable; -} // inline namespace v1 - -} // namespace tbb - -#endif /* __TBB_combinable_H */ - diff --git a/include/oneapi/tbb/concurrent_hash_map.h b/include/oneapi/tbb/concurrent_hash_map.h deleted file mode 100644 index b300337..0000000 --- a/include/oneapi/tbb/concurrent_hash_map.h +++ /dev/null @@ -1,1664 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_concurrent_hash_map_H -#define __TBB_concurrent_hash_map_H - -#include "detail/_namespace_injection.h" -#include "detail/_utils.h" -#include "detail/_assert.h" -#include "detail/_allocator_traits.h" -#include "detail/_containers_helpers.h" -#include "detail/_template_helpers.h" -#include "detail/_hash_compare.h" -#include "detail/_range_common.h" -#include "tbb_allocator.h" -#include "spin_rw_mutex.h" - -#include -#include -#include -#include -#include // Need std::pair -#include // Need std::memset - -namespace tbb { -namespace detail { -namespace d2 { - -#if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS && __TBB_CPP20_CONCEPTS_PRESENT -template -concept ch_map_rw_scoped_lockable = rw_scoped_lockable && - requires(const typename Mutex::scoped_lock& sl) { - { sl.is_writer() } -> std::convertible_to; -}; -#endif - -template -struct hash_map_node_base : no_copy { - using mutex_type = MutexType; - // Scoped lock type for mutex - using scoped_type = typename MutexType::scoped_lock; - // Next node in chain - hash_map_node_base* next; - mutex_type mutex; -}; - -// Incompleteness flag value -static void* const rehash_req_flag = reinterpret_cast(std::size_t(3)); -// Rehashed empty bucket flag -static void* const empty_rehashed_flag = reinterpret_cast(std::size_t(0)); - -template -bool rehash_required( hash_map_node_base* node_ptr ) { - return reinterpret_cast(node_ptr) == rehash_req_flag; -} - -#if TBB_USE_ASSERT -template -bool empty_rehashed( hash_map_node_base* node_ptr ) { - return reinterpret_cast(node_ptr) == empty_rehashed_flag; -} -#endif - -// base class of concurrent_hash_map - -template -class hash_map_base { -public: - using size_type = std::size_t; - using hashcode_type = std::size_t; - using segment_index_type = std::size_t; - using node_base = hash_map_node_base; - - struct bucket : no_copy { - using mutex_type = MutexType; - using scoped_type = typename mutex_type::scoped_lock; - - bucket() : node_list(nullptr) {} - bucket( node_base* ptr ) : node_list(ptr) {} - - mutex_type mutex; - std::atomic node_list; - }; - - using allocator_type = Allocator; - using allocator_traits_type = tbb::detail::allocator_traits; - using bucket_allocator_type = typename allocator_traits_type::template rebind_alloc; - using bucket_allocator_traits = tbb::detail::allocator_traits; - - // Count of segments in the first block - static constexpr size_type embedded_block = 1; - // Count of segments in the first block - static constexpr size_type embedded_buckets = 1 << embedded_block; - // Count of segments in the first block - static constexpr size_type first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096 - // Size of a pointer / table size - static constexpr size_type pointers_per_table = sizeof(segment_index_type) * 8; // one segment per bit - - using segment_ptr_type = bucket*; - using atomic_segment_type = std::atomic; - using segments_table_type = atomic_segment_type[pointers_per_table]; - - hash_map_base( const allocator_type& alloc ) : my_allocator(alloc), my_mask(embedded_buckets - 1), my_size(0) { - for (size_type i = 0; i != embedded_buckets; ++i) { - my_embedded_segment[i].node_list.store(nullptr, std::memory_order_relaxed); - } - - for (size_type segment_index = 0; segment_index < pointers_per_table; ++segment_index) { - auto argument = segment_index < embedded_block ? my_embedded_segment + segment_base(segment_index) : nullptr; - my_table[segment_index].store(argument, std::memory_order_relaxed); - } - - __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks"); - } - - // segment index of given index in the array - static segment_index_type segment_index_of( size_type index ) { - return segment_index_type(tbb::detail::log2( index|1 )); - } - - // the first array index of given segment - static segment_index_type segment_base( segment_index_type k ) { - return (segment_index_type(1) << k & ~segment_index_type(1)); - } - - // segment size except for k == 0 - static size_type segment_size( segment_index_type k ) { - return size_type(1) << k; // fake value for k==0 - } - - // true if ptr is valid pointer - static bool is_valid( void* ptr ) { - return reinterpret_cast(ptr) > uintptr_t(63); - } - - template - void init_buckets_impl( segment_ptr_type ptr, size_type sz, const Args&... args ) { - for (size_type i = 0; i < sz; ++i) { - bucket_allocator_traits::construct(my_allocator, ptr + i, args...); - } - } - - // Initialize buckets - void init_buckets( segment_ptr_type ptr, size_type sz, bool is_initial ) { - if (is_initial) { - init_buckets_impl(ptr, sz); - } else { - init_buckets_impl(ptr, sz, reinterpret_cast(rehash_req_flag)); - } - } - - // Add node n to bucket b - static void add_to_bucket( bucket* b, node_base* n ) { - __TBB_ASSERT(!rehash_required(b->node_list.load(std::memory_order_relaxed)), nullptr); - n->next = b->node_list.load(std::memory_order_relaxed); - b->node_list.store(n, std::memory_order_relaxed); // its under lock and flag is set - } - - const bucket_allocator_type& get_allocator() const { - return my_allocator; - } - - bucket_allocator_type& get_allocator() { - return my_allocator; - } - - // Enable segment - void enable_segment( segment_index_type k, bool is_initial = false ) { - __TBB_ASSERT( k, "Zero segment must be embedded" ); - size_type sz; - __TBB_ASSERT( !is_valid(my_table[k].load(std::memory_order_relaxed)), "Wrong concurrent assignment"); - if (k >= first_block) { - sz = segment_size(k); - segment_ptr_type ptr = nullptr; - try_call( [&] { - ptr = bucket_allocator_traits::allocate(my_allocator, sz); - } ).on_exception( [&] { - my_table[k].store(nullptr, std::memory_order_relaxed); - }); - - __TBB_ASSERT(ptr, nullptr); - init_buckets(ptr, sz, is_initial); - my_table[k].store(ptr, std::memory_order_release); - sz <<= 1;// double it to get entire capacity of the container - } else { // the first block - __TBB_ASSERT( k == embedded_block, "Wrong segment index" ); - sz = segment_size(first_block); - segment_ptr_type ptr = nullptr; - try_call( [&] { - ptr = bucket_allocator_traits::allocate(my_allocator, sz - embedded_buckets); - } ).on_exception( [&] { - my_table[k].store(nullptr, std::memory_order_relaxed); - }); - - __TBB_ASSERT(ptr, nullptr); - init_buckets(ptr, sz - embedded_buckets, is_initial); - ptr -= segment_base(embedded_block); - for(segment_index_type i = embedded_block; i < first_block; i++) // calc the offsets - my_table[i].store(ptr + segment_base(i), std::memory_order_release); - } - my_mask.store(sz-1, std::memory_order_release); - } - - void delete_segment( segment_index_type s ) { - segment_ptr_type buckets_ptr = my_table[s].load(std::memory_order_relaxed); - size_type sz = segment_size( s ? s : 1 ); - - size_type deallocate_size = 0; - - if (s >= first_block) { // the first segment or the next - deallocate_size = sz; - } else if (s == embedded_block && embedded_block != first_block) { - deallocate_size = segment_size(first_block) - embedded_buckets; - } - - for (size_type i = 0; i < deallocate_size; ++i) { - bucket_allocator_traits::destroy(my_allocator, buckets_ptr + i); - } - if (deallocate_size != 0) { - bucket_allocator_traits::deallocate(my_allocator, buckets_ptr, deallocate_size); - } - - if (s >= embedded_block) my_table[s].store(nullptr, std::memory_order_relaxed); - } - - // Get bucket by (masked) hashcode - bucket *get_bucket( hashcode_type h ) const noexcept { - segment_index_type s = segment_index_of( h ); - h -= segment_base(s); - segment_ptr_type seg = my_table[s].load(std::memory_order_acquire); - __TBB_ASSERT( is_valid(seg), "hashcode must be cut by valid mask for allocated segments" ); - return &seg[h]; - } - - // detail serial rehashing helper - void mark_rehashed_levels( hashcode_type h ) noexcept { - segment_index_type s = segment_index_of( h ); - while (segment_ptr_type seg = my_table[++s].load(std::memory_order_relaxed)) - if (rehash_required(seg[h].node_list.load(std::memory_order_relaxed))) { - seg[h].node_list.store(reinterpret_cast(empty_rehashed_flag), std::memory_order_relaxed); - mark_rehashed_levels( h + ((hashcode_type)1<node_list.load(std::memory_order_acquire))) { - return true; - } - } - return false; - } - - // Insert a node and check for load factor. @return segment index to enable. - segment_index_type insert_new_node( bucket *b, node_base *n, hashcode_type mask ) { - size_type sz = ++my_size; // prefix form is to enforce allocation after the first item inserted - add_to_bucket( b, n ); - // check load factor - if( sz >= mask ) { // TODO: add custom load_factor - segment_index_type new_seg = tbb::detail::log2( mask+1 ); //optimized segment_index_of - __TBB_ASSERT( is_valid(my_table[new_seg-1].load(std::memory_order_relaxed)), "new allocations must not publish new mask until segment has allocated"); - static const segment_ptr_type is_allocating = segment_ptr_type(2); - segment_ptr_type disabled = nullptr; - if (!(my_table[new_seg].load(std::memory_order_acquire)) - && my_table[new_seg].compare_exchange_strong(disabled, is_allocating)) - return new_seg; // The value must be processed - } - return 0; - } - - // Prepare enough segments for number of buckets - void reserve(size_type buckets) { - if( !buckets-- ) return; - bool is_initial = !my_size.load(std::memory_order_relaxed); - for (size_type m = my_mask.load(std::memory_order_relaxed); buckets > m; - m = my_mask.load(std::memory_order_relaxed)) - { - enable_segment( segment_index_of( m+1 ), is_initial ); - } - } - - // Swap hash_map_bases - void internal_swap_content(hash_map_base &table) { - using std::swap; - swap_atomics_relaxed(my_mask, table.my_mask); - swap_atomics_relaxed(my_size, table.my_size); - - for(size_type i = 0; i < embedded_buckets; i++) { - auto temp = my_embedded_segment[i].node_list.load(std::memory_order_relaxed); - my_embedded_segment[i].node_list.store(table.my_embedded_segment[i].node_list.load(std::memory_order_relaxed), - std::memory_order_relaxed); - table.my_embedded_segment[i].node_list.store(temp, std::memory_order_relaxed); - } - for(size_type i = embedded_block; i < pointers_per_table; i++) { - auto temp = my_table[i].load(std::memory_order_relaxed); - my_table[i].store(table.my_table[i].load(std::memory_order_relaxed), - std::memory_order_relaxed); - table.my_table[i].store(temp, std::memory_order_relaxed); - } - } - - void internal_move(hash_map_base&& other) { - my_mask.store(other.my_mask.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.my_mask.store(embedded_buckets - 1, std::memory_order_relaxed); - - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.my_size.store(0, std::memory_order_relaxed); - - for (size_type i = 0; i < embedded_buckets; ++i) { - my_embedded_segment[i].node_list.store(other.my_embedded_segment[i].node_list, std::memory_order_relaxed); - other.my_embedded_segment[i].node_list.store(nullptr, std::memory_order_relaxed); - } - - for (size_type i = embedded_block; i < pointers_per_table; ++i) { - my_table[i].store(other.my_table[i].load(std::memory_order_relaxed), - std::memory_order_relaxed); - other.my_table[i].store(nullptr, std::memory_order_relaxed); - } - } - -protected: - bucket_allocator_type my_allocator; - // Hash mask = sum of allocated segment sizes - 1 - std::atomic my_mask; - // Size of container in stored items - std::atomic my_size; // It must be in separate cache line from my_mask due to performance effects - // Zero segment - bucket my_embedded_segment[embedded_buckets]; - // Segment pointers table. Also prevents false sharing between my_mask and my_size - segments_table_type my_table; -}; - -template -class hash_map_range; - -// Meets requirements of a forward iterator for STL -// Value is either the T or const T type of the container. -template -class hash_map_iterator { - using map_type = Container; - using node = typename Container::node; - using map_base = typename Container::base_type; - using node_base = typename map_base::node_base; - using bucket = typename map_base::bucket; -public: - using value_type = Value; - using size_type = typename Container::size_type; - using difference_type = typename Container::difference_type; - using pointer = value_type*; - using reference = value_type&; - using iterator_category = std::forward_iterator_tag; - - // Construct undefined iterator - hash_map_iterator(): my_map(), my_index(), my_bucket(), my_node() {} - hash_map_iterator( const hash_map_iterator& other ) : - my_map(other.my_map), - my_index(other.my_index), - my_bucket(other.my_bucket), - my_node(other.my_node) - {} - - hash_map_iterator& operator=( const hash_map_iterator& other ) { - my_map = other.my_map; - my_index = other.my_index; - my_bucket = other.my_bucket; - my_node = other.my_node; - return *this; - } - - Value& operator*() const { - __TBB_ASSERT( map_base::is_valid(my_node), "iterator uninitialized or at end of container?" ); - return my_node->value(); - } - - Value* operator->() const {return &operator*();} - - hash_map_iterator& operator++() { - my_node = static_cast( my_node->next ); - if( !my_node ) advance_to_next_bucket(); - return *this; - } - - // Post increment - hash_map_iterator operator++(int) { - hash_map_iterator old(*this); - operator++(); - return old; - } -private: - template - friend bool operator==( const hash_map_iterator& i, const hash_map_iterator& j ); - - template - friend bool operator!=( const hash_map_iterator& i, const hash_map_iterator& j ); - - template - friend ptrdiff_t operator-( const hash_map_iterator& i, const hash_map_iterator& j ); - - template - friend class hash_map_iterator; - - template - friend class hash_map_range; - - void advance_to_next_bucket() { // TODO?: refactor to iterator_base class - size_t k = my_index+1; - __TBB_ASSERT( my_bucket, "advancing an invalid iterator?"); - while (k <= my_map->my_mask.load(std::memory_order_relaxed)) { - // Following test uses 2's-complement wizardry - if( k&(k-2) ) // not the beginning of a segment - ++my_bucket; - else my_bucket = my_map->get_bucket( k ); - node_base *n = my_bucket->node_list.load(std::memory_order_relaxed); - if( map_base::is_valid(n) ) { - my_node = static_cast(n); - my_index = k; - return; - } - ++k; - } - my_bucket = nullptr; my_node = nullptr; my_index = k; // the end - } - - template - __TBB_requires(tbb::detail::hash_compare && - ch_map_rw_scoped_lockable) -#else - > - __TBB_requires(tbb::detail::hash_compare) -#endif - friend class concurrent_hash_map; - - hash_map_iterator( const Container &map, std::size_t index, const bucket *b, node_base *n ) : - my_map(&map), my_index(index), my_bucket(b), my_node(static_cast(n)) - { - if( b && !map_base::is_valid(n) ) - advance_to_next_bucket(); - } - - // concurrent_hash_map over which we are iterating. - const Container *my_map; - // Index in hash table for current item - size_t my_index; - // Pointer to bucket - const bucket* my_bucket; - // Pointer to node that has current item - node* my_node; -}; - -template -bool operator==( const hash_map_iterator& i, const hash_map_iterator& j ) { - return i.my_node == j.my_node && i.my_map == j.my_map; -} - -template -bool operator!=( const hash_map_iterator& i, const hash_map_iterator& j ) { - return i.my_node != j.my_node || i.my_map != j.my_map; -} - -// Range class used with concurrent_hash_map -template -class hash_map_range { - using map_type = typename Iterator::map_type; -public: - // Type for size of a range - using size_type = std::size_t; - using value_type = typename Iterator::value_type; - using reference = typename Iterator::reference; - using difference_type = typename Iterator::difference_type; - using iterator = Iterator; - - // True if range is empty. - bool empty() const { return my_begin == my_end; } - - // True if range can be partitioned into two subranges. - bool is_divisible() const { - return my_midpoint != my_end; - } - - // Split range. - hash_map_range( hash_map_range& r, split ) : - my_end(r.my_end), - my_grainsize(r.my_grainsize) - { - r.my_end = my_begin = r.my_midpoint; - __TBB_ASSERT( !empty(), "Splitting despite the range is not divisible" ); - __TBB_ASSERT( !r.empty(), "Splitting despite the range is not divisible" ); - set_midpoint(); - r.set_midpoint(); - } - - // Init range with container and grainsize specified - hash_map_range( const map_type &map, size_type grainsize_ = 1 ) : - my_begin( Iterator( map, 0, map.my_embedded_segment, map.my_embedded_segment->node_list.load(std::memory_order_relaxed) ) ), - my_end( Iterator( map, map.my_mask.load(std::memory_order_relaxed) + 1, nullptr, nullptr ) ), - my_grainsize( grainsize_ ) - { - __TBB_ASSERT( grainsize_>0, "grainsize must be positive" ); - set_midpoint(); - } - - Iterator begin() const { return my_begin; } - Iterator end() const { return my_end; } - // The grain size for this range. - size_type grainsize() const { return my_grainsize; } - -private: - Iterator my_begin; - Iterator my_end; - mutable Iterator my_midpoint; - size_t my_grainsize; - // Set my_midpoint to point approximately half way between my_begin and my_end. - void set_midpoint() const; - template friend class hash_map_range; -}; - -template -void hash_map_range::set_midpoint() const { - // Split by groups of nodes - size_t m = my_end.my_index-my_begin.my_index; - if( m > my_grainsize ) { - m = my_begin.my_index + m/2u; - auto b = my_begin.my_map->get_bucket(m); - my_midpoint = Iterator(*my_begin.my_map,m,b,b->node_list.load(std::memory_order_relaxed)); - } else { - my_midpoint = my_end; - } - __TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index, - "my_begin is after my_midpoint" ); - __TBB_ASSERT( my_midpoint.my_index <= my_end.my_index, - "my_midpoint is after my_end" ); - __TBB_ASSERT( my_begin != my_midpoint || my_begin == my_end, - "[my_begin, my_midpoint) range should not be empty" ); -} - -template , - typename Allocator = tbb_allocator> -#if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS - , typename MutexType = spin_rw_mutex - > - __TBB_requires(tbb::detail::hash_compare && - ch_map_rw_scoped_lockable) -#else - > - __TBB_requires(tbb::detail::hash_compare) -#endif -class concurrent_hash_map -#if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS - : protected hash_map_base -#else - : protected hash_map_base -#endif -{ - template - friend class hash_map_iterator; - - template - friend class hash_map_range; - using allocator_traits_type = tbb::detail::allocator_traits; - -#if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS - using base_type = hash_map_base; -#else - using base_type = hash_map_base; -#endif -public: - using key_type = Key; - using mapped_type = T; - // type_identity is needed to disable implicit deduction guides for std::initializer_list constructors - // and copy/move constructor with explicit allocator argument - using allocator_type = tbb::detail::type_identity_t; - using hash_compare_type = tbb::detail::type_identity_t; - using value_type = std::pair; - using size_type = typename base_type::size_type; - using difference_type = std::ptrdiff_t; -#if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS - using mutex_type = MutexType; -#endif - using pointer = typename allocator_traits_type::pointer; - using const_pointer = typename allocator_traits_type::const_pointer; - - using reference = value_type&; - using const_reference = const value_type&; - using iterator = hash_map_iterator; - using const_iterator = hash_map_iterator; - using range_type = hash_map_range; - using const_range_type = hash_map_range; - -protected: - static_assert(std::is_same::value, - "value_type of the container must be the same as its allocator's"); - - friend class const_accessor; - class node; - using segment_index_type = typename base_type::segment_index_type; - using segment_ptr_type = typename base_type::segment_ptr_type; - using node_base = typename base_type::node_base; - using bucket = typename base_type::bucket; - using hashcode_type = typename base_type::hashcode_type; - using bucket_allocator_type = typename base_type::bucket_allocator_type; - using node_allocator_type = typename base_type::allocator_traits_type::template rebind_alloc; - using node_allocator_traits = tbb::detail::allocator_traits; - hash_compare_type my_hash_compare; - - class node : public node_base { - public: - node() {} - ~node() {} - pointer storage() { return &my_value; } - value_type& value() { return *storage(); } - private: - union { - value_type my_value; - }; - }; - - void delete_node( node_base *n ) { - node_allocator_type node_allocator(this->get_allocator()); - node_allocator_traits::destroy(node_allocator, static_cast(n)->storage()); - node_allocator_traits::destroy(node_allocator, static_cast(n)); - node_allocator_traits::deallocate(node_allocator, static_cast(n), 1); - } - - template - static node* create_node(bucket_allocator_type& allocator, Args&&... args) { - node_allocator_type node_allocator(allocator); - node* node_ptr = node_allocator_traits::allocate(node_allocator, 1); - auto guard = make_raii_guard([&] { - node_allocator_traits::destroy(node_allocator, node_ptr); - node_allocator_traits::deallocate(node_allocator, node_ptr, 1); - }); - - node_allocator_traits::construct(node_allocator, node_ptr); - node_allocator_traits::construct(node_allocator, node_ptr->storage(), std::forward(args)...); - guard.dismiss(); - return node_ptr; - } - - static node* allocate_node_copy_construct(bucket_allocator_type& allocator, const Key &key, const T * t){ - return create_node(allocator, key, *t); - } - - static node* allocate_node_move_construct(bucket_allocator_type& allocator, const Key &key, const T * t){ - return create_node(allocator, key, std::move(*const_cast(t))); - } - - template - static node* allocate_node_default_construct(bucket_allocator_type& allocator, const K &key, const T * ){ - // Emplace construct an empty T object inside the pair - return create_node(allocator, std::piecewise_construct, - std::forward_as_tuple(key), std::forward_as_tuple()); - } - - static node* do_not_allocate_node(bucket_allocator_type& , const Key &, const T * ){ - __TBB_ASSERT(false,"this dummy function should not be called"); - return nullptr; - } - - template - node *search_bucket( const K &key, bucket *b ) const { - node *n = static_cast( b->node_list.load(std::memory_order_relaxed) ); - while (this->is_valid(n) && !my_hash_compare.equal(key, n->value().first)) - n = static_cast( n->next ); - __TBB_ASSERT(!rehash_required(n), "Search can be executed only for rehashed bucket"); - return n; - } - - // bucket accessor is to find, rehash, acquire a lock, and access a bucket - class bucket_accessor : public bucket::scoped_type { - bucket *my_b; - public: - bucket_accessor( concurrent_hash_map *base, const hashcode_type h, bool writer = false ) { acquire( base, h, writer ); } - // find a bucket by masked hashcode, optionally rehash, and acquire the lock - inline void acquire( concurrent_hash_map *base, const hashcode_type h, bool writer = false ) { - my_b = base->get_bucket( h ); - // TODO: actually, notification is unnecessary here, just hiding double-check - if (rehash_required(my_b->node_list.load(std::memory_order_acquire)) - && bucket::scoped_type::try_acquire( my_b->mutex, /*write=*/true ) ) - { - if (rehash_required(my_b->node_list.load(std::memory_order_relaxed))) base->rehash_bucket(my_b, h); // recursive rehashing - } - else bucket::scoped_type::acquire( my_b->mutex, writer ); - __TBB_ASSERT(!rehash_required(my_b->node_list.load(std::memory_order_relaxed)), nullptr); - } - - // get bucket pointer - bucket *operator() () { return my_b; } - }; - - // TODO refactor to hash_base - void rehash_bucket( bucket *b_new, const hashcode_type hash ) { - __TBB_ASSERT( hash > 1, "The lowermost buckets can't be rehashed" ); - b_new->node_list.store(reinterpret_cast(empty_rehashed_flag), std::memory_order_release); // mark rehashed - hashcode_type mask = (hashcode_type(1) << tbb::detail::log2(hash)) - 1; // get parent mask from the topmost bit - bucket_accessor b_old( this, hash & mask ); - - mask = (mask<<1) | 1; // get full mask for new bucket - __TBB_ASSERT( (mask&(mask+1))==0 && (hash & mask) == hash, nullptr ); - restart: - node_base* prev = nullptr; - node_base* curr = b_old()->node_list.load(std::memory_order_acquire); - while (this->is_valid(curr)) { - hashcode_type curr_node_hash = my_hash_compare.hash(static_cast(curr)->value().first); - - if ((curr_node_hash & mask) == hash) { - if (!b_old.is_writer()) { - if (!b_old.upgrade_to_writer()) { - goto restart; // node ptr can be invalid due to concurrent erase - } - } - node_base* next = curr->next; - // exclude from b_old - if (prev == nullptr) { - b_old()->node_list.store(curr->next, std::memory_order_relaxed); - } else { - prev->next = curr->next; - } - this->add_to_bucket(b_new, curr); - curr = next; - } else { - prev = curr; - curr = curr->next; - } - } - } - - template - using hash_compare_is_transparent = dependent_bool, U>; - -public: - - class accessor; - // Combines data access, locking, and garbage collection. - class const_accessor : private node::scoped_type /*which derived from no_copy*/ { -#if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS - friend class concurrent_hash_map; -#else - friend class concurrent_hash_map; -#endif - friend class accessor; - public: - // Type of value - using value_type = const typename concurrent_hash_map::value_type; - - // True if result is empty. - bool empty() const { return !my_node; } - - // Set to null - void release() { - if( my_node ) { - node::scoped_type::release(); - my_node = nullptr; - } - } - - // Return reference to associated value in hash table. - const_reference operator*() const { - __TBB_ASSERT( my_node, "attempt to dereference empty accessor" ); - return my_node->value(); - } - - // Return pointer to associated value in hash table. - const_pointer operator->() const { - return &operator*(); - } - - // Create empty result - const_accessor() : my_node(nullptr), my_hash() {} - - // Destroy result after releasing the underlying reference. - ~const_accessor() { - my_node = nullptr; // scoped lock's release() is called in its destructor - } - protected: - bool is_writer() { return node::scoped_type::is_writer(); } - node *my_node; - hashcode_type my_hash; - }; - - // Allows write access to elements and combines data access, locking, and garbage collection. - class accessor: public const_accessor { - public: - // Type of value - using value_type = typename concurrent_hash_map::value_type; - - // Return reference to associated value in hash table. - reference operator*() const { - __TBB_ASSERT( this->my_node, "attempt to dereference empty accessor" ); - return this->my_node->value(); - } - - // Return pointer to associated value in hash table. - pointer operator->() const { - return &operator*(); - } - }; - - explicit concurrent_hash_map( const hash_compare_type& compare, const allocator_type& a = allocator_type() ) - : base_type(a) - , my_hash_compare(compare) - {} - - concurrent_hash_map() : concurrent_hash_map(hash_compare_type()) {} - - explicit concurrent_hash_map( const allocator_type& a ) - : concurrent_hash_map(hash_compare_type(), a) - {} - - // Construct empty table with n preallocated buckets. This number serves also as initial concurrency level. - concurrent_hash_map( size_type n, const allocator_type &a = allocator_type() ) - : concurrent_hash_map(a) - { - this->reserve(n); - } - - concurrent_hash_map( size_type n, const hash_compare_type& compare, const allocator_type& a = allocator_type() ) - : concurrent_hash_map(compare, a) - { - this->reserve(n); - } - - // Copy constructor - concurrent_hash_map( const concurrent_hash_map &table ) - : concurrent_hash_map(node_allocator_traits::select_on_container_copy_construction(table.get_allocator())) - { - try_call( [&] { - internal_copy(table); - }).on_exception( [&] { - this->clear(); - }); - } - - concurrent_hash_map( const concurrent_hash_map &table, const allocator_type &a) - : concurrent_hash_map(a) - { - try_call( [&] { - internal_copy(table); - }).on_exception( [&] { - this->clear(); - }); - } - - // Move constructor - concurrent_hash_map( concurrent_hash_map &&table ) - : concurrent_hash_map(std::move(table.get_allocator())) - { - this->internal_move(std::move(table)); - } - - // Move constructor - concurrent_hash_map( concurrent_hash_map &&table, const allocator_type &a ) - : concurrent_hash_map(a) - { - using is_equal_type = typename node_allocator_traits::is_always_equal; - internal_move_construct_with_allocator(std::move(table), a, is_equal_type()); - } - - // Construction with copying iteration range and given allocator instance - template - concurrent_hash_map( I first, I last, const allocator_type &a = allocator_type() ) - : concurrent_hash_map(a) - { - try_call( [&] { - internal_copy(first, last, std::distance(first, last)); - }).on_exception( [&] { - this->clear(); - }); - } - - template - concurrent_hash_map( I first, I last, const hash_compare_type& compare, const allocator_type& a = allocator_type() ) - : concurrent_hash_map(compare, a) - { - try_call( [&] { - internal_copy(first, last, std::distance(first, last)); - }).on_exception( [&] { - this->clear(); - }); - } - - concurrent_hash_map( std::initializer_list il, const hash_compare_type& compare = hash_compare_type(), const allocator_type& a = allocator_type() ) - : concurrent_hash_map(compare, a) - { - try_call( [&] { - internal_copy(il.begin(), il.end(), il.size()); - }).on_exception( [&] { - this->clear(); - }); - } - - concurrent_hash_map( std::initializer_list il, const allocator_type& a ) - : concurrent_hash_map(il, hash_compare_type(), a) {} - - // Assignment - concurrent_hash_map& operator=( const concurrent_hash_map &table ) { - if( this != &table ) { - clear(); - copy_assign_allocators(this->my_allocator, table.my_allocator); - internal_copy(table); - } - return *this; - } - - // Move Assignment - concurrent_hash_map& operator=( concurrent_hash_map &&table ) { - if( this != &table ) { - using pocma_type = typename node_allocator_traits::propagate_on_container_move_assignment; - using is_equal_type = typename node_allocator_traits::is_always_equal; - move_assign_allocators(this->my_allocator, table.my_allocator); - internal_move_assign(std::move(table), tbb::detail::disjunction()); - } - return *this; - } - - // Assignment - concurrent_hash_map& operator=( std::initializer_list il ) { - clear(); - internal_copy(il.begin(), il.end(), il.size()); - return *this; - } - - // Rehashes and optionally resizes the whole table. - /** Useful to optimize performance before or after concurrent operations. - Also enables using of find() and count() concurrent methods in serial context. */ - void rehash(size_type sz = 0) { - this->reserve(sz); // TODO: add reduction of number of buckets as well - hashcode_type mask = this->my_mask.load(std::memory_order_relaxed); - hashcode_type b = (mask+1)>>1; // size or first index of the last segment - __TBB_ASSERT((b&(b-1))==0, nullptr); // zero or power of 2 - bucket *bp = this->get_bucket( b ); // only the last segment should be scanned for rehashing - for(; b <= mask; b++, bp++ ) { - node_base *n = bp->node_list.load(std::memory_order_relaxed); - __TBB_ASSERT( this->is_valid(n) || empty_rehashed(n) || rehash_required(n), "Broken internal structure" ); - __TBB_ASSERT( *reinterpret_cast(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" ); - if (rehash_required(n)) { // rehash bucket, conditional because rehashing of a previous bucket may affect this one - hashcode_type h = b; bucket *b_old = bp; - do { - __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" ); - hashcode_type m = ( hashcode_type(1) << tbb::detail::log2( h ) ) - 1; // get parent mask from the topmost bit - b_old = this->get_bucket( h &= m ); - } while( rehash_required(b_old->node_list.load(std::memory_order_relaxed)) ); - // now h - is index of the root rehashed bucket b_old - this->mark_rehashed_levels( h ); // mark all non-rehashed children recursively across all segments - node_base* prev = nullptr; - node_base* curr = b_old->node_list.load(std::memory_order_relaxed); - while (this->is_valid(curr)) { - hashcode_type curr_node_hash = my_hash_compare.hash(static_cast(curr)->value().first); - - if ((curr_node_hash & mask) != h) { // should be rehashed - node_base* next = curr->next; - // exclude from b_old - if (prev == nullptr) { - b_old->node_list.store(curr->next, std::memory_order_relaxed); - } else { - prev->next = curr->next; - } - bucket *b_new = this->get_bucket(curr_node_hash & mask); - __TBB_ASSERT(!rehash_required(b_new->node_list.load(std::memory_order_relaxed)), "hash() function changed for key in table or internal error"); - this->add_to_bucket(b_new, curr); - curr = next; - } else { - prev = curr; - curr = curr->next; - } - } - } - } - } - - // Clear table - void clear() { - hashcode_type m = this->my_mask.load(std::memory_order_relaxed); - __TBB_ASSERT((m&(m+1))==0, "data structure is invalid"); - this->my_size.store(0, std::memory_order_relaxed); - segment_index_type s = this->segment_index_of( m ); - __TBB_ASSERT( s+1 == this->pointers_per_table || !this->my_table[s+1].load(std::memory_order_relaxed), "wrong mask or concurrent grow" ); - do { - __TBB_ASSERT(this->is_valid(this->my_table[s].load(std::memory_order_relaxed)), "wrong mask or concurrent grow" ); - segment_ptr_type buckets_ptr = this->my_table[s].load(std::memory_order_relaxed); - size_type sz = this->segment_size( s ? s : 1 ); - for( segment_index_type i = 0; i < sz; i++ ) - for( node_base *n = buckets_ptr[i].node_list.load(std::memory_order_relaxed); - this->is_valid(n); n = buckets_ptr[i].node_list.load(std::memory_order_relaxed) ) - { - buckets_ptr[i].node_list.store(n->next, std::memory_order_relaxed); - delete_node( n ); - } - this->delete_segment(s); - } while(s-- > 0); - this->my_mask.store(this->embedded_buckets - 1, std::memory_order_relaxed); - } - - // Clear table and destroy it. - ~concurrent_hash_map() { clear(); } - - //------------------------------------------------------------------------ - // Parallel algorithm support - //------------------------------------------------------------------------ - range_type range( size_type grainsize=1 ) { - return range_type( *this, grainsize ); - } - const_range_type range( size_type grainsize=1 ) const { - return const_range_type( *this, grainsize ); - } - - //------------------------------------------------------------------------ - // STL support - not thread-safe methods - //------------------------------------------------------------------------ - iterator begin() { return iterator( *this, 0, this->my_embedded_segment, this->my_embedded_segment->node_list.load(std::memory_order_relaxed) ); } - const_iterator begin() const { return const_iterator( *this, 0, this->my_embedded_segment, this->my_embedded_segment->node_list.load(std::memory_order_relaxed) ); } - const_iterator cbegin() const { return const_iterator( *this, 0, this->my_embedded_segment, this->my_embedded_segment->node_list.load(std::memory_order_relaxed) ); } - iterator end() { return iterator( *this, 0, nullptr, nullptr ); } - const_iterator end() const { return const_iterator( *this, 0, nullptr, nullptr ); } - const_iterator cend() const { return const_iterator( *this, 0, nullptr, nullptr ); } - std::pair equal_range( const Key& key ) { return internal_equal_range( key, end() ); } - std::pair equal_range( const Key& key ) const { return internal_equal_range( key, end() ); } - - template - typename std::enable_if::value, - std::pair>::type equal_range( const K& key ) { - return internal_equal_range(key, end()); - } - - template - typename std::enable_if::value, - std::pair>::type equal_range( const K& key ) const { - return internal_equal_range(key, end()); - } - - // Number of items in table. - size_type size() const { return this->my_size.load(std::memory_order_acquire); } - - // True if size()==0. - __TBB_nodiscard bool empty() const { return size() == 0; } - - // Upper bound on size. - size_type max_size() const { - return allocator_traits_type::max_size(base_type::get_allocator()); - } - - // Returns the current number of buckets - size_type bucket_count() const { return this->my_mask.load(std::memory_order_relaxed) + 1; } - - // return allocator object - allocator_type get_allocator() const { return base_type::get_allocator(); } - - // swap two instances. Iterators are invalidated - void swap(concurrent_hash_map& table) { - using pocs_type = typename node_allocator_traits::propagate_on_container_swap; - using is_equal_type = typename node_allocator_traits::is_always_equal; - swap_allocators(this->my_allocator, table.my_allocator); - internal_swap(table, tbb::detail::disjunction()); - } - - //------------------------------------------------------------------------ - // concurrent map operations - //------------------------------------------------------------------------ - - // Return count of items (0 or 1) - size_type count( const Key &key ) const { - return const_cast(this)->lookup(key, nullptr, nullptr, /*write=*/false, &do_not_allocate_node); - } - - template - typename std::enable_if::value, - size_type>::type count( const K& key ) const { - return const_cast(this)->lookup(key, nullptr, nullptr, /*write=*/false, &do_not_allocate_node); - } - - // Find item and acquire a read lock on the item. - /** Return true if item is found, false otherwise. */ - bool find( const_accessor &result, const Key &key ) const { - result.release(); - return const_cast(this)->lookup(key, nullptr, &result, /*write=*/false, &do_not_allocate_node ); - } - - // Find item and acquire a write lock on the item. - /** Return true if item is found, false otherwise. */ - bool find( accessor &result, const Key &key ) { - result.release(); - return lookup(key, nullptr, &result, /*write=*/true, &do_not_allocate_node); - } - - template - typename std::enable_if::value, - bool>::type find( const_accessor& result, const K& key ) { - result.release(); - return lookup(key, nullptr, &result, /*write=*/false, &do_not_allocate_node); - } - - template - typename std::enable_if::value, - bool>::type find( accessor& result, const K& key ) { - result.release(); - return lookup(key, nullptr, &result, /*write=*/true, &do_not_allocate_node); - } - - // Insert item (if not already present) and acquire a read lock on the item. - /** Returns true if item is new. */ - bool insert( const_accessor &result, const Key &key ) { - result.release(); - return lookup(key, nullptr, &result, /*write=*/false, &allocate_node_default_construct<>); - } - - // Insert item (if not already present) and acquire a write lock on the item. - /** Returns true if item is new. */ - bool insert( accessor &result, const Key &key ) { - result.release(); - return lookup(key, nullptr, &result, /*write=*/true, &allocate_node_default_construct<>); - } - - template - typename std::enable_if::value && - std::is_constructible::value, - bool>::type insert( const_accessor& result, const K& key ) { - result.release(); - return lookup(key, nullptr, &result, /*write=*/false, &allocate_node_default_construct); - } - - template - typename std::enable_if::value && - std::is_constructible::value, - bool>::type insert( accessor& result, const K& key ) { - result.release(); - return lookup(key, nullptr, &result, /*write=*/true, &allocate_node_default_construct); - } - - // Insert item by copying if there is no such key present already and acquire a read lock on the item. - /** Returns true if item is new. */ - bool insert( const_accessor &result, const value_type &value ) { - result.release(); - return lookup(value.first, &value.second, &result, /*write=*/false, &allocate_node_copy_construct); - } - - // Insert item by copying if there is no such key present already and acquire a write lock on the item. - /** Returns true if item is new. */ - bool insert( accessor &result, const value_type &value ) { - result.release(); - return lookup(value.first, &value.second, &result, /*write=*/true, &allocate_node_copy_construct); - } - - // Insert item by copying if there is no such key present already - /** Returns true if item is inserted. */ - bool insert( const value_type &value ) { - return lookup(value.first, &value.second, nullptr, /*write=*/false, &allocate_node_copy_construct); - } - - // Insert item by copying if there is no such key present already and acquire a read lock on the item. - /** Returns true if item is new. */ - bool insert( const_accessor &result, value_type && value ) { - return generic_move_insert(result, std::move(value)); - } - - // Insert item by copying if there is no such key present already and acquire a write lock on the item. - /** Returns true if item is new. */ - bool insert( accessor &result, value_type && value ) { - return generic_move_insert(result, std::move(value)); - } - - // Insert item by copying if there is no such key present already - /** Returns true if item is inserted. */ - bool insert( value_type && value ) { - return generic_move_insert(accessor_not_used(), std::move(value)); - } - - // Insert item by copying if there is no such key present already and acquire a read lock on the item. - /** Returns true if item is new. */ - template - bool emplace( const_accessor &result, Args&&... args ) { - return generic_emplace(result, std::forward(args)...); - } - - // Insert item by copying if there is no such key present already and acquire a write lock on the item. - /** Returns true if item is new. */ - template - bool emplace( accessor &result, Args&&... args ) { - return generic_emplace(result, std::forward(args)...); - } - - // Insert item by copying if there is no such key present already - /** Returns true if item is inserted. */ - template - bool emplace( Args&&... args ) { - return generic_emplace(accessor_not_used(), std::forward(args)...); - } - - // Insert range [first, last) - template - void insert( I first, I last ) { - for ( ; first != last; ++first ) - insert( *first ); - } - - // Insert initializer list - void insert( std::initializer_list il ) { - insert( il.begin(), il.end() ); - } - - // Erase item. - /** Return true if item was erased by particularly this call. */ - bool erase( const Key &key ) { - return internal_erase(key); - } - - template - typename std::enable_if::value, - bool>::type erase( const K& key ) { - return internal_erase(key); - } - - // Erase item by const_accessor. - /** Return true if item was erased by particularly this call. */ - bool erase( const_accessor& item_accessor ) { - return exclude( item_accessor ); - } - - // Erase item by accessor. - /** Return true if item was erased by particularly this call. */ - bool erase( accessor& item_accessor ) { - return exclude( item_accessor ); - } - -protected: - template - node* allocate_node_helper( const K& key, const T* t, AllocateNodeType allocate_node, std::true_type ) { - return allocate_node(base_type::get_allocator(), key, t); - } - - template - node* allocate_node_helper( const K&, const T*, AllocateNodeType, std::false_type ) { - __TBB_ASSERT(false, "allocate_node_helper with std::false_type should never been called"); - return nullptr; - } - - // Insert or find item and optionally acquire a lock on the item. - template - bool lookup( const K &key, const T *t, const_accessor *result, bool write, AllocateNodeType allocate_node, node *tmp_n = nullptr) - { - __TBB_ASSERT( !result || !result->my_node, nullptr ); - bool return_value; - hashcode_type const h = my_hash_compare.hash( key ); - hashcode_type m = this->my_mask.load(std::memory_order_acquire); - segment_index_type grow_segment = 0; - node *n; - restart: - {//lock scope - __TBB_ASSERT((m&(m+1))==0, "data structure is invalid"); - return_value = false; - // get bucket - bucket_accessor b( this, h & m ); - // find a node - n = search_bucket( key, b() ); - if( OpInsert ) { - // [opt] insert a key - if( !n ) { - if( !tmp_n ) { - tmp_n = allocate_node_helper(key, t, allocate_node, std::integral_constant{}); - } - while ( !b.is_writer() && !b.upgrade_to_writer() ) { // TODO: improved insertion - // Rerun search list, in case another thread inserted the intem during the upgrade - n = search_bucket(key, b()); - if (this->is_valid(n)) { // unfortunately, it did - if (!b.downgrade_to_reader()) { - // If the lock was downgraded with reacquiring the mutex - // Rerun search list in case another thread removed the item during the downgrade - n = search_bucket(key, b()); - if (!this->is_valid(n)) { - // Unfortunately, it did - // We need to try upgrading to writer again - continue; - } - } - goto exists; - } - } - - if( this->check_mask_race(h, m) ) - goto restart; // b.release() is done in ~b(). - // insert and set flag to grow the container - grow_segment = this->insert_new_node( b(), n = tmp_n, m ); - tmp_n = nullptr; - return_value = true; - } - } else { // find or count - if( !n ) { - if( this->check_mask_race( h, m ) ) - goto restart; // b.release() is done in ~b(). TODO: replace by continue - return false; - } - return_value = true; - } - exists: - if( !result ) goto check_growth; - // TODO: the following seems as generic/regular operation - // acquire the item - if( !result->try_acquire( n->mutex, write ) ) { - for( tbb::detail::atomic_backoff backoff(true);; ) { - if( result->try_acquire( n->mutex, write ) ) break; - if( !backoff.bounded_pause() ) { - // the wait takes really long, restart the operation - b.release(); - __TBB_ASSERT( !OpInsert || !return_value, "Can't acquire new item in locked bucket?" ); - yield(); - m = this->my_mask.load(std::memory_order_acquire); - goto restart; - } - } - } - }//lock scope - result->my_node = n; - result->my_hash = h; - check_growth: - // [opt] grow the container - if( grow_segment ) { - this->enable_segment( grow_segment ); - } - if( tmp_n ) // if OpInsert only - delete_node( tmp_n ); - return return_value; - } - - struct accessor_not_used { void release(){}}; - friend const_accessor* accessor_location( accessor_not_used const& ){ return nullptr;} - friend const_accessor* accessor_location( const_accessor & a ) { return &a;} - - friend bool is_write_access_needed( accessor const& ) { return true;} - friend bool is_write_access_needed( const_accessor const& ) { return false;} - friend bool is_write_access_needed( accessor_not_used const& ) { return false;} - - template - bool generic_move_insert( Accessor && result, value_type && value ) { - result.release(); - return lookup(value.first, &value.second, accessor_location(result), is_write_access_needed(result), &allocate_node_move_construct); - } - - template - bool generic_emplace( Accessor && result, Args &&... args ) { - result.release(); - node * node_ptr = create_node(base_type::get_allocator(), std::forward(args)...); - return lookup(node_ptr->value().first, nullptr, accessor_location(result), is_write_access_needed(result), &do_not_allocate_node, node_ptr); - } - - // delete item by accessor - bool exclude( const_accessor &item_accessor ) { - __TBB_ASSERT( item_accessor.my_node, nullptr ); - node_base *const exclude_node = item_accessor.my_node; - hashcode_type const hash = item_accessor.my_hash; - hashcode_type mask = this->my_mask.load(std::memory_order_acquire); - do { - // get bucket - bucket_accessor b( this, hash & mask, /*writer=*/true ); - node_base* prev = nullptr; - node_base* curr = b()->node_list.load(std::memory_order_relaxed); - - while (curr && curr != exclude_node) { - prev = curr; - curr = curr->next; - } - - if (curr == nullptr) { // someone else was first - if (this->check_mask_race(hash, mask)) - continue; - item_accessor.release(); - return false; - } - __TBB_ASSERT( curr == exclude_node, nullptr ); - // remove from container - if (prev == nullptr) { - b()->node_list.store(curr->next, std::memory_order_relaxed); - } else { - prev->next = curr->next; - } - - this->my_size--; - break; - } while(true); - if (!item_accessor.is_writer()) { // need to get exclusive lock - item_accessor.upgrade_to_writer(); // return value means nothing here - } - - item_accessor.release(); - delete_node(exclude_node); // Only one thread can delete it - return true; - } - - template - bool internal_erase( const K& key ) { - node_base *erase_node; - hashcode_type const hash = my_hash_compare.hash(key); - hashcode_type mask = this->my_mask.load(std::memory_order_acquire); - restart: - {//lock scope - // get bucket - bucket_accessor b( this, hash & mask ); - search: - node_base* prev = nullptr; - erase_node = b()->node_list.load(std::memory_order_relaxed); - while (this->is_valid(erase_node) && !my_hash_compare.equal(key, static_cast(erase_node)->value().first ) ) { - prev = erase_node; - erase_node = erase_node->next; - } - - if (erase_node == nullptr) { // not found, but mask could be changed - if (this->check_mask_race(hash, mask)) - goto restart; - return false; - } else if (!b.is_writer() && !b.upgrade_to_writer()) { - if (this->check_mask_race(hash, mask)) // contended upgrade, check mask - goto restart; - goto search; - } - - // remove from container - if (prev == nullptr) { - b()->node_list.store(erase_node->next, std::memory_order_relaxed); - } else { - prev->next = erase_node->next; - } - this->my_size--; - } - { - typename node::scoped_type item_locker( erase_node->mutex, /*write=*/true ); - } - // note: there should be no threads pretending to acquire this mutex again, do not try to upgrade const_accessor! - delete_node(erase_node); // Only one thread can delete it due to write lock on the bucket - return true; - } - - // Returns an iterator for an item defined by the key, or for the next item after it (if upper==true) - template - std::pair internal_equal_range( const K& key, I end_ ) const { - hashcode_type h = my_hash_compare.hash( key ); - hashcode_type m = this->my_mask.load(std::memory_order_relaxed); - __TBB_ASSERT((m&(m+1))==0, "data structure is invalid"); - h &= m; - bucket *b = this->get_bucket( h ); - while (rehash_required(b->node_list.load(std::memory_order_relaxed))) { - m = ( hashcode_type(1) << tbb::detail::log2( h ) ) - 1; // get parent mask from the topmost bit - b = this->get_bucket( h &= m ); - } - node *n = search_bucket( key, b ); - if( !n ) - return std::make_pair(end_, end_); - iterator lower(*this, h, b, n), upper(lower); - return std::make_pair(lower, ++upper); - } - - // Copy "source" to *this, where *this must start out empty. - void internal_copy( const concurrent_hash_map& source ) { - hashcode_type mask = source.my_mask.load(std::memory_order_relaxed); - if( this->my_mask.load(std::memory_order_relaxed) == mask ) { // optimized version - this->reserve(source.my_size.load(std::memory_order_relaxed)); // TODO: load_factor? - bucket *dst = nullptr, *src = nullptr; - bool rehashing_required = false; - for( hashcode_type k = 0; k <= mask; k++ ) { - if( k & (k-2) ) ++dst,src++; // not the beginning of a segment - else { dst = this->get_bucket( k ); src = source.get_bucket( k ); } - __TBB_ASSERT(!rehash_required(dst->node_list.load(std::memory_order_relaxed)), "Invalid bucket in destination table"); - node *n = static_cast( src->node_list.load(std::memory_order_relaxed) ); - if (rehash_required(n)) { // source is not rehashed, items are in previous buckets - rehashing_required = true; - dst->node_list.store(reinterpret_cast(rehash_req_flag), std::memory_order_relaxed); - } else for(; n; n = static_cast( n->next ) ) { - node* node_ptr = create_node(base_type::get_allocator(), n->value().first, n->value().second); - this->add_to_bucket( dst, node_ptr); - this->my_size.fetch_add(1, std::memory_order_relaxed); - } - } - if( rehashing_required ) rehash(); - } else internal_copy(source.begin(), source.end(), source.my_size.load(std::memory_order_relaxed)); - } - - template - void internal_copy( I first, I last, size_type reserve_size ) { - this->reserve(reserve_size); // TODO: load_factor? - hashcode_type m = this->my_mask.load(std::memory_order_relaxed); - for(; first != last; ++first) { - hashcode_type h = my_hash_compare.hash( (*first).first ); - bucket *b = this->get_bucket( h & m ); - __TBB_ASSERT(!rehash_required(b->node_list.load(std::memory_order_relaxed)), "Invalid bucket in destination table"); - node* node_ptr = create_node(base_type::get_allocator(), (*first).first, (*first).second); - this->add_to_bucket( b, node_ptr ); - ++this->my_size; // TODO: replace by non-atomic op - } - } - - void internal_move_construct_with_allocator( concurrent_hash_map&& other, const allocator_type&, - /*is_always_equal=*/std::true_type ) - { - this->internal_move(std::move(other)); - } - - void internal_move_construct_with_allocator( concurrent_hash_map&& other, const allocator_type& a, - /*is_always_equal=*/std::false_type ) - { - if (a == other.get_allocator()){ - this->internal_move(std::move(other)); - } else { - try_call( [&] { - internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()), - other.size()); - }).on_exception( [&] { - this->clear(); - }); - } - } - - void internal_move_assign( concurrent_hash_map&& other, - /*is_always_equal || POCMA = */std::true_type) - { - this->internal_move(std::move(other)); - } - - void internal_move_assign(concurrent_hash_map&& other, /*is_always_equal=*/ std::false_type) { - if (this->my_allocator == other.my_allocator) { - this->internal_move(std::move(other)); - } else { - //do per element move - internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()), - other.size()); - } - } - - void internal_swap(concurrent_hash_map& other, /*is_always_equal || POCS = */ std::true_type) { - this->internal_swap_content(other); - } - - void internal_swap(concurrent_hash_map& other, /*is_always_equal || POCS = */ std::false_type) { - __TBB_ASSERT(this->my_allocator == other.my_allocator, nullptr); - this->internal_swap_content(other); - } - - // Fast find when no concurrent erasure is used. For internal use inside TBB only! - /** Return pointer to item with given key, or nullptr if no such item exists. - Must not be called concurrently with erasure operations. */ - const_pointer internal_fast_find( const Key& key ) const { - hashcode_type h = my_hash_compare.hash( key ); - hashcode_type m = this->my_mask.load(std::memory_order_acquire); - node *n; - restart: - __TBB_ASSERT((m&(m+1))==0, "data structure is invalid"); - bucket *b = this->get_bucket( h & m ); - // TODO: actually, notification is unnecessary here, just hiding double-check - if (rehash_required(b->node_list.load(std::memory_order_acquire))) - { - typename bucket::scoped_type lock; - if( lock.try_acquire( b->mutex, /*write=*/true ) ) { - if (rehash_required(b->node_list.load(std::memory_order_relaxed))) - const_cast(this)->rehash_bucket( b, h & m ); //recursive rehashing - } - else lock.acquire( b->mutex, /*write=*/false ); - __TBB_ASSERT(!rehash_required(b->node_list.load(std::memory_order_relaxed)), nullptr); - } - n = search_bucket( key, b ); - if( n ) - return n->storage(); - else if( this->check_mask_race( h, m ) ) - goto restart; - return nullptr; - } -}; - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -template >, - typename Alloc = tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_hash_map( It, It, HashCompare = HashCompare(), Alloc = Alloc() ) --> concurrent_hash_map, iterator_mapped_t, HashCompare, Alloc>; - -template >, - typename = std::enable_if_t>> -concurrent_hash_map( It, It, Alloc ) --> concurrent_hash_map, iterator_mapped_t, d1::tbb_hash_compare>, Alloc>; - -template >, - typename Alloc = tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_hash_map( std::initializer_list>, HashCompare = HashCompare(), Alloc = Alloc() ) --> concurrent_hash_map, T, HashCompare, Alloc>; - -template >> -concurrent_hash_map( std::initializer_list>, Alloc ) --> concurrent_hash_map, T, d1::tbb_hash_compare>, Alloc>; - -#endif /* __TBB_CPP17_DEDUCTION_GUIDES_PRESENT */ - -template -inline bool operator==(const concurrent_hash_map &a, const concurrent_hash_map &b) { - if(a.size() != b.size()) return false; - typename concurrent_hash_map::const_iterator i(a.begin()), i_end(a.end()); - typename concurrent_hash_map::const_iterator j, j_end(b.end()); - for(; i != i_end; ++i) { - j = b.equal_range(i->first).first; - if( j == j_end || !(i->second == j->second) ) return false; - } - return true; -} - -#if !__TBB_CPP20_COMPARISONS_PRESENT -template -inline bool operator!=(const concurrent_hash_map &a, const concurrent_hash_map &b) -{ return !(a == b); } -#endif // !__TBB_CPP20_COMPARISONS_PRESENT - -template -inline void swap(concurrent_hash_map &a, concurrent_hash_map &b) -{ a.swap( b ); } - -} // namespace d2 -} // namespace detail - -inline namespace v1 { - using detail::split; - using detail::d2::concurrent_hash_map; - using detail::d1::tbb_hash_compare; -} // namespace v1 - -} // namespace tbb - -#endif /* __TBB_concurrent_hash_map_H */ diff --git a/include/oneapi/tbb/concurrent_lru_cache.h b/include/oneapi/tbb/concurrent_lru_cache.h deleted file mode 100644 index 83d0576..0000000 --- a/include/oneapi/tbb/concurrent_lru_cache.h +++ /dev/null @@ -1,374 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_concurrent_lru_cache_H -#define __TBB_concurrent_lru_cache_H - -#if ! TBB_PREVIEW_CONCURRENT_LRU_CACHE - #error Set TBB_PREVIEW_CONCURRENT_LRU_CACHE to include concurrent_lru_cache.h -#endif - -#include "detail/_assert.h" -#include "detail/_aggregator.h" - -#include // for std::map -#include // for std::list -#include // for std::make_pair -#include // for std::find -#include // for std::atomic - -namespace tbb { - -namespace detail { -namespace d1 { - -//----------------------------------------------------------------------------- -// Concurrent LRU cache -//----------------------------------------------------------------------------- - -template -class concurrent_lru_cache : no_assign { -// incapsulated helper classes -private: - struct handle_object; - struct storage_map_value_type; - - struct aggregator_operation; - struct retrieve_aggregator_operation; - struct signal_end_of_usage_aggregator_operation; - -// typedefs -public: - using key_type = KeyT; - using value_type = ValT; - using pointer = ValT*; - using reference = ValT&; - using const_pointer = const ValT*; - using const_reference = const ValT&; - - using value_function_type = KeyToValFunctorT; - using handle = handle_object; -private: - using lru_cache_type = concurrent_lru_cache; - - using storage_map_type = std::map; - using storage_map_iterator_type = typename storage_map_type::iterator; - using storage_map_pointer_type = typename storage_map_type::pointer; - using storage_map_reference_type = typename storage_map_type::reference; - - using history_list_type = std::list; - using history_list_iterator_type = typename history_list_type::iterator; - - using aggregator_operation_type = aggregator_operation; - using aggregator_function_type = aggregating_functor; - using aggregator_type = aggregator; - - friend class aggregating_functor; - -// fields -private: - value_function_type my_value_function; - aggregator_type my_aggregator; - - storage_map_type my_storage_map; // storage map for used objects - history_list_type my_history_list; // history list for unused objects - const std::size_t my_history_list_capacity; // history list's allowed capacity - -// interface -public: - - concurrent_lru_cache(value_function_type value_function, std::size_t cache_capacity) - : my_value_function(value_function), my_history_list_capacity(cache_capacity) { - my_aggregator.initialize_handler(aggregator_function_type(this)); - } - - handle operator[](key_type key) { - retrieve_aggregator_operation op(key); - my_aggregator.execute(&op); - - if (op.is_new_value_needed()) { - op.result().second.my_value = my_value_function(key); - op.result().second.my_is_ready.store(true, std::memory_order_release); - } else { - spin_wait_while_eq(op.result().second.my_is_ready, false); - } - - return handle(*this, op.result()); - } - -private: - - void handle_operations(aggregator_operation* op_list) { - while (op_list) { - op_list->cast_and_handle(*this); - aggregator_operation* prev_op = op_list; - op_list = op_list->next; - - (prev_op->status).store(1, std::memory_order_release); - } - } - - void signal_end_of_usage(storage_map_reference_type map_record_ref) { - signal_end_of_usage_aggregator_operation op(map_record_ref); - my_aggregator.execute(&op); - } - - void signal_end_of_usage_serial(storage_map_reference_type map_record_ref) { - storage_map_iterator_type map_it = my_storage_map.find(map_record_ref.first); - - __TBB_ASSERT(map_it != my_storage_map.end(), - "cache should not return past-end iterators to outer world"); - __TBB_ASSERT(&(*map_it) == &map_record_ref, - "dangling reference has been returned to outside world: data race?"); - __TBB_ASSERT(std::find(my_history_list.begin(), my_history_list.end(), map_it) == my_history_list.end(), - "object in use should not be in list of unused objects "); - - // if it was the last reference, put it to the LRU history - if (! --(map_it->second.my_ref_counter)) { - // if the LRU history is full, evict the oldest items to get space - if (my_history_list.size() >= my_history_list_capacity) { - if (my_history_list_capacity == 0) { - // Since LRU history capacity is zero, there is no need to keep the element in history - my_storage_map.erase(map_it); - return; - } - std::size_t number_of_elements_to_evict = 1 + my_history_list.size() - my_history_list_capacity; - - for (std::size_t i = 0; i < number_of_elements_to_evict; ++i) { - storage_map_iterator_type map_it_to_evict = my_history_list.back(); - - __TBB_ASSERT(map_it_to_evict->second.my_ref_counter == 0, - "item to be evicted should not have a live references"); - - // TODO: can we use forward_list instead of list? pop_front / insert_after last - my_history_list.pop_back(); - my_storage_map.erase(map_it_to_evict); - } - } - - // TODO: can we use forward_list instead of list? pop_front / insert_after last - my_history_list.push_front(map_it); - map_it->second.my_history_list_iterator = my_history_list.begin(); - } - } - - storage_map_reference_type retrieve_serial(key_type key, bool& is_new_value_needed) { - storage_map_iterator_type map_it = my_storage_map.find(key); - - if (map_it == my_storage_map.end()) { - map_it = my_storage_map.emplace_hint( - map_it, std::piecewise_construct, std::make_tuple(key), std::make_tuple(value_type(), 0, my_history_list.end(), false)); - is_new_value_needed = true; - } else { - history_list_iterator_type list_it = map_it->second.my_history_list_iterator; - if (list_it != my_history_list.end()) { - __TBB_ASSERT(map_it->second.my_ref_counter == 0, - "item to be evicted should not have a live references"); - - // Item is going to be used. Therefore it is not a subject for eviction, - // so we remove it from LRU history. - my_history_list.erase(list_it); - map_it->second.my_history_list_iterator = my_history_list.end(); - } - } - - ++(map_it->second.my_ref_counter); - return *map_it; - } -}; - -//----------------------------------------------------------------------------- -// Value type for storage map in concurrent LRU cache -//----------------------------------------------------------------------------- - -template -struct concurrent_lru_cache::storage_map_value_type { -//typedefs -public: - using ref_counter_type = std::size_t; - -// fields -public: - value_type my_value; - ref_counter_type my_ref_counter; - history_list_iterator_type my_history_list_iterator; - std::atomic my_is_ready; - -// interface -public: - storage_map_value_type( - value_type const& value, ref_counter_type ref_counter, - history_list_iterator_type history_list_iterator, bool is_ready) - : my_value(value), my_ref_counter(ref_counter), - my_history_list_iterator(history_list_iterator), my_is_ready(is_ready) {} -}; - -//----------------------------------------------------------------------------- -// Handle object for operator[] in concurrent LRU cache -//----------------------------------------------------------------------------- - -template -struct concurrent_lru_cache::handle_object { -// fields -private: - lru_cache_type* my_lru_cache_ptr; - storage_map_pointer_type my_map_record_ptr; - -// interface -public: - handle_object() - : my_lru_cache_ptr(nullptr), my_map_record_ptr(nullptr) {} - handle_object(lru_cache_type& lru_cache_ref, storage_map_reference_type map_record_ref) - : my_lru_cache_ptr(&lru_cache_ref), my_map_record_ptr(&map_record_ref) {} - - handle_object(handle_object&) = delete; - void operator=(handle_object&) = delete; - - handle_object(handle_object&& other) - : my_lru_cache_ptr(other.my_lru_cache_ptr), my_map_record_ptr(other.my_map_record_ptr) { - - __TBB_ASSERT( - (other.my_lru_cache_ptr != nullptr && other.my_map_record_ptr != nullptr) || - (other.my_lru_cache_ptr == nullptr && other.my_map_record_ptr == nullptr), - "invalid state of moving object?"); - - other.my_lru_cache_ptr = nullptr; - other.my_map_record_ptr = nullptr; - } - - handle_object& operator=(handle_object&& other) { - __TBB_ASSERT( - (other.my_lru_cache_ptr != nullptr && other.my_map_record_ptr != nullptr) || - (other.my_lru_cache_ptr == nullptr && other.my_map_record_ptr == nullptr), - "invalid state of moving object?"); - - if (my_lru_cache_ptr) - my_lru_cache_ptr->signal_end_of_usage(*my_map_record_ptr); - - my_lru_cache_ptr = other.my_lru_cache_ptr; - my_map_record_ptr = other.my_map_record_ptr; - other.my_lru_cache_ptr = nullptr; - other.my_map_record_ptr = nullptr; - - return *this; - } - - ~handle_object() { - if (my_lru_cache_ptr) - my_lru_cache_ptr->signal_end_of_usage(*my_map_record_ptr); - } - - operator bool() const { - return (my_lru_cache_ptr && my_map_record_ptr); - } - - value_type& value() { - __TBB_ASSERT(my_lru_cache_ptr, "get value from already moved object?"); - __TBB_ASSERT(my_map_record_ptr, "get value from an invalid or already moved object?"); - - return my_map_record_ptr->second.my_value; - } -}; - -//----------------------------------------------------------------------------- -// Aggregator operation for aggregator type in concurrent LRU cache -//----------------------------------------------------------------------------- - -template -struct concurrent_lru_cache::aggregator_operation - : aggregated_operation { -// incapsulated helper classes -public: - enum class op_type { retrieve, signal_end_of_usage }; - -// fields -private: - op_type my_op; - -// interface -public: - aggregator_operation(op_type op) : my_op(op) {} - - // TODO: aggregator_operation can be implemented - // - as a statically typed variant type or CRTP? (static, dependent on the use case) - // - or use pointer to function and apply_visitor (dynamic) - // - or use virtual functions (dynamic) - void cast_and_handle(lru_cache_type& lru_cache_ref) { - if (my_op == op_type::retrieve) - static_cast(this)->handle(lru_cache_ref); - else - static_cast(this)->handle(lru_cache_ref); - } -}; - -template -struct concurrent_lru_cache::retrieve_aggregator_operation - : aggregator_operation, private no_assign { -public: - key_type my_key; - storage_map_pointer_type my_map_record_ptr; - bool my_is_new_value_needed; - -public: - retrieve_aggregator_operation(key_type key) - : aggregator_operation(aggregator_operation::op_type::retrieve), - my_key(key), my_map_record_ptr(nullptr), my_is_new_value_needed(false) {} - - void handle(lru_cache_type& lru_cache_ref) { - my_map_record_ptr = &lru_cache_ref.retrieve_serial(my_key, my_is_new_value_needed); - } - - storage_map_reference_type result() { - __TBB_ASSERT(my_map_record_ptr, "Attempt to call result() before calling handle()"); - return *my_map_record_ptr; - } - - bool is_new_value_needed() { return my_is_new_value_needed; } -}; - -template -struct concurrent_lru_cache::signal_end_of_usage_aggregator_operation - : aggregator_operation, private no_assign { - -private: - storage_map_reference_type my_map_record_ref; - -public: - signal_end_of_usage_aggregator_operation(storage_map_reference_type map_record_ref) - : aggregator_operation(aggregator_operation::op_type::signal_end_of_usage), - my_map_record_ref(map_record_ref) {} - - void handle(lru_cache_type& lru_cache_ref) { - lru_cache_ref.signal_end_of_usage_serial(my_map_record_ref); - } -}; - -// TODO: if we have guarantees that KeyToValFunctorT always have -// ValT as a return type and KeyT as an argument type -// we can deduce template parameters of concurrent_lru_cache -// by pattern matching on KeyToValFunctorT - -} // namespace d1 -} // namespace detail - -inline namespace v1 { - -using detail::d1::concurrent_lru_cache; - -} // inline namespace v1 -} // namespace tbb - -#endif // __TBB_concurrent_lru_cache_H diff --git a/include/oneapi/tbb/concurrent_map.h b/include/oneapi/tbb/concurrent_map.h deleted file mode 100644 index c1817a1..0000000 --- a/include/oneapi/tbb/concurrent_map.h +++ /dev/null @@ -1,350 +0,0 @@ -/* - Copyright (c) 2019-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_concurrent_map_H -#define __TBB_concurrent_map_H - -#include "detail/_namespace_injection.h" -#include "detail/_concurrent_skip_list.h" -#include "tbb_allocator.h" -#include -#include -#include - -namespace tbb { -namespace detail { -namespace d2 { - -template -struct map_traits { - static constexpr std::size_t max_level = RandomGenerator::max_level; - using random_level_generator_type = RandomGenerator; - using key_type = Key; - using mapped_type = Value; - using compare_type = KeyCompare; - using value_type = std::pair; - using reference = value_type&; - using const_reference = const value_type&; - using allocator_type = Allocator; - - static constexpr bool allow_multimapping = AllowMultimapping; - - class value_compare { - public: - bool operator()(const value_type& lhs, const value_type& rhs) const { - return comp(lhs.first, rhs.first); - } - - protected: - value_compare(compare_type c) : comp(c) {} - - friend struct map_traits; - - compare_type comp; - }; - - static value_compare value_comp(compare_type comp) { return value_compare(comp); } - - static const key_type& get_key(const_reference val) { - return val.first; - } -}; // struct map_traits - -template -class concurrent_multimap; - -template , typename Allocator = tbb::tbb_allocator>> -class concurrent_map : public concurrent_skip_list, Allocator, false>> { - using base_type = concurrent_skip_list, Allocator, false>>; -public: - using key_type = Key; - using mapped_type = Value; - using value_type = typename base_type::value_type; - using size_type = typename base_type::size_type; - using difference_type = typename base_type::difference_type; - using key_compare = Compare; - using value_compare = typename base_type::value_compare; - using allocator_type = Allocator; - - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - - using node_type = typename base_type::node_type; - - // Include constructors of base type - using base_type::base_type; - - // Required for implicit deduction guides - concurrent_map() = default; - concurrent_map( const concurrent_map& ) = default; - concurrent_map( const concurrent_map& other, const allocator_type& alloc ) : base_type(other, alloc) {} - concurrent_map( concurrent_map&& ) = default; - concurrent_map( concurrent_map&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {} - // Required to respect the rule of 5 - concurrent_map& operator=( const concurrent_map& ) = default; - concurrent_map& operator=( concurrent_map&& ) = default; - - concurrent_map& operator=( std::initializer_list il ) { - base_type::operator= (il); - return *this; - } - - // Observers - mapped_type& at(const key_type& key) { - iterator it = this->find(key); - - if (it == this->end()) { - throw_exception(exception_id::invalid_key); - } - return it->second; - } - - const mapped_type& at(const key_type& key) const { - return const_cast(this)->at(key); - } - - mapped_type& operator[](const key_type& key) { - iterator it = this->find(key); - - if (it == this->end()) { - it = this->emplace(std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>()).first; - } - return it->second; - } - - mapped_type& operator[](key_type&& key) { - iterator it = this->find(key); - - if (it == this->end()) { - it = this->emplace(std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>()).first; - } - return it->second; - } - - using base_type::insert; - - template - typename std::enable_if::value, - std::pair>::type insert( P&& value ) - { - return this->emplace(std::forward

(value)); - } - - template - typename std::enable_if::value, - iterator>::type insert( const_iterator hint, P&& value ) - { - return this->emplace_hint(hint, std::forward

(value)); - } - - template - void merge(concurrent_map& source) { - this->internal_merge(source); - } - - template - void merge(concurrent_map&& source) { - this->internal_merge(std::move(source)); - } - - template - void merge(concurrent_multimap& source) { - this->internal_merge(source); - } - - template - void merge(concurrent_multimap&& source) { - this->internal_merge(std::move(source)); - } -}; // class concurrent_map - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template >, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_map( It, It, Comp = Comp(), Alloc = Alloc() ) --> concurrent_map, iterator_mapped_t, Comp, Alloc>; - -template >, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_map( std::initializer_list>, Comp = Comp(), Alloc = Alloc() ) --> concurrent_map, T, Comp, Alloc>; - -template >, - typename = std::enable_if_t>> -concurrent_map( It, It, Alloc ) --> concurrent_map, iterator_mapped_t, - std::less>, Alloc>; - -template >> -concurrent_map( std::initializer_list>, Alloc ) --> concurrent_map, T, std::less>, Alloc>; - -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template -void swap( concurrent_map& lhs, - concurrent_map& rhs ) -{ - lhs.swap(rhs); -} - -template , typename Allocator = tbb::tbb_allocator>> -class concurrent_multimap : public concurrent_skip_list, Allocator, true>> { - using base_type = concurrent_skip_list, Allocator, true>>; -public: - using key_type = Key; - using mapped_type = Value; - using value_type = typename base_type::value_type; - using size_type = typename base_type::size_type; - using difference_type = typename base_type::difference_type; - using key_compare = Compare; - using value_compare = typename base_type::value_compare; - using allocator_type = Allocator; - - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - - using node_type = typename base_type::node_type; - - // Include constructors of base_type - using base_type::base_type; - using base_type::insert; - - // Required for implicit deduction guides - concurrent_multimap() = default; - concurrent_multimap( const concurrent_multimap& ) = default; - concurrent_multimap( const concurrent_multimap& other, const allocator_type& alloc ) : base_type(other, alloc) {} - concurrent_multimap( concurrent_multimap&& ) = default; - concurrent_multimap( concurrent_multimap&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {} - // Required to respect the rule of 5 - concurrent_multimap& operator=( const concurrent_multimap& ) = default; - concurrent_multimap& operator=( concurrent_multimap&& ) = default; - - concurrent_multimap& operator=( std::initializer_list il ) { - base_type::operator= (il); - return *this; - } - - template - typename std::enable_if::value, - std::pair>::type insert( P&& value ) - { - return this->emplace(std::forward

(value)); - } - - template - typename std::enable_if::value, - iterator>::type insert( const_iterator hint, P&& value ) - { - return this->emplace_hint(hint, std::forward

(value)); - } - - template - void merge(concurrent_multimap& source) { - this->internal_merge(source); - } - - template - void merge(concurrent_multimap&& source) { - this->internal_merge(std::move(source)); - } - - template - void merge(concurrent_map& source) { - this->internal_merge(source); - } - - template - void merge(concurrent_map&& source) { - this->internal_merge(std::move(source)); - } -}; // class concurrent_multimap - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template >, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_multimap( It, It, Comp = Comp(), Alloc = Alloc() ) --> concurrent_multimap, iterator_mapped_t, Comp, Alloc>; - -template >, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_multimap( std::initializer_list>, Comp = Comp(), Alloc = Alloc() ) --> concurrent_multimap, T, Comp, Alloc>; - -template >, - typename = std::enable_if_t>> -concurrent_multimap( It, It, Alloc ) --> concurrent_multimap, iterator_mapped_t, - std::less>, Alloc>; - -template >> -concurrent_multimap( std::initializer_list>, Alloc ) --> concurrent_multimap, T, std::less>, Alloc>; - - -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template -void swap( concurrent_multimap& lhs, - concurrent_multimap& rhs ) -{ - lhs.swap(rhs); -} - -} // namespace d2 -} // namespace detail - -inline namespace v1 { - -using detail::d2::concurrent_map; -using detail::d2::concurrent_multimap; -using detail::split; - -} // inline namespace v1 -} // namespace tbb - -#endif // __TBB_concurrent_map_H diff --git a/include/oneapi/tbb/concurrent_priority_queue.h b/include/oneapi/tbb/concurrent_priority_queue.h deleted file mode 100644 index 6d7eb3e..0000000 --- a/include/oneapi/tbb/concurrent_priority_queue.h +++ /dev/null @@ -1,490 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_concurrent_priority_queue_H -#define __TBB_concurrent_priority_queue_H - -#include "detail/_namespace_injection.h" -#include "detail/_aggregator.h" -#include "detail/_template_helpers.h" -#include "detail/_allocator_traits.h" -#include "detail/_range_common.h" -#include "detail/_exception.h" -#include "detail/_utils.h" -#include "detail/_containers_helpers.h" -#include "cache_aligned_allocator.h" -#include -#include -#include -#include -#include -#include - -namespace tbb { -namespace detail { -namespace d1 { - -template , typename Allocator = cache_aligned_allocator> -class concurrent_priority_queue { -public: - using value_type = T; - using reference = T&; - using const_reference = const T&; - - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - using allocator_type = Allocator; - - concurrent_priority_queue() : concurrent_priority_queue(allocator_type{}) {} - - explicit concurrent_priority_queue( const allocator_type& alloc ) - : mark(0), my_size(0), my_compare(), data(alloc) - { - my_aggregator.initialize_handler(functor{this}); - } - - explicit concurrent_priority_queue( const Compare& compare, const allocator_type& alloc = allocator_type() ) - : mark(0), my_size(0), my_compare(compare), data(alloc) - { - my_aggregator.initialize_handler(functor{this}); - } - - explicit concurrent_priority_queue( size_type init_capacity, const allocator_type& alloc = allocator_type() ) - : mark(0), my_size(0), my_compare(), data(alloc) - { - data.reserve(init_capacity); - my_aggregator.initialize_handler(functor{this}); - } - - explicit concurrent_priority_queue( size_type init_capacity, const Compare& compare, const allocator_type& alloc = allocator_type() ) - : mark(0), my_size(0), my_compare(compare), data(alloc) - { - data.reserve(init_capacity); - my_aggregator.initialize_handler(functor{this}); - } - - template - concurrent_priority_queue( InputIterator begin, InputIterator end, const Compare& compare, const allocator_type& alloc = allocator_type() ) - : mark(0), my_compare(compare), data(begin, end, alloc) - { - my_aggregator.initialize_handler(functor{this}); - heapify(); - my_size.store(data.size(), std::memory_order_relaxed); - } - - template - concurrent_priority_queue( InputIterator begin, InputIterator end, const allocator_type& alloc = allocator_type() ) - : concurrent_priority_queue(begin, end, Compare(), alloc) {} - - concurrent_priority_queue( std::initializer_list init, const Compare& compare, const allocator_type& alloc = allocator_type() ) - : concurrent_priority_queue(init.begin(), init.end(), compare, alloc) {} - - concurrent_priority_queue( std::initializer_list init, const allocator_type& alloc = allocator_type() ) - : concurrent_priority_queue(init, Compare(), alloc) {} - - concurrent_priority_queue( const concurrent_priority_queue& other ) - : mark(other.mark), my_size(other.my_size.load(std::memory_order_relaxed)), my_compare(other.my_compare), - data(other.data) - { - my_aggregator.initialize_handler(functor{this}); - } - - concurrent_priority_queue( const concurrent_priority_queue& other, const allocator_type& alloc ) - : mark(other.mark), my_size(other.my_size.load(std::memory_order_relaxed)), my_compare(other.my_compare), - data(other.data, alloc) - { - my_aggregator.initialize_handler(functor{this}); - } - - concurrent_priority_queue( concurrent_priority_queue&& other ) - : mark(other.mark), my_size(other.my_size.load(std::memory_order_relaxed)), my_compare(other.my_compare), - data(std::move(other.data)) - { - my_aggregator.initialize_handler(functor{this}); - } - - concurrent_priority_queue( concurrent_priority_queue&& other, const allocator_type& alloc ) - : mark(other.mark), my_size(other.my_size.load(std::memory_order_relaxed)), my_compare(other.my_compare), - data(std::move(other.data), alloc) - { - my_aggregator.initialize_handler(functor{this}); - } - - concurrent_priority_queue& operator=( const concurrent_priority_queue& other ) { - if (this != &other) { - data = other.data; - mark = other.mark; - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - } - return *this; - } - - concurrent_priority_queue& operator=( concurrent_priority_queue&& other ) { - if (this != &other) { - // TODO: check if exceptions from std::vector::operator=(vector&&) should be handled separately - data = std::move(other.data); - mark = other.mark; - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - } - return *this; - } - - concurrent_priority_queue& operator=( std::initializer_list init ) { - assign(init.begin(), init.end()); - return *this; - } - - template - void assign( InputIterator begin, InputIterator end ) { - data.assign(begin, end); - mark = 0; - my_size.store(data.size(), std::memory_order_relaxed); - heapify(); - } - - void assign( std::initializer_list init ) { - assign(init.begin(), init.end()); - } - - /* Returned value may not reflect results of pending operations. - This operation reads shared data and will trigger a race condition. */ - __TBB_nodiscard bool empty() const { return size() == 0; } - - // Returns the current number of elements contained in the queue - /* Returned value may not reflect results of pending operations. - This operation reads shared data and will trigger a race condition. */ - size_type size() const { return my_size.load(std::memory_order_relaxed); } - - /* This operation can be safely used concurrently with other push, try_pop or emplace operations. */ - void push( const value_type& value ) { - cpq_operation op_data(value, PUSH_OP); - my_aggregator.execute(&op_data); - if (op_data.status == FAILED) - throw_exception(exception_id::bad_alloc); - } - - /* This operation can be safely used concurrently with other push, try_pop or emplace operations. */ - void push( value_type&& value ) { - cpq_operation op_data(value, PUSH_RVALUE_OP); - my_aggregator.execute(&op_data); - if (op_data.status == FAILED) - throw_exception(exception_id::bad_alloc); - } - - /* This operation can be safely used concurrently with other push, try_pop or emplace operations. */ - template - void emplace( Args&&... args ) { - // TODO: support uses allocator construction in this place - push(value_type(std::forward(args)...)); - } - - // Gets a reference to and removes highest priority element - /* If a highest priority element was found, sets elem and returns true, - otherwise returns false. - This operation can be safely used concurrently with other push, try_pop or emplace operations. */ - bool try_pop( value_type& value ) { - cpq_operation op_data(value, POP_OP); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - // This operation affects the whole container => it is not thread-safe - void clear() { - data.clear(); - mark = 0; - my_size.store(0, std::memory_order_relaxed); - } - - // This operation affects the whole container => it is not thread-safe - void swap( concurrent_priority_queue& other ) { - if (this != &other) { - using std::swap; - swap(data, other.data); - swap(mark, other.mark); - - size_type sz = my_size.load(std::memory_order_relaxed); - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.my_size.store(sz, std::memory_order_relaxed); - } - } - - allocator_type get_allocator() const { return data.get_allocator(); } -private: - enum operation_type {INVALID_OP, PUSH_OP, POP_OP, PUSH_RVALUE_OP}; - enum operation_status {WAIT = 0, SUCCEEDED, FAILED}; - - class cpq_operation : public aggregated_operation { - public: - operation_type type; - union { - value_type* elem; - size_type sz; - }; - cpq_operation( const value_type& value, operation_type t ) - : type(t), elem(const_cast(&value)) {} - }; // class cpq_operation - - class functor { - concurrent_priority_queue* my_cpq; - public: - functor() : my_cpq(nullptr) {} - functor( concurrent_priority_queue* cpq ) : my_cpq(cpq) {} - - void operator()(cpq_operation* op_list) { - __TBB_ASSERT(my_cpq != nullptr, "Invalid functor"); - my_cpq->handle_operations(op_list); - } - }; // class functor - - void handle_operations( cpq_operation* op_list ) { - call_itt_notify(acquired, this); - cpq_operation* tmp, *pop_list = nullptr; - __TBB_ASSERT(mark == data.size(), nullptr); - - // First pass processes all constant (amortized; reallocation may happen) time pushes and pops. - while(op_list) { - // ITT note: &(op_list->status) tag is used to cover accesses to op_list - // node. This thread is going to handle the operation, and so will acquire it - // and perform the associated operation w/o triggering a race condition; the - // thread that created the operation is waiting on the status field, so when - // this thread is done with the operation, it will perform a - // store_with_release to give control back to the waiting thread in - // aggregator::insert_operation. - // TODO: enable - call_itt_notify(acquired, &(op_list->status)); - __TBB_ASSERT(op_list->type != INVALID_OP, nullptr); - - tmp = op_list; - op_list = op_list->next.load(std::memory_order_relaxed); - if (tmp->type == POP_OP) { - if (mark < data.size() && - my_compare(data[0], data.back())) - { - // there are newly pushed elems and the last one is higher than top - *(tmp->elem) = std::move(data.back()); - my_size.store(my_size.load(std::memory_order_relaxed) - 1, std::memory_order_relaxed); - tmp->status.store(uintptr_t(SUCCEEDED), std::memory_order_release); - - data.pop_back(); - __TBB_ASSERT(mark <= data.size(), nullptr); - } else { // no convenient item to pop; postpone - tmp->next.store(pop_list, std::memory_order_relaxed); - pop_list = tmp; - } - } else { // PUSH_OP or PUSH_RVALUE_OP - __TBB_ASSERT(tmp->type == PUSH_OP || tmp->type == PUSH_RVALUE_OP, "Unknown operation"); -#if TBB_USE_EXCEPTIONS - try -#endif - { - if (tmp->type == PUSH_OP) { - push_back_helper(*(tmp->elem)); - } else { - data.push_back(std::move(*(tmp->elem))); - } - my_size.store(my_size.load(std::memory_order_relaxed) + 1, std::memory_order_relaxed); - tmp->status.store(uintptr_t(SUCCEEDED), std::memory_order_release); - } -#if TBB_USE_EXCEPTIONS - catch(...) { - tmp->status.store(uintptr_t(FAILED), std::memory_order_release); - } -#endif - } - } - - // Second pass processes pop operations - while(pop_list) { - tmp = pop_list; - pop_list = pop_list->next.load(std::memory_order_relaxed); - __TBB_ASSERT(tmp->type == POP_OP, nullptr); - if (data.empty()) { - tmp->status.store(uintptr_t(FAILED), std::memory_order_release); - } else { - __TBB_ASSERT(mark <= data.size(), nullptr); - if (mark < data.size() && - my_compare(data[0], data.back())) - { - // there are newly pushed elems and the last one is higher than top - *(tmp->elem) = std::move(data.back()); - my_size.store(my_size.load(std::memory_order_relaxed) - 1, std::memory_order_relaxed); - tmp->status.store(uintptr_t(SUCCEEDED), std::memory_order_release); - data.pop_back(); - } else { // extract top and push last element down heap - *(tmp->elem) = std::move(data[0]); - my_size.store(my_size.load(std::memory_order_relaxed) - 1, std::memory_order_relaxed); - tmp->status.store(uintptr_t(SUCCEEDED), std::memory_order_release); - reheap(); - } - } - } - - // heapify any leftover pushed elements before doing the next - // batch of operations - if (mark < data.size()) heapify(); - __TBB_ASSERT(mark == data.size(), nullptr); - call_itt_notify(releasing, this); - } - - // Merge unsorted elements into heap - void heapify() { - if (!mark && data.size() > 0) mark = 1; - for (; mark < data.size(); ++mark) { - // for each unheapified element under size - size_type cur_pos = mark; - value_type to_place = std::move(data[mark]); - do { // push to_place up the heap - size_type parent = (cur_pos - 1) >> 1; - if (!my_compare(data[parent], to_place)) - break; - data[cur_pos] = std::move(data[parent]); - cur_pos = parent; - } while(cur_pos); - data[cur_pos] = std::move(to_place); - } - } - - // Re-heapify after an extraction - // Re-heapify by pushing last element down the heap from the root. - void reheap() { - size_type cur_pos = 0, child = 1; - - while(child < mark) { - size_type target = child; - if (child + 1 < mark && my_compare(data[child], data[child + 1])) - ++target; - // target now has the higher priority child - if (my_compare(data[target], data.back())) - break; - data[cur_pos] = std::move(data[target]); - cur_pos = target; - child = (cur_pos << 1) + 1; - } - if (cur_pos != data.size() - 1) - data[cur_pos] = std::move(data.back()); - data.pop_back(); - if (mark > data.size()) mark = data.size(); - } - - void push_back_helper( const T& value ) { - push_back_helper_impl(value, std::is_copy_constructible{}); - } - - void push_back_helper_impl( const T& value, /*is_copy_constructible = */std::true_type ) { - data.push_back(value); - } - - void push_back_helper_impl( const T&, /*is_copy_constructible = */std::false_type ) { - __TBB_ASSERT(false, "error: calling tbb::concurrent_priority_queue.push(const value_type&) for move-only type"); - } - - using aggregator_type = aggregator; - - aggregator_type my_aggregator; - // Padding added to avoid false sharing - char padding1[max_nfs_size - sizeof(aggregator_type)]; - // The point at which unsorted elements begin - size_type mark; - std::atomic my_size; - Compare my_compare; - - // Padding added to avoid false sharing - char padding2[max_nfs_size - (2*sizeof(size_type)) - sizeof(Compare)]; - //! Storage for the heap of elements in queue, plus unheapified elements - /** data has the following structure: - - binary unheapified - heap elements - ____|_______|____ - | | | - v v v - [_|...|_|_|...|_| |...| ] - 0 ^ ^ ^ - | | |__capacity - | |__my_size - |__mark - - Thus, data stores the binary heap starting at position 0 through - mark-1 (it may be empty). Then there are 0 or more elements - that have not yet been inserted into the heap, in positions - mark through my_size-1. */ - - using vector_type = std::vector; - vector_type data; - - friend bool operator==( const concurrent_priority_queue& lhs, - const concurrent_priority_queue& rhs ) - { - return lhs.data == rhs.data; - } - -#if !__TBB_CPP20_COMPARISONS_PRESENT - friend bool operator!=( const concurrent_priority_queue& lhs, - const concurrent_priority_queue& rhs ) - { - return !(lhs == rhs); - } -#endif -}; // class concurrent_priority_queue - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -template >, - typename Alloc = tbb::cache_aligned_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_priority_queue( It, It, Comp = Comp(), Alloc = Alloc() ) --> concurrent_priority_queue, Comp, Alloc>; - -template >, - typename = std::enable_if_t>> -concurrent_priority_queue( It, It, Alloc ) --> concurrent_priority_queue, std::less>, Alloc>; - -template , - typename Alloc = tbb::cache_aligned_allocator, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_priority_queue( std::initializer_list, Comp = Comp(), Alloc = Alloc() ) --> concurrent_priority_queue; - -template >> -concurrent_priority_queue( std::initializer_list, Alloc ) --> concurrent_priority_queue, Alloc>; - -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template -void swap( concurrent_priority_queue& lhs, - concurrent_priority_queue& rhs ) -{ - lhs.swap(rhs); -} - -} // namespace d1 -} // namespace detail -inline namespace v1 { -using detail::d1::concurrent_priority_queue; - -} // inline namespace v1 -} // namespace tbb - -#endif // __TBB_concurrent_priority_queue_H diff --git a/include/oneapi/tbb/concurrent_queue.h b/include/oneapi/tbb/concurrent_queue.h deleted file mode 100644 index 1e7ff50..0000000 --- a/include/oneapi/tbb/concurrent_queue.h +++ /dev/null @@ -1,700 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_concurrent_queue_H -#define __TBB_concurrent_queue_H - -#include "detail/_namespace_injection.h" -#include "detail/_concurrent_queue_base.h" -#include "detail/_allocator_traits.h" -#include "detail/_exception.h" -#include "detail/_containers_helpers.h" -#include "cache_aligned_allocator.h" - -namespace tbb { -namespace detail { -namespace d2 { - -template -std::pair internal_try_pop_impl(void* dst, QueueRep& queue, Allocator& alloc ) { - ticket_type ticket{}; - do { - // Basically, we need to read `head_counter` before `tail_counter`. To achieve it we build happens-before on `head_counter` - ticket = queue.head_counter.load(std::memory_order_acquire); - do { - if (static_cast(queue.tail_counter.load(std::memory_order_relaxed) - ticket) <= 0) { // queue is empty - // Queue is empty - return { false, ticket }; - } - // Queue had item with ticket k when we looked. Attempt to get that item. - // Another thread snatched the item, retry. - } while (!queue.head_counter.compare_exchange_strong(ticket, ticket + 1)); - } while (!queue.choose(ticket).pop(dst, ticket, queue, alloc)); - return { true, ticket }; -} - -// A high-performance thread-safe non-blocking concurrent queue. -// Multiple threads may each push and pop concurrently. -// Assignment construction is not allowed. -template > -class concurrent_queue { - using allocator_traits_type = tbb::detail::allocator_traits; - using queue_representation_type = concurrent_queue_rep; - using queue_allocator_type = typename allocator_traits_type::template rebind_alloc; - using queue_allocator_traits = tbb::detail::allocator_traits; -public: - using size_type = std::size_t; - using value_type = T; - using reference = T&; - using const_reference = const T&; - using difference_type = std::ptrdiff_t; - - using allocator_type = Allocator; - using pointer = typename allocator_traits_type::pointer; - using const_pointer = typename allocator_traits_type::const_pointer; - - using iterator = concurrent_queue_iterator; - using const_iterator = concurrent_queue_iterator; - - concurrent_queue() : concurrent_queue(allocator_type()) {} - - explicit concurrent_queue(const allocator_type& a) : - my_allocator(a), my_queue_representation(nullptr) - { - my_queue_representation = static_cast(r1::cache_aligned_allocate(sizeof(queue_representation_type))); - queue_allocator_traits::construct(my_allocator, my_queue_representation); - - __TBB_ASSERT(is_aligned(my_queue_representation, max_nfs_size), "alignment error" ); - __TBB_ASSERT(is_aligned(&my_queue_representation->head_counter, max_nfs_size), "alignment error" ); - __TBB_ASSERT(is_aligned(&my_queue_representation->tail_counter, max_nfs_size), "alignment error" ); - __TBB_ASSERT(is_aligned(&my_queue_representation->array, max_nfs_size), "alignment error" ); - } - - template - concurrent_queue(InputIterator begin, InputIterator end, const allocator_type& a = allocator_type()) : - concurrent_queue(a) - { - for (; begin != end; ++begin) - push(*begin); - } - - concurrent_queue( std::initializer_list init, const allocator_type& alloc = allocator_type() ) : - concurrent_queue(init.begin(), init.end(), alloc) - {} - - concurrent_queue(const concurrent_queue& src, const allocator_type& a) : - concurrent_queue(a) - { - my_queue_representation->assign(*src.my_queue_representation, my_allocator, copy_construct_item); - } - - concurrent_queue(const concurrent_queue& src) : - concurrent_queue(queue_allocator_traits::select_on_container_copy_construction(src.get_allocator())) - { - my_queue_representation->assign(*src.my_queue_representation, my_allocator, copy_construct_item); - } - - // Move constructors - concurrent_queue(concurrent_queue&& src) : - concurrent_queue(std::move(src.my_allocator)) - { - internal_swap(src); - } - - concurrent_queue(concurrent_queue&& src, const allocator_type& a) : - concurrent_queue(a) - { - // checking that memory allocated by one instance of allocator can be deallocated - // with another - if (my_allocator == src.my_allocator) { - internal_swap(src); - } else { - // allocators are different => performing per-element move - my_queue_representation->assign(*src.my_queue_representation, my_allocator, move_construct_item); - src.clear(); - } - } - - // Destroy queue - ~concurrent_queue() { - clear(); - my_queue_representation->clear(my_allocator); - queue_allocator_traits::destroy(my_allocator, my_queue_representation); - r1::cache_aligned_deallocate(my_queue_representation); - } - - concurrent_queue& operator=( const concurrent_queue& other ) { - //TODO: implement support for std::allocator_traits::propagate_on_container_copy_assignment - if (my_queue_representation != other.my_queue_representation) { - clear(); - my_allocator = other.my_allocator; - my_queue_representation->assign(*other.my_queue_representation, my_allocator, copy_construct_item); - } - return *this; - } - - concurrent_queue& operator=( concurrent_queue&& other ) { - //TODO: implement support for std::allocator_traits::propagate_on_container_move_assignment - if (my_queue_representation != other.my_queue_representation) { - clear(); - if (my_allocator == other.my_allocator) { - internal_swap(other); - } else { - my_queue_representation->assign(*other.my_queue_representation, other.my_allocator, move_construct_item); - other.clear(); - my_allocator = std::move(other.my_allocator); - } - } - return *this; - } - - concurrent_queue& operator=( std::initializer_list init ) { - assign(init); - return *this; - } - - template - void assign( InputIterator first, InputIterator last ) { - concurrent_queue src(first, last); - clear(); - my_queue_representation->assign(*src.my_queue_representation, my_allocator, move_construct_item); - } - - void assign( std::initializer_list init ) { - assign(init.begin(), init.end()); - } - - void swap ( concurrent_queue& other ) { - //TODO: implement support for std::allocator_traits::propagate_on_container_swap - __TBB_ASSERT(my_allocator == other.my_allocator, "unequal allocators"); - internal_swap(other); - } - - // Enqueue an item at tail of queue. - void push(const T& value) { - internal_push(value); - } - - void push(T&& value) { - internal_push(std::move(value)); - } - - template - void emplace( Args&&... args ) { - internal_push(std::forward(args)...); - } - - // Attempt to dequeue an item from head of queue. - /** Does not wait for item to become available. - Returns true if successful; false otherwise. */ - bool try_pop( T& result ) { - return internal_try_pop(&result); - } - - // Return the number of items in the queue; thread unsafe - size_type unsafe_size() const { - std::ptrdiff_t size = my_queue_representation->size(); - return size < 0 ? 0 : size_type(size); - } - - // Equivalent to size()==0. - __TBB_nodiscard bool empty() const { - return my_queue_representation->empty(); - } - - // Clear the queue. not thread-safe. - void clear() { - my_queue_representation->clear(my_allocator); - } - - // Return allocator object - allocator_type get_allocator() const { return my_allocator; } - - //------------------------------------------------------------------------ - // The iterators are intended only for debugging. They are slow and not thread safe. - //------------------------------------------------------------------------ - - iterator unsafe_begin() { return concurrent_queue_iterator_provider::get(*this); } - iterator unsafe_end() { return iterator(); } - const_iterator unsafe_begin() const { return concurrent_queue_iterator_provider::get(*this); } - const_iterator unsafe_end() const { return const_iterator(); } - const_iterator unsafe_cbegin() const { return concurrent_queue_iterator_provider::get(*this); } - const_iterator unsafe_cend() const { return const_iterator(); } - -private: - void internal_swap(concurrent_queue& src) { - using std::swap; - swap(my_queue_representation, src.my_queue_representation); - } - - template - void internal_push( Args&&... args ) { - ticket_type k = my_queue_representation->tail_counter++; - my_queue_representation->choose(k).push(k, *my_queue_representation, my_allocator, std::forward(args)...); - } - - bool internal_try_pop( void* dst ) { - return internal_try_pop_impl(dst, *my_queue_representation, my_allocator).first; - } - - template - friend class concurrent_queue_iterator; - - static void copy_construct_item(T* location, const void* src) { - // TODO: use allocator_traits for copy construction - new (location) value_type(*static_cast(src)); - // queue_allocator_traits::construct(my_allocator, location, *static_cast(src)); - } - - static void move_construct_item(T* location, const void* src) { - // TODO: use allocator_traits for move construction - new (location) value_type(std::move(*static_cast(const_cast(src)))); - } - - queue_allocator_type my_allocator; - queue_representation_type* my_queue_representation; - - friend void swap( concurrent_queue& lhs, concurrent_queue& rhs ) { - lhs.swap(rhs); - } - - friend bool operator==( const concurrent_queue& lhs, const concurrent_queue& rhs ) { - return lhs.unsafe_size() == rhs.unsafe_size() && std::equal(lhs.unsafe_begin(), lhs.unsafe_end(), rhs.unsafe_begin()); - } - -#if !__TBB_CPP20_COMPARISONS_PRESENT - friend bool operator!=( const concurrent_queue& lhs, const concurrent_queue& rhs ) { - return !(lhs == rhs); - } -#endif // __TBB_CPP20_COMPARISONS_PRESENT -}; // class concurrent_queue - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -// Deduction guide for the constructor from two iterators -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_queue( It, It, Alloc = Alloc() ) --> concurrent_queue, Alloc>; - -#endif /* __TBB_CPP17_DEDUCTION_GUIDES_PRESENT */ - -class concurrent_monitor; - -// The concurrent monitor tags for concurrent_bounded_queue. -static constexpr std::size_t cbq_slots_avail_tag = 0; -static constexpr std::size_t cbq_items_avail_tag = 1; -} // namespace d2 - - -namespace r1 { - class concurrent_monitor; - - TBB_EXPORT std::uint8_t* __TBB_EXPORTED_FUNC allocate_bounded_queue_rep( std::size_t queue_rep_size ); - TBB_EXPORT void __TBB_EXPORTED_FUNC deallocate_bounded_queue_rep( std::uint8_t* mem, std::size_t queue_rep_size ); - TBB_EXPORT void __TBB_EXPORTED_FUNC abort_bounded_queue_monitors( concurrent_monitor* monitors ); - TBB_EXPORT void __TBB_EXPORTED_FUNC notify_bounded_queue_monitor( concurrent_monitor* monitors, std::size_t monitor_tag - , std::size_t ticket ); - TBB_EXPORT void __TBB_EXPORTED_FUNC wait_bounded_queue_monitor( concurrent_monitor* monitors, std::size_t monitor_tag, - std::ptrdiff_t target, d1::delegate_base& predicate ); -} // namespace r1 - - -namespace d2 { -// A high-performance thread-safe blocking concurrent bounded queue. -// Supports boundedness and blocking semantics. -// Multiple threads may each push and pop concurrently. -// Assignment construction is not allowed. -template > -class concurrent_bounded_queue { - using allocator_traits_type = tbb::detail::allocator_traits; - using queue_representation_type = concurrent_queue_rep; - using queue_allocator_type = typename allocator_traits_type::template rebind_alloc; - using queue_allocator_traits = tbb::detail::allocator_traits; - - template - void internal_wait(r1::concurrent_monitor* monitors, std::size_t monitor_tag, std::ptrdiff_t target, FuncType pred) { - d1::delegated_function func(pred); - r1::wait_bounded_queue_monitor(monitors, monitor_tag, target, func); - } -public: - using size_type = std::ptrdiff_t; - using value_type = T; - using reference = T&; - using const_reference = const T&; - using difference_type = std::ptrdiff_t; - - using allocator_type = Allocator; - using pointer = typename allocator_traits_type::pointer; - using const_pointer = typename allocator_traits_type::const_pointer; - - using iterator = concurrent_queue_iterator; - using const_iterator = concurrent_queue_iterator ; - - concurrent_bounded_queue() : concurrent_bounded_queue(allocator_type()) {} - - explicit concurrent_bounded_queue( const allocator_type& a ) : - my_allocator(a), my_capacity(0), my_abort_counter(0), my_queue_representation(nullptr) - { - my_queue_representation = reinterpret_cast( - r1::allocate_bounded_queue_rep(sizeof(queue_representation_type))); - my_monitors = reinterpret_cast(my_queue_representation + 1); - queue_allocator_traits::construct(my_allocator, my_queue_representation); - my_capacity = std::size_t(-1) / (queue_representation_type::item_size > 1 ? queue_representation_type::item_size : 2); - - __TBB_ASSERT(is_aligned(my_queue_representation, max_nfs_size), "alignment error" ); - __TBB_ASSERT(is_aligned(&my_queue_representation->head_counter, max_nfs_size), "alignment error" ); - __TBB_ASSERT(is_aligned(&my_queue_representation->tail_counter, max_nfs_size), "alignment error" ); - __TBB_ASSERT(is_aligned(&my_queue_representation->array, max_nfs_size), "alignment error" ); - } - - template - concurrent_bounded_queue( InputIterator begin, InputIterator end, const allocator_type& a = allocator_type() ) : - concurrent_bounded_queue(a) - { - for (; begin != end; ++begin) - push(*begin); - } - - concurrent_bounded_queue( std::initializer_list init, const allocator_type& alloc = allocator_type() ): - concurrent_bounded_queue(init.begin(), init.end(), alloc) - {} - - concurrent_bounded_queue( const concurrent_bounded_queue& src, const allocator_type& a ) : - concurrent_bounded_queue(a) - { - my_queue_representation->assign(*src.my_queue_representation, my_allocator, copy_construct_item); - } - - concurrent_bounded_queue( const concurrent_bounded_queue& src ) : - concurrent_bounded_queue(queue_allocator_traits::select_on_container_copy_construction(src.get_allocator())) - { - my_queue_representation->assign(*src.my_queue_representation, my_allocator, copy_construct_item); - } - - // Move constructors - concurrent_bounded_queue( concurrent_bounded_queue&& src ) : - concurrent_bounded_queue(std::move(src.my_allocator)) - { - internal_swap(src); - } - - concurrent_bounded_queue( concurrent_bounded_queue&& src, const allocator_type& a ) : - concurrent_bounded_queue(a) - { - // checking that memory allocated by one instance of allocator can be deallocated - // with another - if (my_allocator == src.my_allocator) { - internal_swap(src); - } else { - // allocators are different => performing per-element move - my_queue_representation->assign(*src.my_queue_representation, my_allocator, move_construct_item); - src.clear(); - } - } - - // Destroy queue - ~concurrent_bounded_queue() { - clear(); - my_queue_representation->clear(my_allocator); - queue_allocator_traits::destroy(my_allocator, my_queue_representation); - r1::deallocate_bounded_queue_rep(reinterpret_cast(my_queue_representation), - sizeof(queue_representation_type)); - } - - concurrent_bounded_queue& operator=( const concurrent_bounded_queue& other ) { - //TODO: implement support for std::allocator_traits::propagate_on_container_copy_assignment - if (my_queue_representation != other.my_queue_representation) { - clear(); - my_allocator = other.my_allocator; - my_queue_representation->assign(*other.my_queue_representation, my_allocator, copy_construct_item); - } - return *this; - } - - concurrent_bounded_queue& operator=( concurrent_bounded_queue&& other ) { - //TODO: implement support for std::allocator_traits::propagate_on_container_move_assignment - if (my_queue_representation != other.my_queue_representation) { - clear(); - if (my_allocator == other.my_allocator) { - internal_swap(other); - } else { - my_queue_representation->assign(*other.my_queue_representation, other.my_allocator, move_construct_item); - other.clear(); - my_allocator = std::move(other.my_allocator); - } - } - return *this; - } - - concurrent_bounded_queue& operator=( std::initializer_list init ) { - assign(init); - return *this; - } - - template - void assign( InputIterator first, InputIterator last ) { - concurrent_bounded_queue src(first, last); - clear(); - my_queue_representation->assign(*src.my_queue_representation, my_allocator, move_construct_item); - } - - void assign( std::initializer_list init ) { - assign(init.begin(), init.end()); - } - - void swap ( concurrent_bounded_queue& other ) { - //TODO: implement support for std::allocator_traits::propagate_on_container_swap - __TBB_ASSERT(my_allocator == other.my_allocator, "unequal allocators"); - internal_swap(other); - } - - // Enqueue an item at tail of queue. - void push( const T& value ) { - internal_push(value); - } - - void push( T&& value ) { - internal_push(std::move(value)); - } - - // Enqueue an item at tail of queue if queue is not already full. - // Does not wait for queue to become not full. - // Returns true if item is pushed; false if queue was already full. - bool try_push( const T& value ) { - return internal_push_if_not_full(value); - } - - bool try_push( T&& value ) { - return internal_push_if_not_full(std::move(value)); - } - - template - void emplace( Args&&... args ) { - internal_push(std::forward(args)...); - } - - template - bool try_emplace( Args&&... args ) { - return internal_push_if_not_full(std::forward(args)...); - } - - // Attempt to dequeue an item from head of queue. - void pop( T& result ) { - internal_pop(&result); - } - - /** Does not wait for item to become available. - Returns true if successful; false otherwise. */ - bool try_pop( T& result ) { - return internal_pop_if_present(&result); - } - - void abort() { - internal_abort(); - } - - // Return the number of items in the queue; thread unsafe - std::ptrdiff_t size() const { - return my_queue_representation->size(); - } - - void set_capacity( size_type new_capacity ) { - std::ptrdiff_t c = new_capacity < 0 ? infinite_capacity : new_capacity; - my_capacity = c; - } - - size_type capacity() const { - return my_capacity; - } - - // Equivalent to size()==0. - __TBB_nodiscard bool empty() const { - return my_queue_representation->empty(); - } - - // Clear the queue. not thread-safe. - void clear() { - my_queue_representation->clear(my_allocator); - } - - // Return allocator object - allocator_type get_allocator() const { return my_allocator; } - - //------------------------------------------------------------------------ - // The iterators are intended only for debugging. They are slow and not thread safe. - //------------------------------------------------------------------------ - - iterator unsafe_begin() { return concurrent_queue_iterator_provider::get(*this); } - iterator unsafe_end() { return iterator(); } - const_iterator unsafe_begin() const { return concurrent_queue_iterator_provider::get(*this); } - const_iterator unsafe_end() const { return const_iterator(); } - const_iterator unsafe_cbegin() const { return concurrent_queue_iterator_provider::get(*this); } - const_iterator unsafe_cend() const { return const_iterator(); } - -private: - void internal_swap( concurrent_bounded_queue& src ) { - std::swap(my_queue_representation, src.my_queue_representation); - std::swap(my_monitors, src.my_monitors); - } - - static constexpr std::ptrdiff_t infinite_capacity = std::ptrdiff_t(~size_type(0) / 2); - - template - void internal_push( Args&&... args ) { - unsigned old_abort_counter = my_abort_counter.load(std::memory_order_relaxed); - ticket_type ticket = my_queue_representation->tail_counter++; - std::ptrdiff_t target = ticket - my_capacity; - - if (static_cast(my_queue_representation->head_counter.load(std::memory_order_relaxed)) <= target) { // queue is full - auto pred = [&] { - if (my_abort_counter.load(std::memory_order_relaxed) != old_abort_counter) { - throw_exception(exception_id::user_abort); - } - - return static_cast(my_queue_representation->head_counter.load(std::memory_order_relaxed)) <= target; - }; - - try_call( [&] { - internal_wait(my_monitors, cbq_slots_avail_tag, target, pred); - }).on_exception( [&] { - my_queue_representation->choose(ticket).abort_push(ticket, *my_queue_representation, my_allocator); - }); - - } - __TBB_ASSERT((static_cast(my_queue_representation->head_counter.load(std::memory_order_relaxed)) > target), nullptr); - my_queue_representation->choose(ticket).push(ticket, *my_queue_representation, my_allocator, std::forward(args)...); - r1::notify_bounded_queue_monitor(my_monitors, cbq_items_avail_tag, ticket); - } - - template - bool internal_push_if_not_full( Args&&... args ) { - ticket_type ticket = my_queue_representation->tail_counter.load(std::memory_order_relaxed); - do { - if (static_cast(ticket - my_queue_representation->head_counter.load(std::memory_order_relaxed)) >= my_capacity) { - // Queue is full - return false; - } - // Queue had empty slot with ticket k when we looked. Attempt to claim that slot. - // Another thread claimed the slot, so retry. - } while (!my_queue_representation->tail_counter.compare_exchange_strong(ticket, ticket + 1)); - - my_queue_representation->choose(ticket).push(ticket, *my_queue_representation, my_allocator, std::forward(args)...); - r1::notify_bounded_queue_monitor(my_monitors, cbq_items_avail_tag, ticket); - return true; - } - - void internal_pop( void* dst ) { - std::ptrdiff_t target; - // This loop is a single pop operation; abort_counter should not be re-read inside - unsigned old_abort_counter = my_abort_counter.load(std::memory_order_relaxed); - - do { - target = my_queue_representation->head_counter++; - if (static_cast(my_queue_representation->tail_counter.load(std::memory_order_relaxed)) <= target) { - auto pred = [&] { - if (my_abort_counter.load(std::memory_order_relaxed) != old_abort_counter) { - throw_exception(exception_id::user_abort); - } - - return static_cast(my_queue_representation->tail_counter.load(std::memory_order_relaxed)) <= target; - }; - - try_call( [&] { - internal_wait(my_monitors, cbq_items_avail_tag, target, pred); - }).on_exception( [&] { - my_queue_representation->head_counter--; - }); - } - __TBB_ASSERT(static_cast(my_queue_representation->tail_counter.load(std::memory_order_relaxed)) > target, nullptr); - } while (!my_queue_representation->choose(target).pop(dst, target, *my_queue_representation, my_allocator)); - - r1::notify_bounded_queue_monitor(my_monitors, cbq_slots_avail_tag, target); - } - - bool internal_pop_if_present( void* dst ) { - bool present{}; - ticket_type ticket{}; - std::tie(present, ticket) = internal_try_pop_impl(dst, *my_queue_representation, my_allocator); - - if (present) { - r1::notify_bounded_queue_monitor(my_monitors, cbq_slots_avail_tag, ticket); - } - return present; - } - - void internal_abort() { - ++my_abort_counter; - r1::abort_bounded_queue_monitors(my_monitors); - } - - static void copy_construct_item(T* location, const void* src) { - // TODO: use allocator_traits for copy construction - new (location) value_type(*static_cast(src)); - } - - static void move_construct_item(T* location, const void* src) { - // TODO: use allocator_traits for move construction - new (location) value_type(std::move(*static_cast(const_cast(src)))); - } - - template - friend class concurrent_queue_iterator; - - queue_allocator_type my_allocator; - std::ptrdiff_t my_capacity; - std::atomic my_abort_counter; - queue_representation_type* my_queue_representation; - - r1::concurrent_monitor* my_monitors; - - friend void swap( concurrent_bounded_queue& lhs, concurrent_bounded_queue& rhs ) { - lhs.swap(rhs); - } - - friend bool operator==( const concurrent_bounded_queue& lhs, const concurrent_bounded_queue& rhs ) { - return lhs.size() == rhs.size() && std::equal(lhs.unsafe_begin(), lhs.unsafe_end(), rhs.unsafe_begin()); - } - -#if !__TBB_CPP20_COMPARISONS_PRESENT - friend bool operator!=( const concurrent_bounded_queue& lhs, const concurrent_bounded_queue& rhs ) { - return !(lhs == rhs); - } -#endif // __TBB_CPP20_COMPARISONS_PRESENT -}; // class concurrent_bounded_queue - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -// Deduction guide for the constructor from two iterators -template >> -concurrent_bounded_queue( It, It, Alloc = Alloc() ) --> concurrent_bounded_queue, Alloc>; - -#endif /* __TBB_CPP17_DEDUCTION_GUIDES_PRESENT */ - -} //namespace d2 -} // namesapce detail - -inline namespace v1 { - -using detail::d2::concurrent_queue; -using detail::d2::concurrent_bounded_queue; -using detail::r1::user_abort; -using detail::r1::bad_last_alloc; - -} // inline namespace v1 -} // namespace tbb - -#endif // __TBB_concurrent_queue_H diff --git a/include/oneapi/tbb/concurrent_set.h b/include/oneapi/tbb/concurrent_set.h deleted file mode 100644 index dd14331..0000000 --- a/include/oneapi/tbb/concurrent_set.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - Copyright (c) 2019-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_concurrent_set_H -#define __TBB_concurrent_set_H - -#include "detail/_namespace_injection.h" -#include "detail/_concurrent_skip_list.h" -#include "tbb_allocator.h" -#include -#include - -namespace tbb { -namespace detail { -namespace d2 { - -template -struct set_traits { - static constexpr std::size_t max_level = RandomGenerator::max_level; - using random_level_generator_type = RandomGenerator; - using key_type = Key; - using value_type = key_type; - using compare_type = KeyCompare; - using value_compare = compare_type; - using reference = value_type&; - using const_reference = const value_type&; - using allocator_type = Allocator; - - static constexpr bool allow_multimapping = AllowMultimapping; - - static const key_type& get_key(const_reference val) { - return val; - } - - static value_compare value_comp(compare_type comp) { return comp; } -}; // struct set_traits - -template -class concurrent_multiset; - -template , typename Allocator = tbb::tbb_allocator> -class concurrent_set : public concurrent_skip_list, Allocator, false>> { - using base_type = concurrent_skip_list, Allocator, false>>; -public: - using key_type = Key; - using value_type = typename base_type::value_type; - using size_type = typename base_type::size_type; - using difference_type = typename base_type::difference_type; - using key_compare = Compare; - using value_compare = typename base_type::value_compare; - using allocator_type = Allocator; - - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - - using node_type = typename base_type::node_type; - - // Include constructors of base_type - using base_type::base_type; - - // Required for implicit deduction guides - concurrent_set() = default; - concurrent_set( const concurrent_set& ) = default; - concurrent_set( const concurrent_set& other, const allocator_type& alloc ) : base_type(other, alloc) {} - concurrent_set( concurrent_set&& ) = default; - concurrent_set( concurrent_set&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {} - // Required to respect the rule of 5 - concurrent_set& operator=( const concurrent_set& ) = default; - concurrent_set& operator=( concurrent_set&& ) = default; - - concurrent_set& operator=( std::initializer_list il ) { - base_type::operator= (il); - return *this; - } - - template - void merge(concurrent_set& source) { - this->internal_merge(source); - } - - template - void merge(concurrent_set&& source) { - this->internal_merge(std::move(source)); - } - - template - void merge(concurrent_multiset& source) { - this->internal_merge(source); - } - - template - void merge(concurrent_multiset&& source) { - this->internal_merge(std::move(source)); - } -}; // class concurrent_set - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template >, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_set( It, It, Comp = Comp(), Alloc = Alloc() ) --> concurrent_set, Comp, Alloc>; - -template , - typename Alloc = tbb::tbb_allocator, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_set( std::initializer_list, Comp = Comp(), Alloc = Alloc() ) --> concurrent_set; - -template >, - typename = std::enable_if_t>> -concurrent_set( It, It, Alloc ) --> concurrent_set, - std::less>, Alloc>; - -template >> -concurrent_set( std::initializer_list, Alloc ) --> concurrent_set, Alloc>; - -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template -void swap( concurrent_set& lhs, - concurrent_set& rhs ) -{ - lhs.swap(rhs); -} - -template , typename Allocator = tbb::tbb_allocator> -class concurrent_multiset : public concurrent_skip_list, Allocator, true>> { - using base_type = concurrent_skip_list, Allocator, true>>; -public: - using key_type = Key; - using value_type = typename base_type::value_type; - using size_type = typename base_type::size_type; - using difference_type = typename base_type::difference_type; - using key_compare = Compare; - using value_compare = typename base_type::value_compare; - using allocator_type = Allocator; - - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - - using node_type = typename base_type::node_type; - - // Include constructors of base_type; - using base_type::base_type; - - // Required for implicit deduction guides - concurrent_multiset() = default; - concurrent_multiset( const concurrent_multiset& ) = default; - concurrent_multiset( const concurrent_multiset& other, const allocator_type& alloc ) : base_type(other, alloc) {} - concurrent_multiset( concurrent_multiset&& ) = default; - concurrent_multiset( concurrent_multiset&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {} - // Required to respect the rule of 5 - concurrent_multiset& operator=( const concurrent_multiset& ) = default; - concurrent_multiset& operator=( concurrent_multiset&& ) = default; - - concurrent_multiset& operator=( std::initializer_list il ) { - base_type::operator= (il); - return *this; - } - - template - void merge(concurrent_set& source) { - this->internal_merge(source); - } - - template - void merge(concurrent_set&& source) { - this->internal_merge(std::move(source)); - } - - template - void merge(concurrent_multiset& source) { - this->internal_merge(source); - } - - template - void merge(concurrent_multiset&& source) { - this->internal_merge(std::move(source)); - } -}; // class concurrent_multiset - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template >, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_multiset( It, It, Comp = Comp(), Alloc = Alloc() ) --> concurrent_multiset, Comp, Alloc>; - -template , - typename Alloc = tbb::tbb_allocator, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_multiset( std::initializer_list, Comp = Comp(), Alloc = Alloc() ) --> concurrent_multiset; - -template >, - typename = std::enable_if_t>> -concurrent_multiset( It, It, Alloc ) --> concurrent_multiset, std::less>, Alloc>; - -template >> -concurrent_multiset( std::initializer_list, Alloc ) --> concurrent_multiset, Alloc>; - -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template -void swap( concurrent_multiset& lhs, - concurrent_multiset& rhs ) -{ - lhs.swap(rhs); -} - -} // namespace d2 -} // namespace detail - -inline namespace v1 { - -using detail::d2::concurrent_set; -using detail::d2::concurrent_multiset; -using detail::split; - -} // inline namespace v1 -} // namespace tbb - -#endif // __TBB_concurrent_set_H diff --git a/include/oneapi/tbb/concurrent_unordered_map.h b/include/oneapi/tbb/concurrent_unordered_map.h deleted file mode 100644 index 336425c..0000000 --- a/include/oneapi/tbb/concurrent_unordered_map.h +++ /dev/null @@ -1,414 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_concurrent_unordered_map_H -#define __TBB_concurrent_unordered_map_H - -#include "detail/_namespace_injection.h" -#include "detail/_concurrent_unordered_base.h" -#include "tbb_allocator.h" -#include - -namespace tbb { -namespace detail { -namespace d1 { - -template -struct concurrent_unordered_map_traits { - using value_type = std::pair; - using key_type = Key; - using allocator_type = Allocator; - using hash_compare_type = hash_compare; - static constexpr bool allow_multimapping = AllowMultimapping; - - static constexpr const key_type& get_key( const value_type& value ) { - return value.first; - } -}; // struct concurrent_unordered_map_traits - -template -class concurrent_unordered_multimap; - -template , typename KeyEqual = std::equal_to, - typename Allocator = tbb::tbb_allocator> > -class concurrent_unordered_map - : public concurrent_unordered_base> -{ - using traits_type = concurrent_unordered_map_traits; - using base_type = concurrent_unordered_base; -public: - using key_type = typename base_type::key_type; - using mapped_type = T; - using value_type = typename base_type::value_type; - using size_type = typename base_type::size_type; - using difference_type = typename base_type::difference_type; - using hasher = typename base_type::hasher; - using key_equal = typename base_type::key_equal; - using allocator_type = typename base_type::allocator_type; - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - using local_iterator = typename base_type::local_iterator; - using const_local_iterator = typename base_type::const_local_iterator; - using node_type = typename base_type::node_type; - - // Include constructors of base type - using base_type::base_type; - - // Required for implicit deduction guides - concurrent_unordered_map() = default; - concurrent_unordered_map( const concurrent_unordered_map& ) = default; - concurrent_unordered_map( const concurrent_unordered_map& other, const allocator_type& alloc ) : base_type(other, alloc) {} - concurrent_unordered_map( concurrent_unordered_map&& ) = default; - concurrent_unordered_map( concurrent_unordered_map&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {} - // Required to respect the rule of 5 - concurrent_unordered_map& operator=( const concurrent_unordered_map& ) = default; - concurrent_unordered_map& operator=( concurrent_unordered_map&& ) = default; - - concurrent_unordered_map& operator=( std::initializer_list il ) { - base_type::operator= (il); - return *this; - } - - // Observers - mapped_type& operator[]( const key_type& key ) { - iterator where = this->find(key); - - if (where == this->end()) { - where = this->emplace(std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>()).first; - } - return where->second; - } - - mapped_type& operator[]( key_type&& key ) { - iterator where = this->find(key); - - if (where == this->end()) { - where = this->emplace(std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>()).first; - } - return where->second; - } - - mapped_type& at( const key_type& key ) { - iterator where = this->find(key); - - if (where == this->end()) { - throw_exception(exception_id::invalid_key); - } - return where->second; - } - - const mapped_type& at( const key_type& key ) const { - const_iterator where = this->find(key); - - if (where == this->end()) { - throw_exception(exception_id::out_of_range); - } - return where->second; - } - - using base_type::insert; - - template - typename std::enable_if::value, - std::pair>::type insert( P&& value ) { - return this->emplace(std::forward

(value)); - } - - template - typename std::enable_if::value, - iterator>::type insert( const_iterator hint, P&& value ) { - return this->emplace_hint(hint, std::forward

(value)); - } - - template - void merge( concurrent_unordered_map& source ) { - this->internal_merge(source); - } - - template - void merge( concurrent_unordered_map&& source ) { - this->internal_merge(std::move(source)); - } - - template - void merge( concurrent_unordered_multimap& source ) { - this->internal_merge(source); - } - - template - void merge( concurrent_unordered_multimap&& source ) { - this->internal_merge(std::move(source)); - } -}; // class concurrent_unordered_map - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -template >, - typename KeyEq = std::equal_to>, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_map( It, It, std::size_t = {}, - Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() ) --> concurrent_unordered_map, iterator_mapped_t, Hash, KeyEq, Alloc>; - -template >, - typename KeyEq = std::equal_to>, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_map( std::initializer_list>, std::size_t = {}, - Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() ) --> concurrent_unordered_map, T, Hash, KeyEq, Alloc>; - -template >, - typename = std::enable_if_t>> -concurrent_unordered_map( It, It, std::size_t, Alloc ) --> concurrent_unordered_map, iterator_mapped_t, - std::hash>, - std::equal_to>, Alloc>; - -// TODO: investigate if a deduction guide for concurrent_unordered_map(It, It, Alloc) is needed - -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_map( It, It, std::size_t, Hash, Alloc ) --> concurrent_unordered_map, iterator_mapped_t, - Hash, std::equal_to>, Alloc>; - -template >> -concurrent_unordered_map( std::initializer_list>, std::size_t, Alloc ) --> concurrent_unordered_map, T, std::hash>, - std::equal_to>, Alloc>; - -template >> -concurrent_unordered_map( std::initializer_list>, Alloc ) --> concurrent_unordered_map, T, std::hash>, - std::equal_to>, Alloc>; - -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_map( std::initializer_list>, std::size_t, Hash, Alloc ) --> concurrent_unordered_map, T, Hash, - std::equal_to>, Alloc>; - -#if __APPLE__ && __TBB_CLANG_VERSION == 100000 -// An explicit deduction guide is required for copy/move constructor with allocator for APPLE LLVM 10.0.0 -// due to an issue with generating an implicit deduction guide for these constructors under several strange surcumstances. -// Currently the issue takes place because the last template parameter for Traits is boolean, it should not affect the deduction guides -// The issue reproduces only on this version of the compiler -template -concurrent_unordered_map( concurrent_unordered_map, Alloc ) --> concurrent_unordered_map; -#endif - -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template -void swap( concurrent_unordered_map& lhs, - concurrent_unordered_map& rhs ) { - lhs.swap(rhs); -} - -template , typename KeyEqual = std::equal_to, - typename Allocator = tbb::tbb_allocator> > -class concurrent_unordered_multimap - : public concurrent_unordered_base> -{ - using traits_type = concurrent_unordered_map_traits; - using base_type = concurrent_unordered_base; -public: - using key_type = typename base_type::key_type; - using mapped_type = T; - using value_type = typename base_type::value_type; - using size_type = typename base_type::size_type; - using difference_type = typename base_type::difference_type; - using hasher = typename base_type::hasher; - using key_equal = typename base_type::key_equal; - using allocator_type = typename base_type::allocator_type; - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - using local_iterator = typename base_type::local_iterator; - using const_local_iterator = typename base_type::const_local_iterator; - using node_type = typename base_type::node_type; - - // Include constructors of base type - using base_type::base_type; - using base_type::insert; - - // Required for implicit deduction guides - concurrent_unordered_multimap() = default; - concurrent_unordered_multimap( const concurrent_unordered_multimap& ) = default; - concurrent_unordered_multimap( const concurrent_unordered_multimap& other, const allocator_type& alloc ) : base_type(other, alloc) {} - concurrent_unordered_multimap( concurrent_unordered_multimap&& ) = default; - concurrent_unordered_multimap( concurrent_unordered_multimap&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {} - // Required to respect the rule of 5 - concurrent_unordered_multimap& operator=( const concurrent_unordered_multimap& ) = default; - concurrent_unordered_multimap& operator=( concurrent_unordered_multimap&& ) = default; - - concurrent_unordered_multimap& operator=( std::initializer_list il ) { - base_type::operator= (il); - return *this; - } - - template - typename std::enable_if::value, - std::pair>::type insert( P&& value ) { - return this->emplace(std::forward

(value)); - } - - template - typename std::enable_if::value, - iterator>::type insert( const_iterator hint, P&& value ) { - return this->emplace_hint(hint, std::forward(value)); - } - - template - void merge( concurrent_unordered_map& source ) { - this->internal_merge(source); - } - - template - void merge( concurrent_unordered_map&& source ) { - this->internal_merge(std::move(source)); - } - - template - void merge( concurrent_unordered_multimap& source ) { - this->internal_merge(source); - } - - template - void merge( concurrent_unordered_multimap&& source ) { - this->internal_merge(std::move(source)); - } -}; // class concurrent_unordered_multimap - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template >, - typename KeyEq = std::equal_to>, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_multimap( It, It, std::size_t = {}, Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() ) --> concurrent_unordered_multimap, iterator_mapped_t, Hash, KeyEq, Alloc>; - -template >, - typename KeyEq = std::equal_to>, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_multimap( std::initializer_list>, std::size_t = {}, - Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() ) --> concurrent_unordered_multimap, T, Hash, KeyEq, Alloc>; - -template >, - typename = std::enable_if_t>> -concurrent_unordered_multimap( It, It, std::size_t, Alloc ) --> concurrent_unordered_multimap, iterator_mapped_t, - std::hash>, - std::equal_to>, Alloc>; - -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_multimap( It, It, std::size_t, Hash, Alloc ) --> concurrent_unordered_multimap, iterator_mapped_t, Hash, - std::equal_to>, Alloc>; - -template >> -concurrent_unordered_multimap( std::initializer_list>, std::size_t, Alloc ) --> concurrent_unordered_multimap, T, std::hash>, - std::equal_to>, Alloc>; - -template >> -concurrent_unordered_multimap( std::initializer_list>, Alloc ) --> concurrent_unordered_multimap, T, std::hash>, - std::equal_to>, Alloc>; - -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_multimap( std::initializer_list>, std::size_t, Hash, Alloc ) --> concurrent_unordered_multimap, T, Hash, - std::equal_to>, Alloc>; - -#if __APPLE__ && __TBB_CLANG_VERSION == 100000 -// An explicit deduction guide is required for copy/move constructor with allocator for APPLE LLVM 10.0.0 -// due to an issue with generating an implicit deduction guide for these constructors under several strange surcumstances. -// Currently the issue takes place because the last template parameter for Traits is boolean, it should not affect the deduction guides -// The issue reproduces only on this version of the compiler -template -concurrent_unordered_multimap( concurrent_unordered_multimap, Alloc ) --> concurrent_unordered_multimap; -#endif -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template -void swap( concurrent_unordered_multimap& lhs, - concurrent_unordered_multimap& rhs ) { - lhs.swap(rhs); -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { - -using detail::d1::concurrent_unordered_map; -using detail::d1::concurrent_unordered_multimap; -using detail::split; - -} // inline namespace v1 -} // namespace tbb - -#endif // __TBB_concurrent_unordered_map_H diff --git a/include/oneapi/tbb/concurrent_unordered_set.h b/include/oneapi/tbb/concurrent_unordered_set.h deleted file mode 100644 index c135b92..0000000 --- a/include/oneapi/tbb/concurrent_unordered_set.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_concurrent_unordered_set_H -#define __TBB_concurrent_unordered_set_H - -#include "detail/_namespace_injection.h" -#include "detail/_concurrent_unordered_base.h" -#include "tbb_allocator.h" - -namespace tbb { -namespace detail { -namespace d1 { - -template -struct concurrent_unordered_set_traits { - using key_type = Key; - using value_type = key_type; - using allocator_type = Allocator; - using hash_compare_type = hash_compare; - static constexpr bool allow_multimapping = AllowMultimapping; - - static constexpr const key_type& get_key( const value_type& value ) { - return value; - } -}; // class concurrent_unordered_set_traits - -template -class concurrent_unordered_multiset; - -template , typename KeyEqual = std::equal_to, - typename Allocator = tbb::tbb_allocator> -class concurrent_unordered_set - : public concurrent_unordered_base> -{ - using traits_type = concurrent_unordered_set_traits; - using base_type = concurrent_unordered_base; -public: - using key_type = typename base_type::key_type; - using value_type = typename base_type::value_type; - using size_type = typename base_type::size_type; - using difference_type = typename base_type::difference_type; - using hasher = typename base_type::hasher; - using key_equal = typename base_type::key_equal; - using allocator_type = typename base_type::allocator_type; - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - using local_iterator = typename base_type::local_iterator; - using const_local_iterator = typename base_type::const_local_iterator; - using node_type = typename base_type::node_type; - - // Include constructors of base_type; - using base_type::base_type; - - // Required for implicit deduction guides - concurrent_unordered_set() = default; - concurrent_unordered_set( const concurrent_unordered_set& ) = default; - concurrent_unordered_set( const concurrent_unordered_set& other, const allocator_type& alloc ) : base_type(other, alloc) {} - concurrent_unordered_set( concurrent_unordered_set&& ) = default; - concurrent_unordered_set( concurrent_unordered_set&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {} - // Required to respect the rule of 5 - concurrent_unordered_set& operator=( const concurrent_unordered_set& ) = default; - concurrent_unordered_set& operator=( concurrent_unordered_set&& ) = default; - - concurrent_unordered_set& operator=( std::initializer_list il ) { - base_type::operator= (il); - return *this; - } - - template - void merge( concurrent_unordered_set& source ) { - this->internal_merge(source); - } - - template - void merge( concurrent_unordered_set&& source ) { - this->internal_merge(std::move(source)); - } - - template - void merge( concurrent_unordered_multiset& source ) { - this->internal_merge(source); - } - - template - void merge( concurrent_unordered_multiset&& source ) { - this->internal_merge(std::move(source)); - } -}; // class concurrent_unordered_set - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template >, - typename KeyEq = std::equal_to>, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_set( It, It, std::size_t = {}, Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() ) --> concurrent_unordered_set, Hash, KeyEq, Alloc>; - -template , - typename KeyEq = std::equal_to, - typename Alloc = tbb::tbb_allocator, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_set( std::initializer_list, std::size_t = {}, - Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() ) --> concurrent_unordered_set; - -template >, - typename = std::enable_if_t>> -concurrent_unordered_set( It, It, std::size_t, Alloc ) --> concurrent_unordered_set, std::hash>, - std::equal_to>, Alloc>; - -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_set( It, It, std::size_t, Hash, Alloc ) --> concurrent_unordered_set, Hash, std::equal_to>, Alloc>; - -template >> -concurrent_unordered_set( std::initializer_list, std::size_t, Alloc ) --> concurrent_unordered_set, std::equal_to, Alloc>; - -template >> -concurrent_unordered_set( std::initializer_list, Alloc ) --> concurrent_unordered_set, std::equal_to, Alloc>; - -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_set( std::initializer_list, std::size_t, Hash, Alloc ) --> concurrent_unordered_set, Alloc>; - -#if __APPLE__ && __TBB_CLANG_VERSION == 100000 -// An explicit deduction guide is required for copy/move constructor with allocator for APPLE LLVM 10.0.0 -// due to an issue with generating an implicit deduction guide for these constructors under several strange surcumstances. -// Currently the issue takes place because the last template parameter for Traits is boolean, it should not affect the deduction guides -// The issue reproduces only on this version of the compiler -template -concurrent_unordered_set( concurrent_unordered_set, Alloc ) --> concurrent_unordered_set; -#endif -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template -void swap( concurrent_unordered_set& lhs, - concurrent_unordered_set& rhs ) { - lhs.swap(rhs); -} - -template , typename KeyEqual = std::equal_to, - typename Allocator = tbb::tbb_allocator> -class concurrent_unordered_multiset - : public concurrent_unordered_base> -{ - using traits_type = concurrent_unordered_set_traits; - using base_type = concurrent_unordered_base; -public: - using key_type = typename base_type::key_type; - using value_type = typename base_type::value_type; - using size_type = typename base_type::size_type; - using difference_type = typename base_type::difference_type; - using hasher = typename base_type::hasher; - using key_equal = typename base_type::key_equal; - using allocator_type = typename base_type::allocator_type; - using reference = typename base_type::reference; - using const_reference = typename base_type::const_reference; - using pointer = typename base_type::pointer; - using const_pointer = typename base_type::const_pointer; - using iterator = typename base_type::iterator; - using const_iterator = typename base_type::const_iterator; - using local_iterator = typename base_type::local_iterator; - using const_local_iterator = typename base_type::const_local_iterator; - using node_type = typename base_type::node_type; - - // Include constructors of base_type; - using base_type::base_type; - - // Required for implicit deduction guides - concurrent_unordered_multiset() = default; - concurrent_unordered_multiset( const concurrent_unordered_multiset& ) = default; - concurrent_unordered_multiset( const concurrent_unordered_multiset& other, const allocator_type& alloc ) : base_type(other, alloc) {} - concurrent_unordered_multiset( concurrent_unordered_multiset&& ) = default; - concurrent_unordered_multiset( concurrent_unordered_multiset&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {} - // Required to respect the rule of 5 - concurrent_unordered_multiset& operator=( const concurrent_unordered_multiset& ) = default; - concurrent_unordered_multiset& operator=( concurrent_unordered_multiset&& ) = default; - - concurrent_unordered_multiset& operator=( std::initializer_list il ) { - base_type::operator= (il); - return *this; - } - - template - void merge( concurrent_unordered_set& source ) { - this->internal_merge(source); - } - - template - void merge( concurrent_unordered_set&& source ) { - this->internal_merge(std::move(source)); - } - - template - void merge( concurrent_unordered_multiset& source ) { - this->internal_merge(source); - } - - template - void merge( concurrent_unordered_multiset&& source ) { - this->internal_merge(std::move(source)); - } -}; // class concurrent_unordered_multiset - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -template >, - typename KeyEq = std::equal_to>, - typename Alloc = tbb::tbb_allocator>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_multiset( It, It, std::size_t = {}, Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() ) --> concurrent_unordered_multiset, Hash, KeyEq, Alloc>; - -template , - typename KeyEq = std::equal_to, - typename Alloc = tbb::tbb_allocator, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_multiset( std::initializer_list, std::size_t = {}, - Hash = Hash(), KeyEq = KeyEq(), Alloc = Alloc() ) --> concurrent_unordered_multiset; - -template >, - typename = std::enable_if_t>> -concurrent_unordered_multiset( It, It, std::size_t, Alloc ) --> concurrent_unordered_multiset, std::hash>, - std::equal_to>, Alloc>; - -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_multiset( It, It, std::size_t, Hash, Alloc ) --> concurrent_unordered_multiset, Hash, std::equal_to>, Alloc>; - -template >> -concurrent_unordered_multiset( std::initializer_list, std::size_t, Alloc ) --> concurrent_unordered_multiset, std::equal_to, Alloc>; - -template >> -concurrent_unordered_multiset( std::initializer_list, Alloc ) --> concurrent_unordered_multiset, std::equal_to, Alloc>; - -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_unordered_multiset( std::initializer_list, std::size_t, Hash, Alloc ) --> concurrent_unordered_multiset, Alloc>; - -#if __APPLE__ && __TBB_CLANG_VERSION == 100000 -// An explicit deduction guide is required for copy/move constructor with allocator for APPLE LLVM 10.0.0 -// due to an issue with generating an implicit deduction guide for these constructors under several strange surcumstances. -// Currently the issue takes place because the last template parameter for Traits is boolean, it should not affect the deduction guides -// The issue reproduces only on this version of the compiler -template -concurrent_unordered_multiset( concurrent_unordered_multiset, Alloc ) --> concurrent_unordered_multiset; -#endif -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -template -void swap( concurrent_unordered_multiset& lhs, - concurrent_unordered_multiset& rhs ) { - lhs.swap(rhs); -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { - -using detail::d1::concurrent_unordered_set; -using detail::d1::concurrent_unordered_multiset; -using detail::split; - -} // inline namespace v1 -} // namespace tbb - -#endif // __TBB_concurrent_unordered_set_H diff --git a/include/oneapi/tbb/concurrent_vector.h b/include/oneapi/tbb/concurrent_vector.h deleted file mode 100644 index 2a2cb1e..0000000 --- a/include/oneapi/tbb/concurrent_vector.h +++ /dev/null @@ -1,1129 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_concurrent_vector_H -#define __TBB_concurrent_vector_H - -#include "detail/_namespace_injection.h" -#include "detail/_utils.h" -#include "detail/_assert.h" -#include "detail/_allocator_traits.h" -#include "detail/_segment_table.h" -#include "detail/_containers_helpers.h" -#include "blocked_range.h" -#include "cache_aligned_allocator.h" - -#include -#include // std::move_if_noexcept -#include -#if __TBB_CPP20_COMPARISONS_PRESENT -#include -#endif - -namespace tbb { -namespace detail { -namespace d1 { - -template -class vector_iterator { - using vector_type = Vector; - -public: - using value_type = Value; - using size_type = typename vector_type::size_type; - using difference_type = typename vector_type::difference_type; - using pointer = value_type*; - using reference = value_type&; - using iterator_category = std::random_access_iterator_tag; - - template - friend vector_iterator operator+( typename vector_iterator::difference_type, const vector_iterator& ); - - template - friend typename vector_iterator::difference_type operator-( const vector_iterator&, const vector_iterator& ); - - template - friend bool operator==( const vector_iterator&, const vector_iterator& ); - - template - friend bool operator<( const vector_iterator&, const vector_iterator& ); - - template - friend class vector_iterator; - - template - friend class concurrent_vector; - -private: - vector_iterator( const vector_type& vector, size_type index, value_type* item = nullptr ) - : my_vector(const_cast(&vector)), my_index(index), my_item(item) - {} - -public: - vector_iterator() : my_vector(nullptr), my_index(~size_type(0)), my_item(nullptr) - {} - - vector_iterator( const vector_iterator& other ) - : my_vector(other.my_vector), my_index(other.my_index), my_item(other.my_item) - {} - - vector_iterator& operator=( const vector_iterator& other ) { - my_vector = other.my_vector; - my_index = other.my_index; - my_item = other.my_item; - return *this; - } - - vector_iterator operator+( difference_type offset ) const { - return vector_iterator(*my_vector, my_index + offset); - } - - vector_iterator& operator+=( difference_type offset ) { - my_index += offset; - my_item = nullptr; - return *this; - } - - vector_iterator operator-( difference_type offset ) const { - return vector_iterator(*my_vector, my_index - offset); - } - - vector_iterator& operator-=( difference_type offset ) { - my_index -= offset; - my_item = nullptr; - return *this; - } - - reference operator*() const { - value_type *item = my_item; - if (item == nullptr) { - item = &my_vector->internal_subscript(my_index); - } else { - __TBB_ASSERT(item == &my_vector->internal_subscript(my_index), "corrupt cache"); - } - return *item; - } - - pointer operator->() const { return &(operator*()); } - - reference operator[]( difference_type k ) const { - return my_vector->internal_subscript(my_index + k); - } - - vector_iterator& operator++() { - ++my_index; - if (my_item != nullptr) { - if (vector_type::is_first_element_in_segment(my_index)) { - // If the iterator crosses a segment boundary, the pointer become invalid - // as possibly next segment is in another memory location - my_item = nullptr; - } else { - ++my_item; - } - } - return *this; - } - - vector_iterator operator++(int) { - vector_iterator result = *this; - ++(*this); - return result; - } - - vector_iterator& operator--() { - __TBB_ASSERT(my_index > 0, "operator--() applied to iterator already at beginning of concurrent_vector"); - --my_index; - if (my_item != nullptr) { - if (vector_type::is_first_element_in_segment(my_index)) { - // If the iterator crosses a segment boundary, the pointer become invalid - // as possibly next segment is in another memory location - my_item = nullptr; - } else { - --my_item; - } - } - return *this; - } - - vector_iterator operator--(int) { - vector_iterator result = *this; - --(*this); - return result; - } - -private: - // concurrent_vector over which we are iterating. - vector_type* my_vector; - - // Index into the vector - size_type my_index; - - // Caches my_vector *it; - // If my_item == nullptr cached value is not available use internal_subscript(my_index) - mutable value_type* my_item; -}; // class vector_iterator - -template -vector_iterator operator+( typename vector_iterator::difference_type offset, - const vector_iterator& v ) -{ - return vector_iterator(*v.my_vector, v.my_index + offset); -} - -template -typename vector_iterator::difference_type operator-( const vector_iterator& i, - const vector_iterator& j ) -{ - using difference_type = typename vector_iterator::difference_type; - return static_cast(i.my_index) - static_cast(j.my_index); -} - -template -bool operator==( const vector_iterator& i, const vector_iterator& j ) { - return i.my_vector == j.my_vector && i.my_index == j.my_index; -} - -template -bool operator!=( const vector_iterator& i, const vector_iterator& j ) { - return !(i == j); -} - -template -bool operator<( const vector_iterator& i, const vector_iterator& j ) { - return i.my_index < j.my_index; -} - -template -bool operator>( const vector_iterator& i, const vector_iterator& j ) { - return j < i; -} - -template -bool operator>=( const vector_iterator& i, const vector_iterator& j ) { - return !(i < j); -} - -template -bool operator<=( const vector_iterator& i, const vector_iterator& j ) { - return !(j < i); -} - -static constexpr std::size_t embedded_table_num_segments = 3; - -template > -class concurrent_vector - : private segment_table, embedded_table_num_segments> -{ - using self_type = concurrent_vector; - using base_type = segment_table; - - friend class segment_table; - - template - class generic_range_type : public tbb::blocked_range { - using base_type = tbb::blocked_range; - public: - using value_type = T; - using reference = T&; - using const_reference = const T&; - using iterator = Iterator; - using difference_type = std::ptrdiff_t; - - using base_type::base_type; - - template - generic_range_type( const generic_range_type& r) : blocked_range(r.begin(), r.end(), r.grainsize()) {} - generic_range_type( generic_range_type& r, split ) : blocked_range(r, split()) {} - }; // class generic_range_type - - static_assert(std::is_same::value, - "value_type of the container must be the same as its allocator's"); - using allocator_traits_type = tbb::detail::allocator_traits; - // Segment table for concurrent_vector can be extended - static constexpr bool allow_table_extending = true; - static constexpr bool is_noexcept_assignment = allocator_traits_type::propagate_on_container_move_assignment::value || - allocator_traits_type::is_always_equal::value; - static constexpr bool is_noexcept_swap = allocator_traits_type::propagate_on_container_swap::value || - allocator_traits_type::is_always_equal::value; - -public: - using value_type = T; - using allocator_type = Allocator; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using reference = value_type&; - using const_reference = const value_type&; - - using pointer = typename allocator_traits_type::pointer; - using const_pointer = typename allocator_traits_type::const_pointer; - - using iterator = vector_iterator; - using const_iterator = vector_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - using range_type = generic_range_type; - using const_range_type = generic_range_type; - - concurrent_vector() : concurrent_vector(allocator_type()) {} - - explicit concurrent_vector( const allocator_type& alloc ) noexcept - : base_type(alloc) - {} - - explicit concurrent_vector( size_type count, const value_type& value, - const allocator_type& alloc = allocator_type() ) - : concurrent_vector(alloc) - { - try_call( [&] { - grow_by(count, value); - } ).on_exception( [&] { - base_type::clear(); - }); - } - - explicit concurrent_vector( size_type count, const allocator_type& alloc = allocator_type() ) - : concurrent_vector(alloc) - { - try_call( [&] { - grow_by(count); - } ).on_exception( [&] { - base_type::clear(); - }); - } - - template - concurrent_vector( InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type() ) - : concurrent_vector(alloc) - { - try_call( [&] { - grow_by(first, last); - } ).on_exception( [&] { - base_type::clear(); - }); - } - - concurrent_vector( const concurrent_vector& other ) - : base_type(segment_table_allocator_traits::select_on_container_copy_construction(other.get_allocator())) - { - try_call( [&] { - grow_by(other.begin(), other.end()); - } ).on_exception( [&] { - base_type::clear(); - }); - } - - concurrent_vector( const concurrent_vector& other, const allocator_type& alloc ) - : base_type(other, alloc) {} - - concurrent_vector(concurrent_vector&& other) noexcept - : base_type(std::move(other)) - {} - - concurrent_vector( concurrent_vector&& other, const allocator_type& alloc ) - : base_type(std::move(other), alloc) - {} - - concurrent_vector( std::initializer_list init, - const allocator_type& alloc = allocator_type() ) - : concurrent_vector(init.begin(), init.end(), alloc) - {} - - ~concurrent_vector() {} - - // Assignment - concurrent_vector& operator=( const concurrent_vector& other ) { - base_type::operator=(other); - return *this; - } - - concurrent_vector& operator=( concurrent_vector&& other ) noexcept(is_noexcept_assignment) { - base_type::operator=(std::move(other)); - return *this; - } - - concurrent_vector& operator=( std::initializer_list init ) { - assign(init); - return *this; - } - - void assign( size_type count, const value_type& value ) { - destroy_elements(); - grow_by(count, value); - } - - template - typename std::enable_if::value, void>::type - assign( InputIterator first, InputIterator last ) { - destroy_elements(); - grow_by(first, last); - } - - void assign( std::initializer_list init ) { - destroy_elements(); - assign(init.begin(), init.end()); - } - - // Concurrent growth - iterator grow_by( size_type delta ) { - return internal_grow_by_delta(delta); - } - - iterator grow_by( size_type delta, const value_type& value ) { - return internal_grow_by_delta(delta, value); - } - - template - typename std::enable_if::value, iterator>::type - grow_by( ForwardIterator first, ForwardIterator last ) { - auto delta = std::distance(first, last); - return internal_grow_by_delta(delta, first, last); - } - - iterator grow_by( std::initializer_list init ) { - return grow_by(init.begin(), init.end()); - } - - iterator grow_to_at_least( size_type n ) { - return internal_grow_to_at_least(n); - } - iterator grow_to_at_least( size_type n, const value_type& value ) { - return internal_grow_to_at_least(n, value); - } - - iterator push_back( const value_type& item ) { - return internal_emplace_back(item); - } - - iterator push_back( value_type&& item ) { - return internal_emplace_back(std::move(item)); - } - - template - iterator emplace_back( Args&&... args ) { - return internal_emplace_back(std::forward(args)...); - } - - // Items access - reference operator[]( size_type index ) { - return internal_subscript(index); - } - const_reference operator[]( size_type index ) const { - return internal_subscript(index); - } - - reference at( size_type index ) { - return internal_subscript_with_exceptions(index); - } - const_reference at( size_type index ) const { - return internal_subscript_with_exceptions(index); - } - - // Get range for iterating with parallel algorithms - range_type range( size_t grainsize = 1 ) { - return range_type(begin(), end(), grainsize); - } - - // Get const range for iterating with parallel algorithms - const_range_type range( size_t grainsize = 1 ) const { - return const_range_type(begin(), end(), grainsize); - } - - reference front() { - return internal_subscript(0); - } - - const_reference front() const { - return internal_subscript(0); - } - - reference back() { - return internal_subscript(size() - 1); - } - - const_reference back() const { - return internal_subscript(size() - 1); - } - - // Iterators - iterator begin() { return iterator(*this, 0); } - const_iterator begin() const { return const_iterator(*this, 0); } - const_iterator cbegin() const { return const_iterator(*this, 0); } - - iterator end() { return iterator(*this, size()); } - const_iterator end() const { return const_iterator(*this, size()); } - const_iterator cend() const { return const_iterator(*this, size()); } - - reverse_iterator rbegin() { return reverse_iterator(end()); } - const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); } - - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); } - - allocator_type get_allocator() const { - return base_type::get_allocator(); - } - - // Storage - bool empty() const noexcept { - return 0 == size(); - } - - size_type size() const noexcept { - return std::min(this->my_size.load(std::memory_order_acquire), capacity()); - } - - size_type max_size() const noexcept { - return allocator_traits_type::max_size(base_type::get_allocator()); - } - - size_type capacity() const noexcept { - return base_type::capacity(); - } - - void reserve( size_type n ) { - if (n == 0) return; - - if (n > max_size()) { - tbb::detail::throw_exception(exception_id::reservation_length_error); - } - - this->assign_first_block_if_necessary(this->segment_index_of(n - 1) + 1); - base_type::reserve(n); - } - - void resize( size_type n ) { - internal_resize(n); - } - - void resize( size_type n, const value_type& val ) { - internal_resize(n, val); - } - - void shrink_to_fit() { - internal_compact(); - } - - void swap(concurrent_vector& other) noexcept(is_noexcept_swap) { - base_type::swap(other); - } - - void clear() { - destroy_elements(); - } - -private: - using segment_type = typename base_type::segment_type; - using segment_table_type = typename base_type::segment_table_type; - using segment_table_allocator_traits = typename base_type::segment_table_allocator_traits; - using segment_index_type = typename base_type::segment_index_type; - - using segment_element_type = typename base_type::value_type; - using segment_element_allocator_type = typename allocator_traits_type::template rebind_alloc; - using segment_element_allocator_traits = tbb::detail::allocator_traits; - - segment_table_type allocate_long_table( const typename base_type::atomic_segment* embedded_table, size_type start_index ) { - __TBB_ASSERT(start_index <= this->embedded_table_size, "Start index out of embedded table"); - - // If other threads are trying to set pointers in the short segment, wait for them to finish their - // assignments before we copy the short segment to the long segment. Note: grow_to_at_least depends on it - for (segment_index_type i = 0; this->segment_base(i) < start_index; ++i) { - spin_wait_while_eq(embedded_table[i], segment_type(nullptr)); - } - - // It is possible that the table was extend by a thread allocating first_block, need to check this. - if (this->get_table() != embedded_table) { - return nullptr; - } - - // Allocate long segment table and fill with null pointers - segment_table_type new_segment_table = segment_table_allocator_traits::allocate(base_type::get_allocator(), this->pointers_per_long_table); - // Copy segment pointers from the embedded table - for (size_type segment_index = 0; segment_index < this->pointers_per_embedded_table; ++segment_index) { - segment_table_allocator_traits::construct(base_type::get_allocator(), &new_segment_table[segment_index], - embedded_table[segment_index].load(std::memory_order_relaxed)); - } - for (size_type segment_index = this->pointers_per_embedded_table; segment_index < this->pointers_per_long_table; ++segment_index) { - segment_table_allocator_traits::construct(base_type::get_allocator(), &new_segment_table[segment_index], nullptr); - } - - return new_segment_table; - } - - // create_segment function is required by the segment_table base class - segment_type create_segment( segment_table_type table, segment_index_type seg_index, size_type index ) { - size_type first_block = this->my_first_block.load(std::memory_order_relaxed); - // First block allocation - if (seg_index < first_block) { - // If 0 segment is already allocated, then it remains to wait until the segments are filled to requested - if (table[0].load(std::memory_order_acquire) != nullptr) { - spin_wait_while_eq(table[seg_index], segment_type(nullptr)); - return nullptr; - } - - segment_element_allocator_type segment_allocator(base_type::get_allocator()); - segment_type new_segment = nullptr; - size_type first_block_size = this->segment_size(first_block); - try_call( [&] { - new_segment = segment_element_allocator_traits::allocate(segment_allocator, first_block_size); - } ).on_exception( [&] { - segment_type disabled_segment = nullptr; - if (table[0].compare_exchange_strong(disabled_segment, this->segment_allocation_failure_tag)) { - size_type end_segment = table == this->my_embedded_table ? this->pointers_per_embedded_table : first_block; - for (size_type i = 1; i < end_segment; ++i) { - table[i].store(this->segment_allocation_failure_tag, std::memory_order_release); - } - } - }); - - segment_type disabled_segment = nullptr; - if (table[0].compare_exchange_strong(disabled_segment, new_segment)) { - this->extend_table_if_necessary(table, 0, first_block_size); - for (size_type i = 1; i < first_block; ++i) { - table[i].store(new_segment, std::memory_order_release); - } - - // Other threads can wait on a snapshot of an embedded table, need to fill it. - for (size_type i = 1; i < first_block && i < this->pointers_per_embedded_table; ++i) { - this->my_embedded_table[i].store(new_segment, std::memory_order_release); - } - } else if (new_segment != this->segment_allocation_failure_tag) { - // Deallocate the memory - segment_element_allocator_traits::deallocate(segment_allocator, new_segment, first_block_size); - // 0 segment is already allocated, then it remains to wait until the segments are filled to requested - spin_wait_while_eq(table[seg_index], segment_type(nullptr)); - } - } else { - size_type offset = this->segment_base(seg_index); - if (index == offset) { - __TBB_ASSERT(table[seg_index].load(std::memory_order_relaxed) == nullptr, "Only this thread can enable this segment"); - segment_element_allocator_type segment_allocator(base_type::get_allocator()); - segment_type new_segment = this->segment_allocation_failure_tag; - try_call( [&] { - new_segment = segment_element_allocator_traits::allocate(segment_allocator,this->segment_size(seg_index)); - // Shift base address to simplify access by index - new_segment -= this->segment_base(seg_index); - } ).on_completion( [&] { - table[seg_index].store(new_segment, std::memory_order_release); - }); - } else { - spin_wait_while_eq(table[seg_index], segment_type(nullptr)); - } - } - return nullptr; - } - - // Returns the number of elements in the segment to be destroy - size_type number_of_elements_in_segment( segment_index_type seg_index ) { - size_type curr_vector_size = this->my_size.load(std::memory_order_relaxed); - size_type curr_segment_base = this->segment_base(seg_index); - - if (seg_index == 0) { - return std::min(curr_vector_size, this->segment_size(seg_index)); - } else { - // Perhaps the segment is allocated, but there are no elements in it. - if (curr_vector_size < curr_segment_base) { - return 0; - } - return curr_segment_base * 2 > curr_vector_size ? curr_vector_size - curr_segment_base : curr_segment_base; - } - } - - segment_type nullify_segment( segment_table_type table, size_type segment_index ) { - segment_type target_segment = table[segment_index].load(std::memory_order_relaxed); - if (segment_index >= this->my_first_block) { - table[segment_index].store(nullptr, std::memory_order_relaxed); - } else { - if (segment_index == 0) { - for (size_type i = 0; i < this->my_first_block; ++i) { - table[i].store(nullptr, std::memory_order_relaxed); - } - } - } - - return target_segment; - } - - void deallocate_segment( segment_type address, segment_index_type seg_index ) { - segment_element_allocator_type segment_allocator(base_type::get_allocator()); - size_type first_block = this->my_first_block.load(std::memory_order_relaxed); - if (seg_index >= first_block) { - segment_element_allocator_traits::deallocate(segment_allocator, address, this->segment_size(seg_index)); - } - else if (seg_index == 0) { - size_type elements_to_deallocate = first_block > 0 ? this->segment_size(first_block) : this->segment_size(0); - segment_element_allocator_traits::deallocate(segment_allocator, address, elements_to_deallocate); - } - } - - // destroy_segment function is required by the segment_table base class - void destroy_segment( segment_type address, segment_index_type seg_index ) { - size_type elements_to_destroy = number_of_elements_in_segment(seg_index); - segment_element_allocator_type segment_allocator(base_type::get_allocator()); - - for (size_type i = 0; i < elements_to_destroy; ++i) { - segment_element_allocator_traits::destroy(segment_allocator, address + i); - } - - deallocate_segment(address, seg_index); - } - - // copy_segment function is required by the segment_table base class - void copy_segment( segment_index_type seg_index, segment_type from, segment_type to ) { - size_type i = 0; - try_call( [&] { - for (; i != number_of_elements_in_segment(seg_index); ++i) { - segment_table_allocator_traits::construct(base_type::get_allocator(), to + i, from[i]); - } - } ).on_exception( [&] { - // Zero-initialize items left not constructed after the exception - zero_unconstructed_elements(this->get_segment(seg_index) + i, this->segment_size(seg_index) - i); - - segment_index_type last_segment = this->segment_index_of(this->my_size.load(std::memory_order_relaxed)); - auto table = this->get_table(); - for (segment_index_type j = seg_index + 1; j != last_segment; ++j) { - auto curr_segment = table[j].load(std::memory_order_relaxed); - if (curr_segment) { - zero_unconstructed_elements(curr_segment + this->segment_base(j), this->segment_size(j)); - } - } - this->my_size.store(this->segment_size(seg_index) + i, std::memory_order_relaxed); - }); - } - - // move_segment function is required by the segment_table base class - void move_segment( segment_index_type seg_index, segment_type from, segment_type to ) { - size_type i = 0; - try_call( [&] { - for (; i != number_of_elements_in_segment(seg_index); ++i) { - segment_table_allocator_traits::construct(base_type::get_allocator(), to + i, std::move(from[i])); - } - } ).on_exception( [&] { - // Zero-initialize items left not constructed after the exception - zero_unconstructed_elements(this->get_segment(seg_index) + i, this->segment_size(seg_index) - i); - - segment_index_type last_segment = this->segment_index_of(this->my_size.load(std::memory_order_relaxed)); - auto table = this->get_table(); - for (segment_index_type j = seg_index + 1; j != last_segment; ++j) { - auto curr_segment = table[j].load(std::memory_order_relaxed); - if (curr_segment) { - zero_unconstructed_elements(curr_segment + this->segment_base(j), this->segment_size(j)); - } - } - this->my_size.store(this->segment_size(seg_index) + i, std::memory_order_relaxed); - }); - } - - static constexpr bool is_first_element_in_segment( size_type index ) { - // An element is the first in a segment if its index is equal to a power of two - return is_power_of_two_at_least(index, 2); - } - - const_reference internal_subscript( size_type index ) const { - return const_cast(this)->internal_subscript(index); - } - - reference internal_subscript( size_type index ) { - __TBB_ASSERT(index < this->my_size.load(std::memory_order_relaxed), "Invalid subscript index"); - return base_type::template internal_subscript(index); - } - - const_reference internal_subscript_with_exceptions( size_type index ) const { - return const_cast(this)->internal_subscript_with_exceptions(index); - } - - reference internal_subscript_with_exceptions( size_type index ) { - if (index >= this->my_size.load(std::memory_order_acquire)) { - tbb::detail::throw_exception(exception_id::out_of_range); - } - - segment_table_type table = this->my_segment_table.load(std::memory_order_acquire); - - size_type seg_index = this->segment_index_of(index); - if (base_type::number_of_segments(table) < seg_index) { - tbb::detail::throw_exception(exception_id::out_of_range); - } - - if (table[seg_index] <= this->segment_allocation_failure_tag) { - tbb::detail::throw_exception(exception_id::out_of_range); - } - - return base_type::template internal_subscript(index); - } - - static void zero_unconstructed_elements( pointer start, size_type count ) { - std::memset(static_cast(start), 0, count * sizeof(value_type)); - } - - template - iterator internal_emplace_back( Args&&... args ) { - size_type old_size = this->my_size++; - this->assign_first_block_if_necessary(default_first_block_size); - auto element_address = &base_type::template internal_subscript(old_size); - - // try_call API is not convenient here due to broken - // variadic capture on GCC 4.8.5 - auto value_guard = make_raii_guard([&] { - zero_unconstructed_elements(element_address, /*count =*/1); - }); - - segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, std::forward(args)...); - value_guard.dismiss(); - return iterator(*this, old_size, element_address); - } - - template - void internal_loop_construct( segment_table_type table, size_type start_idx, size_type end_idx, const Args&... args ) { - static_assert(sizeof...(Args) < 2, "Too many parameters"); - for (size_type idx = start_idx; idx < end_idx; ++idx) { - auto element_address = &base_type::template internal_subscript(idx); - // try_call API is not convenient here due to broken - // variadic capture on GCC 4.8.5 - auto value_guard = make_raii_guard( [&] { - segment_index_type last_allocated_segment = this->find_last_allocated_segment(table); - size_type segment_size = this->segment_size(last_allocated_segment); - end_idx = end_idx < segment_size ? end_idx : segment_size; - for (size_type i = idx; i < end_idx; ++i) { - zero_unconstructed_elements(&this->internal_subscript(i), /*count =*/1); - } - }); - segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, args...); - value_guard.dismiss(); - } - } - - template - void internal_loop_construct( segment_table_type table, size_type start_idx, size_type end_idx, ForwardIterator first, ForwardIterator ) { - for (size_type idx = start_idx; idx < end_idx; ++idx) { - auto element_address = &base_type::template internal_subscript(idx); - try_call( [&] { - segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, *first++); - } ).on_exception( [&] { - segment_index_type last_allocated_segment = this->find_last_allocated_segment(table); - size_type segment_size = this->segment_size(last_allocated_segment); - end_idx = end_idx < segment_size ? end_idx : segment_size; - for (size_type i = idx; i < end_idx; ++i) { - zero_unconstructed_elements(&this->internal_subscript(i), /*count =*/1); - } - }); - } - } - - template - iterator internal_grow( size_type start_idx, size_type end_idx, const Args&... args ) { - this->assign_first_block_if_necessary(this->segment_index_of(end_idx - 1) + 1); - size_type seg_index = this->segment_index_of(end_idx - 1); - segment_table_type table = this->get_table(); - this->extend_table_if_necessary(table, start_idx, end_idx); - - if (seg_index > this->my_first_block.load(std::memory_order_relaxed)) { - // So that other threads be able to work with the last segment of grow_by, allocate it immediately. - // If the last segment is not less than the first block - if (table[seg_index].load(std::memory_order_relaxed) == nullptr) { - size_type first_element = this->segment_base(seg_index); - if (first_element >= start_idx && first_element < end_idx) { - segment_type segment = table[seg_index].load(std::memory_order_relaxed); - base_type::enable_segment(segment, table, seg_index, first_element); - } - } - } - - internal_loop_construct(table, start_idx, end_idx, args...); - - return iterator(*this, start_idx, &base_type::template internal_subscript(start_idx)); - } - - - template - iterator internal_grow_by_delta( size_type delta, const Args&... args ) { - if (delta == size_type(0)) { - return end(); - } - size_type start_idx = this->my_size.fetch_add(delta); - size_type end_idx = start_idx + delta; - - return internal_grow(start_idx, end_idx, args...); - } - - template - iterator internal_grow_to_at_least( size_type new_size, const Args&... args ) { - size_type old_size = this->my_size.load(std::memory_order_relaxed); - if (new_size == size_type(0)) return iterator(*this, 0); - while (old_size < new_size && !this->my_size.compare_exchange_weak(old_size, new_size)) - {} - - int delta = static_cast(new_size) - static_cast(old_size); - if (delta > 0) { - return internal_grow(old_size, new_size, args...); - } - - size_type end_segment = this->segment_index_of(new_size - 1); - - // Check/wait for segments allocation completes - if (end_segment >= this->pointers_per_embedded_table && - this->get_table() == this->my_embedded_table) - { - spin_wait_while_eq(this->my_segment_table, this->my_embedded_table); - } - - for (segment_index_type seg_idx = 0; seg_idx <= end_segment; ++seg_idx) { - if (this->get_table()[seg_idx].load(std::memory_order_relaxed) == nullptr) { - atomic_backoff backoff(true); - while (this->get_table()[seg_idx].load(std::memory_order_relaxed) == nullptr) { - backoff.pause(); - } - } - } - - #if TBB_USE_DEBUG - size_type cap = capacity(); - __TBB_ASSERT( cap >= new_size, nullptr); - #endif - return iterator(*this, size()); - } - - template - void internal_resize( size_type n, const Args&... args ) { - if (n == 0) { - clear(); - return; - } - - size_type old_size = this->my_size.load(std::memory_order_acquire); - if (n > old_size) { - reserve(n); - grow_to_at_least(n, args...); - } else { - if (old_size == n) { - return; - } - size_type last_segment = this->segment_index_of(old_size - 1); - // Delete segments - for (size_type seg_idx = this->segment_index_of(n - 1) + 1; seg_idx <= last_segment; ++seg_idx) { - this->delete_segment(seg_idx); - } - - // If n > segment_size(n) => we need to destroy all of the items in the first segment - // Otherwise, we need to destroy only items with the index < n - size_type n_segment = this->segment_index_of(n - 1); - size_type last_index_to_destroy = std::min(this->segment_base(n_segment) + this->segment_size(n_segment), old_size); - // Destroy elements in curr segment - for (size_type idx = n; idx < last_index_to_destroy; ++idx) { - segment_table_allocator_traits::destroy(base_type::get_allocator(), &base_type::template internal_subscript(idx)); - } - this->my_size.store(n, std::memory_order_release); - } - } - - void destroy_elements() { - allocator_type alloc(base_type::get_allocator()); - for (size_type i = 0; i < this->my_size.load(std::memory_order_relaxed); ++i) { - allocator_traits_type::destroy(alloc, &base_type::template internal_subscript(i)); - } - this->my_size.store(0, std::memory_order_relaxed); - } - - static bool incompact_predicate( size_type size ) { - // memory page size - const size_type page_size = 4096; - return size < page_size || ((size - 1) % page_size < page_size / 2 && size < page_size * 128); - } - - void internal_compact() { - const size_type curr_size = this->my_size.load(std::memory_order_relaxed); - segment_table_type table = this->get_table(); - const segment_index_type k_end = this->find_last_allocated_segment(table); // allocated segments - const segment_index_type k_stop = curr_size ? this->segment_index_of(curr_size - 1) + 1 : 0; // number of segments to store existing items: 0=>0; 1,2=>1; 3,4=>2; [5-8]=>3;.. - const segment_index_type first_block = this->my_first_block; // number of merged segments, getting values from atomics - - segment_index_type k = first_block; - if (k_stop < first_block) { - k = k_stop; - } - else { - while (k < k_stop && incompact_predicate(this->segment_size(k) * sizeof(value_type))) k++; - } - - if (k_stop == k_end && k == first_block) { - return; - } - - // First segment optimization - if (k != first_block && k) { - size_type max_block = std::max(first_block, k); - - auto buffer_table = segment_table_allocator_traits::allocate(base_type::get_allocator(), max_block); - - for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) { - segment_table_allocator_traits::construct(base_type::get_allocator(), &buffer_table[seg_idx], - table[seg_idx].load(std::memory_order_relaxed)); - table[seg_idx].store(nullptr, std::memory_order_relaxed); - } - - this->my_first_block.store(k, std::memory_order_relaxed); - size_type index = 0; - try_call( [&] { - for (; index < std::min(this->segment_size(max_block), curr_size); ++index) { - auto element_address = &static_cast(this)->operator[](index); - segment_index_type seg_idx = this->segment_index_of(index); - segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, - std::move_if_noexcept(buffer_table[seg_idx].load(std::memory_order_relaxed)[index])); - } - } ).on_exception( [&] { - segment_element_allocator_type allocator(base_type::get_allocator()); - for (size_type i = 0; i < index; ++i) { - auto element_adress = &this->operator[](i); - segment_element_allocator_traits::destroy(allocator, element_adress); - } - segment_element_allocator_traits::deallocate(allocator, - table[0].load(std::memory_order_relaxed), this->segment_size(max_block)); - - for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) { - table[seg_idx].store(buffer_table[seg_idx].load(std::memory_order_relaxed), - std::memory_order_relaxed); - buffer_table[seg_idx].store(nullptr, std::memory_order_relaxed); - } - segment_table_allocator_traits::deallocate(base_type::get_allocator(), - buffer_table, max_block); - this->my_first_block.store(first_block, std::memory_order_relaxed); - }); - - // Need to correct deallocate old segments - // Method destroy_segment respect active first_block, therefore, - // in order for the segment deletion to work correctly, set the first_block size that was earlier, - // destroy the unnecessary segments. - this->my_first_block.store(first_block, std::memory_order_relaxed); - for (size_type seg_idx = max_block; seg_idx > 0 ; --seg_idx) { - auto curr_segment = buffer_table[seg_idx - 1].load(std::memory_order_relaxed); - if (curr_segment != nullptr) { - destroy_segment(buffer_table[seg_idx - 1].load(std::memory_order_relaxed) + this->segment_base(seg_idx - 1), - seg_idx - 1); - } - } - - this->my_first_block.store(k, std::memory_order_relaxed); - - for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) { - segment_table_allocator_traits::destroy(base_type::get_allocator(), &buffer_table[seg_idx]); - } - - segment_table_allocator_traits::deallocate(base_type::get_allocator(), buffer_table, max_block); - } - // free unnecessary segments allocated by reserve() call - if (k_stop < k_end) { - for (size_type seg_idx = k_end; seg_idx != k_stop; --seg_idx) { - if (table[seg_idx - 1].load(std::memory_order_relaxed) != nullptr) { - this->delete_segment(seg_idx - 1); - } - } - if (!k) this->my_first_block.store(0, std::memory_order_relaxed); - } - } - - // Lever for adjusting the size of first_block at the very first insertion. - // TODO: consider >1 value, check performance - static constexpr size_type default_first_block_size = 1; - - template - friend class vector_iterator; -}; // class concurrent_vector - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -// Deduction guide for the constructor from two iterators -template >, - typename = std::enable_if_t>, - typename = std::enable_if_t>> -concurrent_vector( It, It, Alloc = Alloc() ) --> concurrent_vector, Alloc>; -#endif - -template -void swap(concurrent_vector &lhs, - concurrent_vector &rhs) -{ - lhs.swap(rhs); -} - -template -bool operator==(const concurrent_vector &lhs, - const concurrent_vector &rhs) -{ - return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); -} - -#if !__TBB_CPP20_COMPARISONS_PRESENT -template -bool operator!=(const concurrent_vector &lhs, - const concurrent_vector &rhs) -{ - return !(lhs == rhs); -} -#endif // !__TBB_CPP20_COMPARISONS_PRESENT - -#if __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT -template -tbb::detail::synthesized_three_way_result::value_type> -operator<=>(const concurrent_vector &lhs, - const concurrent_vector &rhs) -{ - return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), - rhs.begin(), rhs.end(), - tbb::detail::synthesized_three_way_comparator{}); -} - -#else - -template -bool operator<(const concurrent_vector &lhs, - const concurrent_vector &rhs) -{ - return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); -} - -template -bool operator<=(const concurrent_vector &lhs, - const concurrent_vector &rhs) -{ - return !(rhs < lhs); -} - -template -bool operator>(const concurrent_vector &lhs, - const concurrent_vector &rhs) -{ - return rhs < lhs; -} - -template -bool operator>=(const concurrent_vector &lhs, - const concurrent_vector &rhs) -{ - return !(lhs < rhs); -} -#endif // __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT - -} // namespace d1 -} // namespace detail - -inline namespace v1 { - using detail::d1::concurrent_vector; -} // namespace v1 - -} // namespace tbb - -#endif // __TBB_concurrent_vector_H diff --git a/include/oneapi/tbb/detail/_aggregator.h b/include/oneapi/tbb/detail/_aggregator.h deleted file mode 100644 index 2e3e5cc..0000000 --- a/include/oneapi/tbb/detail/_aggregator.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - - -#ifndef __TBB_detail__aggregator_H -#define __TBB_detail__aggregator_H - -#include "_assert.h" -#include "_utils.h" -#include -#if !__TBBMALLOC_BUILD // TODO: check this macro with TBB Malloc -#include "../profiling.h" -#endif - -namespace tbb { -namespace detail { -namespace d1 { - -// Base class for aggregated operation -template -class aggregated_operation { -public: - // Zero value means "wait" status, all other values are "user" specified values and - // are defined into the scope of a class which uses "status" - std::atomic status; - - std::atomic next; - aggregated_operation() : status{}, next(nullptr) {} -}; // class aggregated_operation - -// Aggregator base class -/* An aggregator for collecting operations coming from multiple sources and executing - them serially on a single thread. OperationType must be derived from - aggregated_operation. The parameter HandlerType is a functor that will be passed the - list of operations and is expected to handle each operation appropriately, setting the - status of each operation to non-zero. */ -template -class aggregator_generic { -public: - aggregator_generic() : pending_operations(nullptr), handler_busy(false) {} - - // Execute an operation - /* Places an operation into the waitlist (pending_operations), and either handles the list, - or waits for the operation to complete, or returns. - The long_life_time parameter specifies the life time of the given operation object. - Operations with long_life_time == true may be accessed after execution. - A "short" life time operation (long_life_time == false) can be destroyed - during execution, and so any access to it after it was put into the waitlist, - including status check, is invalid. As a consequence, waiting for completion - of such operation causes undefined behavior. */ - template - void execute( OperationType* op, HandlerType& handle_operations, bool long_life_time = true ) { - // op->status should be read before inserting the operation into the - // aggregator waitlist since it can become invalid after executing a - // handler (if the operation has 'short' life time.) - const uintptr_t status = op->status.load(std::memory_order_relaxed); - - // ITT note: &(op->status) tag is used to cover accesses to this op node. This - // thread has created the operation, and now releases it so that the handler - // thread may handle the associated operation w/o triggering a race condition; - // thus this tag will be acquired just before the operation is handled in the - // handle_operations functor. - call_itt_notify(releasing, &(op->status)); - // insert the operation in the queue. - OperationType* res = pending_operations.load(std::memory_order_relaxed); - do { - op->next.store(res, std::memory_order_relaxed); - } while (!pending_operations.compare_exchange_strong(res, op)); - if (!res) { // first in the list; handle the operations - // ITT note: &pending_operations tag covers access to the handler_busy flag, - // which this waiting handler thread will try to set before entering - // handle_operations. - call_itt_notify(acquired, &pending_operations); - start_handle_operations(handle_operations); - // The operation with 'short' life time can already be destroyed - if (long_life_time) - __TBB_ASSERT(op->status.load(std::memory_order_relaxed), nullptr); - } - // Not first; wait for op to be ready - else if (!status) { // operation is blocking here. - __TBB_ASSERT(long_life_time, "Waiting for an operation object that might be destroyed during processing"); - call_itt_notify(prepare, &(op->status)); - spin_wait_while_eq(op->status, uintptr_t(0)); - } - } - -private: - // Trigger the handling of operations when the handler is free - template - void start_handle_operations( HandlerType& handle_operations ) { - OperationType* op_list; - - // ITT note: &handler_busy tag covers access to pending_operations as it is passed - // between active and waiting handlers. Below, the waiting handler waits until - // the active handler releases, and the waiting handler acquires &handler_busy as - // it becomes the active_handler. The release point is at the end of this - // function, when all operations in pending_operations have been handled by the - // owner of this aggregator. - call_itt_notify(prepare, &handler_busy); - // get the handler_busy: - // only one thread can possibly spin here at a time - spin_wait_until_eq(handler_busy, uintptr_t(0)); - call_itt_notify(acquired, &handler_busy); - // acquire fence not necessary here due to causality rule and surrounding atomics - handler_busy.store(1, std::memory_order_relaxed); - - // ITT note: &pending_operations tag covers access to the handler_busy flag - // itself. Capturing the state of the pending_operations signifies that - // handler_busy has been set and a new active handler will now process that list's - // operations. - call_itt_notify(releasing, &pending_operations); - // grab pending_operations - op_list = pending_operations.exchange(nullptr); - - // handle all the operations - handle_operations(op_list); - - // release the handler - handler_busy.store(0, std::memory_order_release); - } - - // An atomically updated list (aka mailbox) of pending operations - std::atomic pending_operations; - // Controls threads access to handle_operations - std::atomic handler_busy; -}; // class aggregator_generic - -template -class aggregator : public aggregator_generic { - HandlerType handle_operations; -public: - aggregator() = default; - - void initialize_handler( HandlerType h ) { handle_operations = h; } - - void execute(OperationType* op) { - aggregator_generic::execute(op, handle_operations); - } -}; // class aggregator - -// the most-compatible friend declaration (vs, gcc, icc) is -// template friend class aggregating_functor; -template -class aggregating_functor { - AggregatingClass* my_object{nullptr}; -public: - aggregating_functor() = default; - aggregating_functor( AggregatingClass* object ) : my_object(object) { - __TBB_ASSERT(my_object, nullptr); - } - - void operator()( OperationList* op_list ) { - __TBB_ASSERT(my_object, nullptr); - my_object->handle_operations(op_list); - } -}; // class aggregating_functor - - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__aggregator_H diff --git a/include/oneapi/tbb/detail/_aligned_space.h b/include/oneapi/tbb/detail/_aligned_space.h deleted file mode 100644 index 13857c4..0000000 --- a/include/oneapi/tbb/detail/_aligned_space.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -#ifndef __TBB_aligned_space_H -#define __TBB_aligned_space_H - -#include - -#include "_template_helpers.h" - -namespace tbb { -namespace detail { -inline namespace d0 { - -//! Block of space aligned sufficiently to construct an array T with N elements. -/** The elements are not constructed or destroyed by this class. - @ingroup memory_allocation */ -template -class aligned_space { - alignas(alignof(T)) std::uint8_t aligned_array[N * sizeof(T)]; - -public: - //! Pointer to beginning of array - T* begin() const { return punned_cast(&aligned_array); } - - //! Pointer to one past last element in array. - T* end() const { return begin() + N; } -}; - -} // namespace d0 -} // namespace detail -} // namespace tbb - -#endif /* __TBB_aligned_space_H */ diff --git a/include/oneapi/tbb/detail/_allocator_traits.h b/include/oneapi/tbb/detail/_allocator_traits.h deleted file mode 100644 index 8c60e25..0000000 --- a/include/oneapi/tbb/detail/_allocator_traits.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__allocator_traits_H -#define __TBB_detail__allocator_traits_H - -#include "_config.h" -#include "_template_helpers.h" -#include -#include - -namespace tbb { -namespace detail { -inline namespace d0 { - -#if !__TBB_CPP17_ALLOCATOR_IS_ALWAYS_EQUAL_PRESENT -// Struct is_always_equal_detector provides the member type "type" which is -// Allocator::is_always_equal if it is present, std::false_type otherwise -template -struct is_always_equal_detector { - using type = std::false_type; -}; - -template -struct is_always_equal_detector> -{ - using type = typename Allocator::is_always_equal; -}; -#endif // !__TBB_CPP17_ALLOCATOR_IS_ALWAYS_EQUAL_PRESENT - -template -class allocator_traits : public std::allocator_traits -{ - using base_type = std::allocator_traits; -public: -#if !__TBB_CPP17_ALLOCATOR_IS_ALWAYS_EQUAL_PRESENT - using is_always_equal = typename is_always_equal_detector::type; -#endif - - template - using rebind_traits = typename tbb::detail::allocator_traits>; -}; // struct allocator_traits - -template -void copy_assign_allocators_impl( Allocator& lhs, const Allocator& rhs, /*pocca = */std::true_type ) { - lhs = rhs; -} - -template -void copy_assign_allocators_impl( Allocator&, const Allocator&, /*pocca = */ std::false_type ) {} - -// Copy assigns allocators only if propagate_on_container_copy_assignment is true -template -void copy_assign_allocators( Allocator& lhs, const Allocator& rhs ) { - using pocca_type = typename allocator_traits::propagate_on_container_copy_assignment; - copy_assign_allocators_impl(lhs, rhs, pocca_type()); -} - -template -void move_assign_allocators_impl( Allocator& lhs, Allocator& rhs, /*pocma = */ std::true_type ) { - lhs = std::move(rhs); -} - -template -void move_assign_allocators_impl( Allocator&, Allocator&, /*pocma = */ std::false_type ) {} - -// Move assigns allocators only if propagate_on_container_move_assignment is true -template -void move_assign_allocators( Allocator& lhs, Allocator& rhs ) { - using pocma_type = typename allocator_traits::propagate_on_container_move_assignment; - move_assign_allocators_impl(lhs, rhs, pocma_type()); -} - -template -void swap_allocators_impl( Allocator& lhs, Allocator& rhs, /*pocs = */ std::true_type ) { - using std::swap; - swap(lhs, rhs); -} - -template -void swap_allocators_impl( Allocator&, Allocator&, /*pocs = */ std::false_type ) {} - -// Swaps allocators only if propagate_on_container_swap is true -template -void swap_allocators( Allocator& lhs, Allocator& rhs ) { - using pocs_type = typename allocator_traits::propagate_on_container_swap; - swap_allocators_impl(lhs, rhs, pocs_type()); -} - -} // inline namespace d0 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__allocator_traits_H diff --git a/include/oneapi/tbb/detail/_assert.h b/include/oneapi/tbb/detail/_assert.h deleted file mode 100644 index fce714f..0000000 --- a/include/oneapi/tbb/detail/_assert.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__assert_H -#define __TBB_detail__assert_H - -#include "_config.h" - -#if __TBBMALLOC_BUILD -namespace rml { namespace internal { -#else -namespace tbb { -namespace detail { -namespace r1 { -#endif -//! Process an assertion failure. -/** Normally called from __TBB_ASSERT macro. - If assertion handler is null, print message for assertion failure and abort. - Otherwise call the assertion handler. */ -TBB_EXPORT void __TBB_EXPORTED_FUNC assertion_failure(const char* location, int line, const char* expression, const char* comment); -#if __TBBMALLOC_BUILD -}} // namespaces rml::internal -#else -} // namespace r1 -} // namespace detail -} // namespace tbb -#endif - -#if __TBBMALLOC_BUILD -//! Release version of assertions -#define __TBB_ASSERT_RELEASE(predicate,message) ((predicate)?((void)0) : rml::internal::assertion_failure(__func__,__LINE__,#predicate,message)) -#else -#define __TBB_ASSERT_RELEASE(predicate,message) ((predicate)?((void)0) : tbb::detail::r1::assertion_failure(__func__,__LINE__,#predicate,message)) -#endif - -#if TBB_USE_ASSERT - //! Assert that predicate is true. - /** If predicate is false, print assertion failure message. - If the comment argument is not nullptr, it is printed as part of the failure message. - The comment argument has no other effect. */ - #define __TBB_ASSERT(predicate,message) __TBB_ASSERT_RELEASE(predicate,message) - //! "Extended" version - #define __TBB_ASSERT_EX __TBB_ASSERT -#else - //! No-op version of __TBB_ASSERT. - #define __TBB_ASSERT(predicate,comment) ((void)0) - //! "Extended" version is useful to suppress warnings if a variable is only used with an assert - #define __TBB_ASSERT_EX(predicate,comment) ((void)(1 && (predicate))) -#endif // TBB_USE_ASSERT - -#endif // __TBB_detail__assert_H diff --git a/include/oneapi/tbb/detail/_attach.h b/include/oneapi/tbb/detail/_attach.h deleted file mode 100644 index 45f2972..0000000 --- a/include/oneapi/tbb/detail/_attach.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright (c) 2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__attach_H -#define __TBB_detail__attach_H - -#include "_config.h" - -namespace tbb { -namespace detail { -namespace d1 { - - struct attach {}; - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__attach_H diff --git a/include/oneapi/tbb/detail/_concurrent_queue_base.h b/include/oneapi/tbb/detail/_concurrent_queue_base.h deleted file mode 100644 index ee628e1..0000000 --- a/include/oneapi/tbb/detail/_concurrent_queue_base.h +++ /dev/null @@ -1,650 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__concurrent_queue_base_H -#define __TBB_detail__concurrent_queue_base_H - -#include "_utils.h" -#include "_exception.h" -#include "_machine.h" -#include "_allocator_traits.h" - -#include "../profiling.h" -#include "../spin_mutex.h" -#include "../cache_aligned_allocator.h" - -#include - -namespace tbb { -namespace detail { -namespace d2 { - -using ticket_type = std::size_t; - -template -inline bool is_valid_page(const Page p) { - return reinterpret_cast(p) > 1; -} - -template -struct concurrent_queue_rep; - -template -class micro_queue_pop_finalizer; - -#if _MSC_VER && !defined(__INTEL_COMPILER) -// unary minus operator applied to unsigned type, result still unsigned -#pragma warning( push ) -#pragma warning( disable: 4146 ) -#endif - -// A queue using simple locking. -// For efficiency, this class has no constructor. -// The caller is expected to zero-initialize it. -template -class micro_queue { -private: - using queue_rep_type = concurrent_queue_rep; - using self_type = micro_queue; -public: - using size_type = std::size_t; - using value_type = T; - using reference = value_type&; - using const_reference = const value_type&; - - using allocator_type = Allocator; - using allocator_traits_type = tbb::detail::allocator_traits; - using queue_allocator_type = typename allocator_traits_type::template rebind_alloc; - - static constexpr size_type item_size = sizeof(T); - static constexpr size_type items_per_page = item_size <= 8 ? 32 : - item_size <= 16 ? 16 : - item_size <= 32 ? 8 : - item_size <= 64 ? 4 : - item_size <= 128 ? 2 : 1; - - struct padded_page { - padded_page() {} - ~padded_page() {} - - reference operator[] (std::size_t index) { - __TBB_ASSERT(index < items_per_page, "Index out of range"); - return items[index]; - } - - const_reference operator[] (std::size_t index) const { - __TBB_ASSERT(index < items_per_page, "Index out of range"); - return items[index]; - } - - padded_page* next{ nullptr }; - std::atomic mask{}; - - union { - value_type items[items_per_page]; - }; - }; // struct padded_page - - using page_allocator_type = typename allocator_traits_type::template rebind_alloc; -protected: - using page_allocator_traits = tbb::detail::allocator_traits; - -public: - using item_constructor_type = void (*)(value_type* location, const void* src); - micro_queue() = default; - micro_queue( const micro_queue& ) = delete; - micro_queue& operator=( const micro_queue& ) = delete; - - size_type prepare_page( ticket_type k, queue_rep_type& base, page_allocator_type page_allocator, - padded_page*& p ) { - __TBB_ASSERT(p == nullptr, "Invalid page argument for prepare_page"); - k &= -queue_rep_type::n_queue; - size_type index = modulo_power_of_two(k / queue_rep_type::n_queue, items_per_page); - if (!index) { - try_call( [&] { - p = page_allocator_traits::allocate(page_allocator, 1); - }).on_exception( [&] { - ++base.n_invalid_entries; - invalidate_page( k ); - }); - page_allocator_traits::construct(page_allocator, p); - } - - spin_wait_until_my_turn(tail_counter, k, base); - d1::call_itt_notify(d1::acquired, &tail_counter); - - if (p) { - spin_mutex::scoped_lock lock( page_mutex ); - padded_page* q = tail_page.load(std::memory_order_relaxed); - if (is_valid_page(q)) { - q->next = p; - } else { - head_page.store(p, std::memory_order_relaxed); - } - tail_page.store(p, std::memory_order_relaxed); - } else { - p = tail_page.load(std::memory_order_relaxed); - } - return index; - } - - template - void push( ticket_type k, queue_rep_type& base, queue_allocator_type& allocator, Args&&... args ) - { - padded_page* p = nullptr; - page_allocator_type page_allocator(allocator); - size_type index = prepare_page(k, base, page_allocator, p); - __TBB_ASSERT(p != nullptr, "Page was not prepared"); - - // try_call API is not convenient here due to broken - // variadic capture on GCC 4.8.5 - auto value_guard = make_raii_guard([&] { - ++base.n_invalid_entries; - d1::call_itt_notify(d1::releasing, &tail_counter); - tail_counter.fetch_add(queue_rep_type::n_queue); - }); - - page_allocator_traits::construct(page_allocator, &(*p)[index], std::forward(args)...); - // If no exception was thrown, mark item as present. - p->mask.store(p->mask.load(std::memory_order_relaxed) | uintptr_t(1) << index, std::memory_order_relaxed); - d1::call_itt_notify(d1::releasing, &tail_counter); - - value_guard.dismiss(); - tail_counter.fetch_add(queue_rep_type::n_queue); - } - - void abort_push( ticket_type k, queue_rep_type& base, queue_allocator_type& allocator ) { - padded_page* p = nullptr; - prepare_page(k, base, allocator, p); - ++base.n_invalid_entries; - tail_counter.fetch_add(queue_rep_type::n_queue); - } - - bool pop( void* dst, ticket_type k, queue_rep_type& base, queue_allocator_type& allocator ) { - k &= -queue_rep_type::n_queue; - spin_wait_until_eq(head_counter, k); - d1::call_itt_notify(d1::acquired, &head_counter); - spin_wait_while_eq(tail_counter, k); - d1::call_itt_notify(d1::acquired, &tail_counter); - padded_page *p = head_page.load(std::memory_order_relaxed); - __TBB_ASSERT( p, nullptr ); - size_type index = modulo_power_of_two( k/queue_rep_type::n_queue, items_per_page ); - bool success = false; - { - page_allocator_type page_allocator(allocator); - micro_queue_pop_finalizer finalizer(*this, page_allocator, - k + queue_rep_type::n_queue, index == items_per_page - 1 ? p : nullptr ); - if (p->mask.load(std::memory_order_relaxed) & (std::uintptr_t(1) << index)) { - success = true; - assign_and_destroy_item(dst, *p, index); - } else { - --base.n_invalid_entries; - } - } - return success; - } - - micro_queue& assign( const micro_queue& src, queue_allocator_type& allocator, - item_constructor_type construct_item ) - { - head_counter.store(src.head_counter.load(std::memory_order_relaxed), std::memory_order_relaxed); - tail_counter.store(src.tail_counter.load(std::memory_order_relaxed), std::memory_order_relaxed); - - const padded_page* srcp = src.head_page.load(std::memory_order_relaxed); - if( is_valid_page(srcp) ) { - ticket_type g_index = head_counter.load(std::memory_order_relaxed); - size_type n_items = (tail_counter.load(std::memory_order_relaxed) - head_counter.load(std::memory_order_relaxed)) - / queue_rep_type::n_queue; - size_type index = modulo_power_of_two(head_counter.load(std::memory_order_relaxed) / queue_rep_type::n_queue, items_per_page); - size_type end_in_first_page = (index+n_items < items_per_page) ? (index + n_items) : items_per_page; - - try_call( [&] { - head_page.store(make_copy(allocator, srcp, index, end_in_first_page, g_index, construct_item), std::memory_order_relaxed); - }).on_exception( [&] { - head_counter.store(0, std::memory_order_relaxed); - tail_counter.store(0, std::memory_order_relaxed); - }); - padded_page* cur_page = head_page.load(std::memory_order_relaxed); - - try_call( [&] { - if (srcp != src.tail_page.load(std::memory_order_relaxed)) { - for (srcp = srcp->next; srcp != src.tail_page.load(std::memory_order_relaxed); srcp=srcp->next ) { - cur_page->next = make_copy( allocator, srcp, 0, items_per_page, g_index, construct_item ); - cur_page = cur_page->next; - } - - __TBB_ASSERT(srcp == src.tail_page.load(std::memory_order_relaxed), nullptr ); - size_type last_index = modulo_power_of_two(tail_counter.load(std::memory_order_relaxed) / queue_rep_type::n_queue, items_per_page); - if( last_index==0 ) last_index = items_per_page; - - cur_page->next = make_copy( allocator, srcp, 0, last_index, g_index, construct_item ); - cur_page = cur_page->next; - } - tail_page.store(cur_page, std::memory_order_relaxed); - }).on_exception( [&] { - padded_page* invalid_page = reinterpret_cast(std::uintptr_t(1)); - tail_page.store(invalid_page, std::memory_order_relaxed); - }); - } else { - head_page.store(nullptr, std::memory_order_relaxed); - tail_page.store(nullptr, std::memory_order_relaxed); - } - return *this; - } - - padded_page* make_copy( queue_allocator_type& allocator, const padded_page* src_page, size_type begin_in_page, - size_type end_in_page, ticket_type& g_index, item_constructor_type construct_item ) - { - page_allocator_type page_allocator(allocator); - padded_page* new_page = page_allocator_traits::allocate(page_allocator, 1); - new_page->next = nullptr; - new_page->mask.store(src_page->mask.load(std::memory_order_relaxed), std::memory_order_relaxed); - for (; begin_in_page!=end_in_page; ++begin_in_page, ++g_index) { - if (new_page->mask.load(std::memory_order_relaxed) & uintptr_t(1) << begin_in_page) { - copy_item(*new_page, begin_in_page, *src_page, begin_in_page, construct_item); - } - } - return new_page; - } - - void invalidate_page( ticket_type k ) { - // Append an invalid page at address 1 so that no more pushes are allowed. - padded_page* invalid_page = reinterpret_cast(std::uintptr_t(1)); - { - spin_mutex::scoped_lock lock( page_mutex ); - tail_counter.store(k + queue_rep_type::n_queue + 1, std::memory_order_relaxed); - padded_page* q = tail_page.load(std::memory_order_relaxed); - if (is_valid_page(q)) { - q->next = invalid_page; - } else { - head_page.store(invalid_page, std::memory_order_relaxed); - } - tail_page.store(invalid_page, std::memory_order_relaxed); - } - } - - padded_page* get_head_page() { - return head_page.load(std::memory_order_relaxed); - } - - void clear(queue_allocator_type& allocator, padded_page* new_head = nullptr, padded_page* new_tail = nullptr) { - padded_page* curr_page = get_head_page(); - size_type index = (head_counter.load(std::memory_order_relaxed) / queue_rep_type::n_queue) % items_per_page; - page_allocator_type page_allocator(allocator); - - while (curr_page && is_valid_page(curr_page)) { - while (index != items_per_page) { - if (curr_page->mask.load(std::memory_order_relaxed) & (std::uintptr_t(1) << index)) { - page_allocator_traits::destroy(page_allocator, &curr_page->operator[](index)); - } - ++index; - } - - index = 0; - padded_page* next_page = curr_page->next; - page_allocator_traits::destroy(page_allocator, curr_page); - page_allocator_traits::deallocate(page_allocator, curr_page, 1); - curr_page = next_page; - } - head_counter.store(0, std::memory_order_relaxed); - tail_counter.store(0, std::memory_order_relaxed); - head_page.store(new_head, std::memory_order_relaxed); - tail_page.store(new_tail, std::memory_order_relaxed); - } - - void clear_and_invalidate(queue_allocator_type& allocator) { - padded_page* invalid_page = reinterpret_cast(std::uintptr_t(1)); - clear(allocator, invalid_page, invalid_page); - } - -private: - // template - friend class micro_queue_pop_finalizer; - - // Class used to ensure exception-safety of method "pop" - class destroyer { - value_type& my_value; - public: - destroyer( reference value ) : my_value(value) {} - destroyer( const destroyer& ) = delete; - destroyer& operator=( const destroyer& ) = delete; - ~destroyer() {my_value.~T();} - }; // class destroyer - - void copy_item( padded_page& dst, size_type dindex, const padded_page& src, size_type sindex, - item_constructor_type construct_item ) - { - auto& src_item = src[sindex]; - construct_item( &dst[dindex], static_cast(&src_item) ); - } - - void assign_and_destroy_item( void* dst, padded_page& src, size_type index ) { - auto& from = src[index]; - destroyer d(from); - *static_cast(dst) = std::move(from); - } - - void spin_wait_until_my_turn( std::atomic& counter, ticket_type k, queue_rep_type& rb ) const { - for (atomic_backoff b{};; b.pause()) { - ticket_type c = counter.load(std::memory_order_acquire); - if (c == k) return; - else if (c & 1) { - ++rb.n_invalid_entries; - throw_exception( exception_id::bad_last_alloc); - } - } - } - - std::atomic head_page{}; - std::atomic head_counter{}; - - std::atomic tail_page{}; - std::atomic tail_counter{}; - - spin_mutex page_mutex{}; -}; // class micro_queue - -#if _MSC_VER && !defined(__INTEL_COMPILER) -#pragma warning( pop ) -#endif // warning 4146 is back - -template -class micro_queue_pop_finalizer { -public: - using padded_page = typename Container::padded_page; - using allocator_type = Allocator; - using allocator_traits_type = tbb::detail::allocator_traits; - - micro_queue_pop_finalizer( Container& queue, Allocator& alloc, ticket_type k, padded_page* p ) : - my_ticket_type(k), my_queue(queue), my_page(p), allocator(alloc) - {} - - micro_queue_pop_finalizer( const micro_queue_pop_finalizer& ) = delete; - micro_queue_pop_finalizer& operator=( const micro_queue_pop_finalizer& ) = delete; - - ~micro_queue_pop_finalizer() { - padded_page* p = my_page; - if( is_valid_page(p) ) { - spin_mutex::scoped_lock lock( my_queue.page_mutex ); - padded_page* q = p->next; - my_queue.head_page.store(q, std::memory_order_relaxed); - if( !is_valid_page(q) ) { - my_queue.tail_page.store(nullptr, std::memory_order_relaxed); - } - } - my_queue.head_counter.store(my_ticket_type, std::memory_order_release); - if ( is_valid_page(p) ) { - allocator_traits_type::destroy(allocator, static_cast(p)); - allocator_traits_type::deallocate(allocator, static_cast(p), 1); - } - } -private: - ticket_type my_ticket_type; - Container& my_queue; - padded_page* my_page; - Allocator& allocator; -}; // class micro_queue_pop_finalizer - -#if _MSC_VER && !defined(__INTEL_COMPILER) -// structure was padded due to alignment specifier -#pragma warning( push ) -#pragma warning( disable: 4324 ) -#endif - -template -struct concurrent_queue_rep { - using self_type = concurrent_queue_rep; - using size_type = std::size_t; - using micro_queue_type = micro_queue; - using allocator_type = Allocator; - using allocator_traits_type = tbb::detail::allocator_traits; - using padded_page = typename micro_queue_type::padded_page; - using page_allocator_type = typename micro_queue_type::page_allocator_type; - using item_constructor_type = typename micro_queue_type::item_constructor_type; -private: - using page_allocator_traits = tbb::detail::allocator_traits; - using queue_allocator_type = typename allocator_traits_type::template rebind_alloc; - -public: - // must be power of 2 - static constexpr size_type n_queue = 8; - // Approximately n_queue/golden ratio - static constexpr size_type phi = 3; - static constexpr size_type item_size = micro_queue_type::item_size; - static constexpr size_type items_per_page = micro_queue_type::items_per_page; - - concurrent_queue_rep() {} - - concurrent_queue_rep( const concurrent_queue_rep& ) = delete; - concurrent_queue_rep& operator=( const concurrent_queue_rep& ) = delete; - - void clear( queue_allocator_type& alloc ) { - for (size_type index = 0; index < n_queue; ++index) { - array[index].clear(alloc); - } - head_counter.store(0, std::memory_order_relaxed); - tail_counter.store(0, std::memory_order_relaxed); - n_invalid_entries.store(0, std::memory_order_relaxed); - } - - void assign( const concurrent_queue_rep& src, queue_allocator_type& alloc, item_constructor_type construct_item ) { - head_counter.store(src.head_counter.load(std::memory_order_relaxed), std::memory_order_relaxed); - tail_counter.store(src.tail_counter.load(std::memory_order_relaxed), std::memory_order_relaxed); - n_invalid_entries.store(src.n_invalid_entries.load(std::memory_order_relaxed), std::memory_order_relaxed); - - // copy or move micro_queues - size_type queue_idx = 0; - try_call( [&] { - for (; queue_idx < n_queue; ++queue_idx) { - array[queue_idx].assign(src.array[queue_idx], alloc, construct_item); - } - }).on_exception( [&] { - for (size_type i = 0; i < queue_idx + 1; ++i) { - array[i].clear_and_invalidate(alloc); - } - head_counter.store(0, std::memory_order_relaxed); - tail_counter.store(0, std::memory_order_relaxed); - n_invalid_entries.store(0, std::memory_order_relaxed); - }); - - __TBB_ASSERT(head_counter.load(std::memory_order_relaxed) == src.head_counter.load(std::memory_order_relaxed) && - tail_counter.load(std::memory_order_relaxed) == src.tail_counter.load(std::memory_order_relaxed), - "the source concurrent queue should not be concurrently modified." ); - } - - bool empty() const { - ticket_type tc = tail_counter.load(std::memory_order_acquire); - ticket_type hc = head_counter.load(std::memory_order_relaxed); - // if tc!=r.tail_counter, the queue was not empty at some point between the two reads. - return tc == tail_counter.load(std::memory_order_relaxed) && - std::ptrdiff_t(tc - hc - n_invalid_entries.load(std::memory_order_relaxed)) <= 0; - } - - std::ptrdiff_t size() const { - __TBB_ASSERT(sizeof(std::ptrdiff_t) <= sizeof(size_type), nullptr); - std::ptrdiff_t hc = head_counter.load(std::memory_order_acquire); - std::ptrdiff_t tc = tail_counter.load(std::memory_order_relaxed); - std::ptrdiff_t nie = n_invalid_entries.load(std::memory_order_relaxed); - - return tc - hc - nie; - } - - friend class micro_queue; - - // Map ticket_type to an array index - static size_type index( ticket_type k ) { - return k * phi % n_queue; - } - - micro_queue_type& choose( ticket_type k ) { - // The formula here approximates LRU in a cache-oblivious way. - return array[index(k)]; - } - - alignas(max_nfs_size) micro_queue_type array[n_queue]; - - alignas(max_nfs_size) std::atomic head_counter{}; - alignas(max_nfs_size) std::atomic tail_counter{}; - alignas(max_nfs_size) std::atomic n_invalid_entries{}; -}; // class concurrent_queue_rep - -#if _MSC_VER && !defined(__INTEL_COMPILER) -#pragma warning( pop ) -#endif - -template -class concurrent_queue_iterator_base { - using queue_rep_type = concurrent_queue_rep; - using padded_page = typename queue_rep_type::padded_page; -protected: - concurrent_queue_iterator_base() = default; - - concurrent_queue_iterator_base( const concurrent_queue_iterator_base& other ) { - assign(other); - } - - concurrent_queue_iterator_base( queue_rep_type* queue_rep ) - : my_queue_rep(queue_rep), - my_head_counter(my_queue_rep->head_counter.load(std::memory_order_relaxed)) - { - for (std::size_t i = 0; i < queue_rep_type::n_queue; ++i) { - my_array[i] = my_queue_rep->array[i].get_head_page(); - } - - if (!get_item(my_item, my_head_counter)) advance(); - } - - void assign( const concurrent_queue_iterator_base& other ) { - my_item = other.my_item; - my_queue_rep = other.my_queue_rep; - - if (my_queue_rep != nullptr) { - my_head_counter = other.my_head_counter; - - for (std::size_t i = 0; i < queue_rep_type::n_queue; ++i) { - my_array[i] = other.my_array[i]; - } - } - } - - void advance() { - __TBB_ASSERT(my_item, "Attempt to increment iterator past end of the queue"); - std::size_t k = my_head_counter; -#if TBB_USE_ASSERT - Value* tmp; - get_item(tmp, k); - __TBB_ASSERT(my_item == tmp, nullptr); -#endif - std::size_t i = modulo_power_of_two(k / queue_rep_type::n_queue, my_queue_rep->items_per_page); - if (i == my_queue_rep->items_per_page - 1) { - padded_page*& root = my_array[queue_rep_type::index(k)]; - root = root->next; - } - // Advance k - my_head_counter = ++k; - if (!get_item(my_item, k)) advance(); - } - - concurrent_queue_iterator_base& operator=( const concurrent_queue_iterator_base& other ) { - this->assign(other); - return *this; - } - - bool get_item( Value*& item, std::size_t k ) { - if (k == my_queue_rep->tail_counter.load(std::memory_order_relaxed)) { - item = nullptr; - return true; - } else { - padded_page* p = my_array[queue_rep_type::index(k)]; - __TBB_ASSERT(p, nullptr); - std::size_t i = modulo_power_of_two(k / queue_rep_type::n_queue, my_queue_rep->items_per_page); - item = &(*p)[i]; - return (p->mask & uintptr_t(1) << i) != 0; - } - } - - Value* my_item{ nullptr }; - queue_rep_type* my_queue_rep{ nullptr }; - ticket_type my_head_counter{}; - padded_page* my_array[queue_rep_type::n_queue]{}; -}; // class concurrent_queue_iterator_base - -struct concurrent_queue_iterator_provider { - template - static Iterator get( const Container& container ) { - return Iterator(container); - } -}; // struct concurrent_queue_iterator_provider - -template -class concurrent_queue_iterator : public concurrent_queue_iterator_base::type, Allocator> { - using base_type = concurrent_queue_iterator_base::type, Allocator>; -public: - using value_type = Value; - using pointer = value_type*; - using reference = value_type&; - using difference_type = std::ptrdiff_t; - using iterator_category = std::forward_iterator_tag; - - concurrent_queue_iterator() = default; - - /** If Value==Container::value_type, then this routine is the copy constructor. - If Value==const Container::value_type, then this routine is a conversion constructor. */ - concurrent_queue_iterator( const concurrent_queue_iterator& other ) - : base_type(other) {} - -private: - concurrent_queue_iterator( const Container& container ) - : base_type(container.my_queue_representation) {} -public: - concurrent_queue_iterator& operator=( const concurrent_queue_iterator& other ) { - this->assign(other); - return *this; - } - - reference operator*() const { - return *static_cast(this->my_item); - } - - pointer operator->() const { return &operator*(); } - - concurrent_queue_iterator& operator++() { - this->advance(); - return *this; - } - - concurrent_queue_iterator operator++(int) { - concurrent_queue_iterator tmp = *this; - ++*this; - return tmp; - } - - friend bool operator==( const concurrent_queue_iterator& lhs, const concurrent_queue_iterator& rhs ) { - return lhs.my_item == rhs.my_item; - } - - friend bool operator!=( const concurrent_queue_iterator& lhs, const concurrent_queue_iterator& rhs ) { - return lhs.my_item != rhs.my_item; - } -private: - friend struct concurrent_queue_iterator_provider; -}; // class concurrent_queue_iterator - -} // namespace d2 -} // namespace detail -} // tbb - -#endif // __TBB_detail__concurrent_queue_base_H diff --git a/include/oneapi/tbb/detail/_concurrent_skip_list.h b/include/oneapi/tbb/detail/_concurrent_skip_list.h deleted file mode 100644 index eaaaac8..0000000 --- a/include/oneapi/tbb/detail/_concurrent_skip_list.h +++ /dev/null @@ -1,1290 +0,0 @@ -/* - Copyright (c) 2019-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__concurrent_skip_list_H -#define __TBB_detail__concurrent_skip_list_H - -#if !defined(__TBB_concurrent_map_H) && !defined(__TBB_concurrent_set_H) -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -#include "_config.h" -#include "_range_common.h" -#include "_allocator_traits.h" -#include "_template_helpers.h" -#include "_node_handle.h" -#include "_containers_helpers.h" -#include "_assert.h" -#include "_exception.h" -#include "../enumerable_thread_specific.h" -#include -#include -#include -#include -#include -#include // Need std::geometric_distribution -#include // Need std::equal and std::lexicographical_compare -#include -#if __TBB_CPP20_COMPARISONS_PRESENT -#include -#endif - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -#pragma warning(push) -#pragma warning(disable: 4127) // warning C4127: conditional expression is constant -#endif - -namespace tbb { -namespace detail { -namespace d2 { - -template -class skip_list_node { - using node_ptr = skip_list_node*; -public: - using value_type = Value; - using atomic_node_ptr = std::atomic; - using size_type = std::size_t; - using container_allocator_type = Allocator; - - using reference = value_type&; - using const_reference = const value_type&; -private: - using allocator_traits = tbb::detail::allocator_traits; - - // Allocator is the same as the container allocator=> allocates unitptr_t - // It is required to rebind it to value_type to get the correct pointer and const_pointer - using value_allocator_traits = typename allocator_traits::template rebind_traits; -public: - using pointer = typename value_allocator_traits::pointer; - using const_pointer = typename value_allocator_traits::const_pointer; - - //In perfect world these constructor and destructor would have been private, - //however this seems technically impractical due to use of allocator_traits. - - //Should not be called directly, instead use create method - skip_list_node( size_type levels ) - : my_height(levels), my_index_number(0) - {} - - //Should not be called directly, instead use destroy method - ~skip_list_node() {} - - skip_list_node( const skip_list_node& ) = delete; - skip_list_node( skip_list_node&& ) = delete; - skip_list_node& operator=( const skip_list_node& ) = delete; - skip_list_node& operator=( skip_list_node&& ) = delete; - - static skip_list_node* create( container_allocator_type& alloc, size_type height ) { - size_type sz = calc_node_size(height); - static_assert(std::is_same::value, "skip_list_node assumes that passed in allocator operates on bytes"); - auto* node = reinterpret_cast(allocator_traits::allocate(alloc, sz)); - - //Construct the node itself - allocator_traits::construct(alloc, node, height); - - //Construct the level pointers - for (size_type l = 0; l < height; ++l) { - allocator_traits::construct(alloc, &node->get_atomic_next(l), nullptr); - } - - return node; - } - - static void destroy( container_allocator_type& alloc, skip_list_node* node ) { - //Destroy the level pointers - for (size_type l = 0; l < node->height(); ++l) { - allocator_traits::destroy(alloc, &node->atomic_next(l)); - } - size_type sz = calc_node_size(node->height()); - // Destroy the node itself - allocator_traits::destroy(alloc, node); - - // Deallocate the node - allocator_traits::deallocate(alloc, reinterpret_cast(node), sz); - } - - - pointer storage() { - return &my_value; - } - - reference value() { - return *storage(); - } - - node_ptr next( size_type level ) const { - node_ptr res = get_atomic_next(level).load(std::memory_order_acquire); - __TBB_ASSERT(res == nullptr || res->height() > level, "Broken internal structure"); - return res; - } - - atomic_node_ptr& atomic_next( size_type level ) { - atomic_node_ptr& res = get_atomic_next(level); -#if TBB_USE_DEBUG - node_ptr node = res.load(std::memory_order_acquire); - __TBB_ASSERT(node == nullptr || node->height() > level, "Broken internal structure"); -#endif - return res; - } - - void set_next( size_type level, node_ptr n ) { - __TBB_ASSERT(n == nullptr || n->height() > level, "Broken internal structure"); - get_atomic_next(level).store(n, std::memory_order_relaxed); - } - - size_type height() const { - return my_height; - } - - void set_index_number( size_type index_num ) { - my_index_number = index_num; - } - - size_type index_number() const { - return my_index_number; - } - -private: - static size_type calc_node_size( size_type height ) { - static_assert(alignof(skip_list_node) >= alignof(atomic_node_ptr), "Incorrect alignment"); - return sizeof(skip_list_node) + height * sizeof(atomic_node_ptr); - } - - atomic_node_ptr& get_atomic_next( size_type level ) { - atomic_node_ptr* arr = reinterpret_cast(this + 1); - return arr[level]; - } - - const atomic_node_ptr& get_atomic_next( size_type level ) const { - const atomic_node_ptr* arr = reinterpret_cast(this + 1); - return arr[level]; - } - - union { - value_type my_value; - }; - size_type my_height; - size_type my_index_number; -}; // class skip_list_node - -template -class skip_list_iterator { - using node_type = NodeType; - using node_ptr = node_type*; -public: - using iterator_category = std::forward_iterator_tag; - using value_type = ValueType; - - using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; - - skip_list_iterator() : skip_list_iterator(nullptr) {} - - skip_list_iterator( const skip_list_iterator& other ) - : my_node_ptr(other.my_node_ptr) {} - - skip_list_iterator& operator=( const skip_list_iterator& other ) { - my_node_ptr = other.my_node_ptr; - return *this; - } - - reference operator*() const { return my_node_ptr->value(); } - pointer operator->() const { return my_node_ptr->storage(); } - - skip_list_iterator& operator++() { - __TBB_ASSERT(my_node_ptr != nullptr, nullptr); - my_node_ptr = my_node_ptr->next(0); - return *this; - } - - skip_list_iterator operator++(int) { - skip_list_iterator tmp = *this; - ++*this; - return tmp; - } - -private: - skip_list_iterator(node_type* n) : my_node_ptr(n) {} - - node_ptr my_node_ptr; - - template - friend class concurrent_skip_list; - - template - friend class skip_list_iterator; - - friend class const_range; - friend class range; - - friend bool operator==( const skip_list_iterator& lhs, const skip_list_iterator& rhs ) { - return lhs.my_node_ptr == rhs.my_node_ptr; - } - - friend bool operator!=( const skip_list_iterator& lhs, const skip_list_iterator& rhs ) { - return lhs.my_node_ptr != rhs.my_node_ptr; - } -}; // class skip_list_iterator - -template -class concurrent_skip_list { -protected: - using container_traits = Traits; - using self_type = concurrent_skip_list; - using allocator_type = typename container_traits::allocator_type; - using allocator_traits_type = tbb::detail::allocator_traits; - using key_compare = typename container_traits::compare_type; - using value_compare = typename container_traits::value_compare; - using key_type = typename container_traits::key_type; - using value_type = typename container_traits::value_type; - static_assert(std::is_same::value, - "value_type of the container should be the same as its allocator"); - - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - static constexpr size_type max_level = container_traits::max_level; - - using node_allocator_type = typename allocator_traits_type::template rebind_alloc; - using node_allocator_traits = tbb::detail::allocator_traits; - - using list_node_type = skip_list_node; - using node_type = d1::node_handle; - - using iterator = skip_list_iterator; - using const_iterator = skip_list_iterator; - - using reference = value_type&; - using const_reference = const value_type&; - using pointer = typename allocator_traits_type::pointer; - using const_pointer = typename allocator_traits_type::const_pointer; - - using random_level_generator_type = typename container_traits::random_level_generator_type; - - using node_ptr = list_node_type*; - - using array_type = std::array; -private: - template - using is_transparent = dependent_bool, T>; -public: - static constexpr bool allow_multimapping = container_traits::allow_multimapping; - - concurrent_skip_list() : my_head_ptr(nullptr), my_size(0), my_max_height(0) {} - - explicit concurrent_skip_list( const key_compare& comp, const allocator_type& alloc = allocator_type() ) - : my_node_allocator(alloc), my_compare(comp), my_head_ptr(nullptr), my_size(0), my_max_height(0) {} - - explicit concurrent_skip_list( const allocator_type& alloc ) - : concurrent_skip_list(key_compare(), alloc) {} - - template - concurrent_skip_list( InputIterator first, InputIterator last, const key_compare& comp = key_compare(), - const allocator_type& alloc = allocator_type() ) - : concurrent_skip_list(comp, alloc) - { - internal_copy(first, last); - } - - template - concurrent_skip_list( InputIterator first, InputIterator last, const allocator_type& alloc ) - : concurrent_skip_list(first, last, key_compare(), alloc) {} - - concurrent_skip_list( std::initializer_list init, const key_compare& comp = key_compare(), - const allocator_type& alloc = allocator_type() ) - : concurrent_skip_list(init.begin(), init.end(), comp, alloc) {} - - concurrent_skip_list( std::initializer_list init, const allocator_type& alloc ) - : concurrent_skip_list(init, key_compare(), alloc) {} - - concurrent_skip_list( const concurrent_skip_list& other ) - : my_node_allocator(node_allocator_traits::select_on_container_copy_construction(other.get_allocator())), - my_compare(other.my_compare), my_rng(other.my_rng), my_head_ptr(nullptr), - my_size(0), my_max_height(0) - { - internal_copy(other); - __TBB_ASSERT(my_size == other.my_size, "Wrong size of copy-constructed container"); - } - - concurrent_skip_list( const concurrent_skip_list& other, const allocator_type& alloc ) - : my_node_allocator(alloc), my_compare(other.my_compare), my_rng(other.my_rng), my_head_ptr(nullptr), - my_size(0), my_max_height(0) - { - internal_copy(other); - __TBB_ASSERT(my_size == other.my_size, "Wrong size of copy-constructed container"); - } - - concurrent_skip_list( concurrent_skip_list&& other ) - : my_node_allocator(std::move(other.my_node_allocator)), my_compare(other.my_compare), - my_rng(std::move(other.my_rng)), my_head_ptr(nullptr) // my_head_ptr would be stored in internal_move - { - internal_move(std::move(other)); - } - - concurrent_skip_list( concurrent_skip_list&& other, const allocator_type& alloc ) - : my_node_allocator(alloc), my_compare(other.my_compare), - my_rng(std::move(other.my_rng)), my_head_ptr(nullptr) - { - using is_always_equal = typename allocator_traits_type::is_always_equal; - internal_move_construct_with_allocator(std::move(other), is_always_equal()); - } - - ~concurrent_skip_list() { - clear(); - delete_head(); - } - - concurrent_skip_list& operator=( const concurrent_skip_list& other ) { - if (this != &other) { - clear(); - copy_assign_allocators(my_node_allocator, other.my_node_allocator); - my_compare = other.my_compare; - my_rng = other.my_rng; - internal_copy(other); - } - return *this; - } - - concurrent_skip_list& operator=( concurrent_skip_list&& other ) { - if (this != &other) { - clear(); - delete_head(); - - my_compare = std::move(other.my_compare); - my_rng = std::move(other.my_rng); - - move_assign_allocators(my_node_allocator, other.my_node_allocator); - using pocma_type = typename node_allocator_traits::propagate_on_container_move_assignment; - using is_always_equal = typename node_allocator_traits::is_always_equal; - internal_move_assign(std::move(other), tbb::detail::disjunction()); - } - return *this; - } - - concurrent_skip_list& operator=( std::initializer_list il ) - { - clear(); - insert(il.begin(),il.end()); - return *this; - } - - std::pair insert( const value_type& value ) { - return internal_insert(value); - } - - std::pair insert( value_type&& value ) { - return internal_insert(std::move(value)); - } - - iterator insert( const_iterator, const_reference value ) { - // Ignore hint - return insert(value).first; - } - - iterator insert( const_iterator, value_type&& value ) { - // Ignore hint - return insert(std::move(value)).first; - } - - template - void insert( InputIterator first, InputIterator last ) { - while (first != last) { - insert(*first); - ++first; - } - } - - void insert( std::initializer_list init ) { - insert(init.begin(), init.end()); - } - - std::pair insert( node_type&& nh ) { - if (!nh.empty()) { - auto insert_node = d1::node_handle_accessor::get_node_ptr(nh); - std::pair insert_result = internal_insert_node(insert_node); - if (insert_result.second) { - d1::node_handle_accessor::deactivate(nh); - } - return insert_result; - } - return std::pair(end(), false); - } - - iterator insert( const_iterator, node_type&& nh ) { - // Ignore hint - return insert(std::move(nh)).first; - } - - template - std::pair emplace( Args&&... args ) { - return internal_insert(std::forward(args)...); - } - - template - iterator emplace_hint( const_iterator, Args&&... args ) { - // Ignore hint - return emplace(std::forward(args)...).first; - } - - iterator unsafe_erase( iterator pos ) { - std::pair extract_result = internal_extract(pos); - if (extract_result.first) { // node was extracted - delete_value_node(extract_result.first); - return extract_result.second; - } - return end(); - } - - iterator unsafe_erase( const_iterator pos ) { - return unsafe_erase(get_iterator(pos)); - } - - iterator unsafe_erase( const_iterator first, const_iterator last ) { - while (first != last) { - // Unsafe erase returns the iterator which follows the erased one - first = unsafe_erase(first); - } - return get_iterator(first); - } - - size_type unsafe_erase( const key_type& key ) { - return internal_erase(key); - } - - template - typename std::enable_if::value - && !std::is_convertible::value - && !std::is_convertible::value, - size_type>::type unsafe_erase( const K& key ) - { - return internal_erase(key); - } - - node_type unsafe_extract( const_iterator pos ) { - std::pair extract_result = internal_extract(pos); - return extract_result.first ? d1::node_handle_accessor::construct(extract_result.first) : node_type(); - } - - node_type unsafe_extract( iterator pos ) { - return unsafe_extract(const_iterator(pos)); - } - - node_type unsafe_extract( const key_type& key ) { - return unsafe_extract(find(key)); - } - - template - typename std::enable_if::value - && !std::is_convertible::value - && !std::is_convertible::value, - node_type>::type unsafe_extract( const K& key ) - { - return unsafe_extract(find(key)); - } - - iterator lower_bound( const key_type& key ) { - return iterator(internal_get_bound(key, my_compare)); - } - - const_iterator lower_bound( const key_type& key ) const { - return const_iterator(internal_get_bound(key, my_compare)); - } - - template - typename std::enable_if::value, iterator>::type lower_bound( const K& key ) { - return iterator(internal_get_bound(key, my_compare)); - } - - template - typename std::enable_if::value, const_iterator>::type lower_bound( const K& key ) const { - return const_iterator(internal_get_bound(key, my_compare)); - } - - iterator upper_bound( const key_type& key ) { - return iterator(internal_get_bound(key, not_greater_compare(my_compare))); - } - - const_iterator upper_bound( const key_type& key ) const { - return const_iterator(internal_get_bound(key, not_greater_compare(my_compare))); - } - - template - typename std::enable_if::value, iterator>::type upper_bound( const K& key ) { - return iterator(internal_get_bound(key, not_greater_compare(my_compare))); - } - - template - typename std::enable_if::value, const_iterator>::type upper_bound( const K& key ) const { - return const_iterator(internal_get_bound(key, not_greater_compare(my_compare))); - } - - iterator find( const key_type& key ) { - return iterator(internal_find(key)); - } - - const_iterator find( const key_type& key ) const { - return const_iterator(internal_find(key)); - } - - template - typename std::enable_if::value, iterator>::type find( const K& key ) { - return iterator(internal_find(key)); - } - - template - typename std::enable_if::value, const_iterator>::type find( const K& key ) const { - return const_iterator(internal_find(key)); - } - - size_type count( const key_type& key ) const { - return internal_count(key); - } - - template - typename std::enable_if::value, size_type>::type count( const K& key ) const { - return internal_count(key); - } - - bool contains( const key_type& key ) const { - return find(key) != end(); - } - - template - typename std::enable_if::value, bool>::type contains( const K& key ) const { - return find(key) != end(); - } - - void clear() noexcept { - // clear is not thread safe - load can be relaxed - node_ptr head = my_head_ptr.load(std::memory_order_relaxed); - - if (head == nullptr) return; // Head is not allocated => container is empty - - node_ptr current = head->next(0); - - // Delete all value nodes in the container - while (current) { - node_ptr next = current->next(0); - delete_value_node(current); - current = next; - } - - for (size_type level = 0; level < head->height(); ++level) { - head->set_next(level, nullptr); - } - - my_size.store(0, std::memory_order_relaxed); - my_max_height.store(0, std::memory_order_relaxed); - } - - iterator begin() { - return iterator(internal_begin()); - } - - const_iterator begin() const { - return const_iterator(internal_begin()); - } - - const_iterator cbegin() const { - return const_iterator(internal_begin()); - } - - iterator end() { - return iterator(nullptr); - } - - const_iterator end() const { - return const_iterator(nullptr); - } - - const_iterator cend() const { - return const_iterator(nullptr); - } - - size_type size() const { - return my_size.load(std::memory_order_relaxed); - } - - size_type max_size() const { - return node_allocator_traits::max_size(my_node_allocator); - } - - __TBB_nodiscard bool empty() const { - return 0 == size(); - } - - allocator_type get_allocator() const { - return my_node_allocator; - } - - void swap(concurrent_skip_list& other) { - if (this != &other) { - using pocs_type = typename node_allocator_traits::propagate_on_container_swap; - using is_always_equal = typename node_allocator_traits::is_always_equal; - internal_swap(other, tbb::detail::disjunction()); - } - } - - std::pair equal_range(const key_type& key) { - return internal_equal_range(key); - } - - std::pair equal_range(const key_type& key) const { - return internal_equal_range(key); - } - - template - typename std::enable_if::value, std::pair>::type equal_range( const K& key ) { - return internal_equal_range(key); - } - - template - typename std::enable_if::value, std::pair>::type equal_range( const K& key ) const { - return internal_equal_range(key); - } - - key_compare key_comp() const { return my_compare; } - - value_compare value_comp() const { return container_traits::value_comp(my_compare); } - - class const_range_type { - public: - using size_type = typename concurrent_skip_list::size_type; - using difference_type = typename concurrent_skip_list::difference_type; - using iterator = typename concurrent_skip_list::const_iterator; - using value_type = typename iterator::value_type; - using reference = typename iterator::reference; - - bool empty() const { - return my_begin.my_node_ptr ? (my_begin.my_node_ptr->next(0) == my_end.my_node_ptr) - : true; - } - - bool is_divisible() const { - return my_begin.my_node_ptr && my_level != 0 - ? my_begin.my_node_ptr->next(my_level - 1) != my_end.my_node_ptr - : false; - } - - size_type size() const { return std::distance(my_begin, my_end); } - - const_range_type( const_range_type& r, split) - : my_end(r.my_end) { - if (r.empty()) { - __TBB_ASSERT(my_end.my_node_ptr == nullptr, nullptr); - my_begin = my_end; - my_level = 0; - } else { - my_begin = iterator(r.my_begin.my_node_ptr->next(r.my_level - 1)); - my_level = my_begin.my_node_ptr->height(); - } - r.my_end = my_begin; - } - - const_range_type( const concurrent_skip_list& l) - : my_end(l.end()), my_begin(l.begin()), - my_level(my_begin.my_node_ptr ? my_begin.my_node_ptr->height() : 0) {} - - iterator begin() const { return my_begin; } - iterator end() const { return my_end; } - size_type grainsize() const { return 1; } - - private: - const_iterator my_end; - const_iterator my_begin; - size_type my_level; - }; // class const_range_type - - class range_type : public const_range_type { - public: - using iterator = typename concurrent_skip_list::iterator; - using value_type = typename iterator::value_type; - using reference = typename iterator::reference; - - range_type(range_type& r, split) : const_range_type(r, split()) {} - range_type(const concurrent_skip_list& l) : const_range_type(l) {} - - iterator begin() const { - node_ptr node = const_range_type::begin().my_node_ptr; - return iterator(node); - } - - iterator end() const { - node_ptr node = const_range_type::end().my_node_ptr; - return iterator(node); - } - }; // class range_type - - range_type range() { return range_type(*this); } - const_range_type range() const { return const_range_type(*this); } - -private: - node_ptr internal_begin() const { - node_ptr head = get_head(); - return head == nullptr ? head : head->next(0); - } - - void internal_move(concurrent_skip_list&& other) { - my_head_ptr.store(other.my_head_ptr.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.my_head_ptr.store(nullptr, std::memory_order_relaxed); - - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.my_size.store(0, std::memory_order_relaxed); - - my_max_height.store(other.my_max_height.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.my_max_height.store(0, std::memory_order_relaxed); - } - - void internal_move_construct_with_allocator(concurrent_skip_list&& other, - /*is_always_equal = */std::true_type) { - internal_move(std::move(other)); - } - - void internal_move_construct_with_allocator(concurrent_skip_list&& other, - /*is_always_equal = */std::false_type) { - if (my_node_allocator == other.get_allocator()) { - internal_move(std::move(other)); - } else { - my_size.store(0, std::memory_order_relaxed); - my_max_height.store(other.my_max_height.load(std::memory_order_relaxed), std::memory_order_relaxed); - internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end())); - } - } - - static const key_type& get_key( node_ptr n ) { - __TBB_ASSERT(n, nullptr); - return container_traits::get_key(static_cast(n)->value()); - } - - template - bool found( node_ptr node, const K& key ) const { - return node != nullptr && !my_compare(key, get_key(node)); - } - - template - node_ptr internal_find(const K& key) const { - return allow_multimapping ? internal_find_multi(key) : internal_find_unique(key); - } - - template - node_ptr internal_find_multi( const K& key ) const { - node_ptr prev = get_head(); - if (prev == nullptr) return nullptr; // If the head node is not allocated - exit - - node_ptr curr = nullptr; - node_ptr old_curr = curr; - - for (size_type h = my_max_height.load(std::memory_order_acquire); h > 0; --h) { - curr = internal_find_position(h - 1, prev, key, my_compare); - - if (curr != old_curr && found(curr, key)) { - return curr; - } - old_curr = curr; - } - return nullptr; - } - - template - node_ptr internal_find_unique( const K& key ) const { - const_iterator it = lower_bound(key); - return (it == end() || my_compare(key, container_traits::get_key(*it))) ? nullptr : it.my_node_ptr; - } - - template - size_type internal_count( const K& key ) const { - if (allow_multimapping) { - // TODO: reimplement without double traversal - std::pair r = equal_range(key); - return std::distance(r.first, r.second); - } - return size_type(contains(key) ? 1 : 0); - } - - template - std::pair internal_equal_range(const K& key) const { - iterator lb = get_iterator(lower_bound(key)); - auto result = std::make_pair(lb, lb); - - // If the lower bound points to the node with the requested key - if (found(lb.my_node_ptr, key)) { - - if (!allow_multimapping) { - // For unique containers - move the second iterator forward and exit - ++result.second; - } else { - // For multi containers - find the upper bound starting from the lower bound - node_ptr prev = lb.my_node_ptr; - node_ptr curr = nullptr; - not_greater_compare cmp(my_compare); - - // Start from the lower bound of the range - for (size_type h = prev->height(); h > 0; --h) { - curr = prev->next(h - 1); - while (curr && cmp(get_key(curr), key)) { - prev = curr; - // If the height of the next node is greater than the current one - jump to its height - if (h < curr->height()) { - h = curr->height(); - } - curr = prev->next(h - 1); - } - } - result.second = iterator(curr); - } - } - - return result; - } - - // Finds position on the level using comparator cmp starting from the node prev - template - node_ptr internal_find_position( size_type level, node_ptr& prev, const K& key, - const Comparator& cmp ) const { - __TBB_ASSERT(level < prev->height(), "Wrong level to find position"); - node_ptr curr = prev->next(level); - - while (curr && cmp(get_key(curr), key)) { - prev = curr; - __TBB_ASSERT(level < prev->height(), nullptr); - curr = prev->next(level); - } - - return curr; - } - - // The same as previous overload, but allows index_number comparison - template - node_ptr internal_find_position( size_type level, node_ptr& prev, node_ptr node, - const Comparator& cmp ) const { - __TBB_ASSERT(level < prev->height(), "Wrong level to find position"); - node_ptr curr = prev->next(level); - - while (curr && cmp(get_key(curr), get_key(node))) { - if (allow_multimapping && cmp(get_key(node), get_key(curr)) && curr->index_number() > node->index_number()) { - break; - } - - prev = curr; - __TBB_ASSERT(level < prev->height(), nullptr); - curr = prev->next(level); - } - return curr; - } - - template - void fill_prev_curr_arrays(array_type& prev_nodes, array_type& curr_nodes, node_ptr node, const key_type& key, - const Comparator& cmp, node_ptr head ) { - - size_type curr_max_height = my_max_height.load(std::memory_order_acquire); - size_type node_height = node->height(); - if (curr_max_height < node_height) { - std::fill(prev_nodes.begin() + curr_max_height, prev_nodes.begin() + node_height, head); - std::fill(curr_nodes.begin() + curr_max_height, curr_nodes.begin() + node_height, nullptr); - } - - node_ptr prev = head; - for (size_type level = curr_max_height; level > 0; --level) { - node_ptr curr = internal_find_position(level - 1, prev, key, cmp); - prev_nodes[level - 1] = prev; - curr_nodes[level - 1] = curr; - } - } - - void fill_prev_array_for_existing_node( array_type& prev_nodes, node_ptr node ) { - node_ptr head = create_head_if_necessary(); - prev_nodes.fill(head); - - node_ptr prev = head; - for (size_type level = node->height(); level > 0; --level) { - while (prev->next(level - 1) != node) { - prev = prev->next(level - 1); - } - prev_nodes[level - 1] = prev; - } - } - - struct not_greater_compare { - const key_compare& my_less_compare; - - not_greater_compare( const key_compare& less_compare ) : my_less_compare(less_compare) {} - - template - bool operator()( const K1& first, const K2& second ) const { - return !my_less_compare(second, first); - } - }; - - not_greater_compare select_comparator( /*allow_multimapping = */ std::true_type ) { - return not_greater_compare(my_compare); - } - - key_compare select_comparator( /*allow_multimapping = */ std::false_type ) { - return my_compare; - } - - template - std::pair internal_insert( Args&&... args ) { - node_ptr new_node = create_value_node(std::forward(args)...); - std::pair insert_result = internal_insert_node(new_node); - if (!insert_result.second) { - delete_value_node(new_node); - } - return insert_result; - } - - std::pair internal_insert_node( node_ptr new_node ) { - array_type prev_nodes; - array_type curr_nodes; - size_type new_height = new_node->height(); - auto compare = select_comparator(std::integral_constant{}); - - node_ptr head_node = create_head_if_necessary(); - - for (;;) { - fill_prev_curr_arrays(prev_nodes, curr_nodes, new_node, get_key(new_node), compare, head_node); - - node_ptr prev = prev_nodes[0]; - node_ptr next = curr_nodes[0]; - - if (allow_multimapping) { - new_node->set_index_number(prev->index_number() + 1); - } else { - if (found(next, get_key(new_node))) { - return std::pair(iterator(next), false); - } - } - - new_node->set_next(0, next); - if (!prev->atomic_next(0).compare_exchange_strong(next, new_node)) { - continue; - } - - // If the node was successfully linked on the first level - it will be linked on other levels - // Insertion cannot fail starting from this point - - // If the height of inserted node is greater than maximum - increase maximum - size_type max_height = my_max_height.load(std::memory_order_acquire); - for (;;) { - if (new_height <= max_height || my_max_height.compare_exchange_strong(max_height, new_height)) { - // If the maximum was successfully updated by current thread - // or by an other thread for the value, greater or equal to new_height - break; - } - } - - for (std::size_t level = 1; level < new_height; ++level) { - // Link the node on upper levels - for (;;) { - prev = prev_nodes[level]; - next = static_cast(curr_nodes[level]); - - new_node->set_next(level, next); - __TBB_ASSERT(new_node->height() > level, "Internal structure break"); - if (prev->atomic_next(level).compare_exchange_strong(next, new_node)) { - break; - } - - for (size_type lev = level; lev != new_height; ++lev ) { - curr_nodes[lev] = internal_find_position(lev, prev_nodes[lev], new_node, compare); - } - } - } - ++my_size; - return std::pair(iterator(new_node), true); - } - } - - template - node_ptr internal_get_bound( const K& key, const Comparator& cmp ) const { - node_ptr prev = get_head(); - if (prev == nullptr) return nullptr; // If the head node is not allocated - exit - - node_ptr curr = nullptr; - - for (size_type h = my_max_height.load(std::memory_order_acquire); h > 0; --h) { - curr = internal_find_position(h - 1, prev, key, cmp); - } - - return curr; - } - - template - size_type internal_erase( const K& key ) { - auto eq = equal_range(key); - size_type old_size = size(); - unsafe_erase(eq.first, eq.second); - return old_size - size(); - } - - // Returns node_ptr to the extracted node and node_ptr to the next node after the extracted - std::pair internal_extract( const_iterator it ) { - std::pair result(nullptr, nullptr); - if ( it != end() ) { - array_type prev_nodes; - - node_ptr erase_node = it.my_node_ptr; - node_ptr next_node = erase_node->next(0); - fill_prev_array_for_existing_node(prev_nodes, erase_node); - - for (size_type level = 0; level < erase_node->height(); ++level) { - prev_nodes[level]->set_next(level, erase_node->next(level)); - erase_node->set_next(level, nullptr); - } - my_size.fetch_sub(1, std::memory_order_relaxed); - - result.first = erase_node; - result.second = next_node; - } - return result; - } - -protected: - template - void internal_merge( SourceType&& source ) { - using source_type = typename std::decay::type; - using source_iterator = typename source_type::iterator; - static_assert((std::is_same::value), "Incompatible containers cannot be merged"); - - for (source_iterator it = source.begin(); it != source.end();) { - source_iterator where = it++; - if (allow_multimapping || !contains(container_traits::get_key(*where))) { - node_type handle = source.unsafe_extract(where); - __TBB_ASSERT(!handle.empty(), "Extracted handle in merge is empty"); - - if (!insert(std::move(handle)).second) { - __TBB_ASSERT(!handle.empty(), "Handle should not be empty if insert fails"); - //If the insertion fails - return the node into source - source.insert(std::move(handle)); - } - __TBB_ASSERT(handle.empty(), "Node handle should be empty after the insertion"); - } - } - } - -private: - void internal_copy( const concurrent_skip_list& other ) { - internal_copy(other.begin(), other.end()); - } - - template - void internal_copy( Iterator first, Iterator last ) { - try_call([&] { - for (auto it = first; it != last; ++it) { - insert(*it); - } - }).on_exception([&] { - clear(); - delete_head(); - }); - } - - node_ptr create_node( size_type height ) { - return list_node_type::create(my_node_allocator, height); - } - - template - node_ptr create_value_node( Args&&... args ) { - node_ptr node = create_node(my_rng()); - - // try_call API is not convenient here due to broken - // variadic capture on GCC 4.8.5 - auto value_guard = make_raii_guard([&] { - delete_node(node); - }); - - // Construct the value inside the node - node_allocator_traits::construct(my_node_allocator, node->storage(), std::forward(args)...); - value_guard.dismiss(); - return node; - } - - node_ptr create_head_node() { - return create_node(max_level); - } - - void delete_head() { - node_ptr head = my_head_ptr.load(std::memory_order_relaxed); - if (head != nullptr) { - delete_node(head); - my_head_ptr.store(nullptr, std::memory_order_relaxed); - } - } - - void delete_node( node_ptr node ) { - list_node_type::destroy(my_node_allocator, node); - } - - void delete_value_node( node_ptr node ) { - // Destroy the value inside the node - node_allocator_traits::destroy(my_node_allocator, node->storage()); - delete_node(node); - } - - node_ptr get_head() const { - return my_head_ptr.load(std::memory_order_acquire); - } - - node_ptr create_head_if_necessary() { - node_ptr current_head = get_head(); - if (current_head == nullptr) { - // Head node was not created - create it - node_ptr new_head = create_head_node(); - if (my_head_ptr.compare_exchange_strong(current_head, new_head)) { - current_head = new_head; - } else { - // If an other thread has already created the head node - destroy new_head - // current_head now points to the actual head node - delete_node(new_head); - } - } - __TBB_ASSERT(my_head_ptr.load(std::memory_order_relaxed) != nullptr, nullptr); - __TBB_ASSERT(current_head != nullptr, nullptr); - return current_head; - } - - static iterator get_iterator( const_iterator it ) { - return iterator(it.my_node_ptr); - } - - void internal_move_assign( concurrent_skip_list&& other, /*POCMA || is_always_equal =*/std::true_type ) { - internal_move(std::move(other)); - } - - void internal_move_assign( concurrent_skip_list&& other, /*POCMA || is_always_equal =*/std::false_type ) { - if (my_node_allocator == other.my_node_allocator) { - internal_move(std::move(other)); - } else { - internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end())); - } - } - - void internal_swap_fields( concurrent_skip_list& other ) { - using std::swap; - swap_allocators(my_node_allocator, other.my_node_allocator); - swap(my_compare, other.my_compare); - swap(my_rng, other.my_rng); - - swap_atomics_relaxed(my_head_ptr, other.my_head_ptr); - swap_atomics_relaxed(my_size, other.my_size); - swap_atomics_relaxed(my_max_height, other.my_max_height); - } - - void internal_swap( concurrent_skip_list& other, /*POCMA || is_always_equal =*/std::true_type ) { - internal_swap_fields(other); - } - - void internal_swap( concurrent_skip_list& other, /*POCMA || is_always_equal =*/std::false_type ) { - __TBB_ASSERT(my_node_allocator == other.my_node_allocator, "Swapping with unequal allocators is not allowed"); - internal_swap_fields(other); - } - - node_allocator_type my_node_allocator; - key_compare my_compare; - random_level_generator_type my_rng; - std::atomic my_head_ptr; - std::atomic my_size; - std::atomic my_max_height; - - template - friend class concurrent_skip_list; -}; // class concurrent_skip_list - -template -bool operator==( const concurrent_skip_list& lhs, const concurrent_skip_list& rhs ) { - if (lhs.size() != rhs.size()) return false; -#if _MSC_VER - // Passing "unchecked" iterators to std::equal with 3 parameters - // causes compiler warnings. - // The workaround is to use overload with 4 parameters, which is - // available since C++14 - minimally supported version on MSVC - return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); -#else - return std::equal(lhs.begin(), lhs.end(), rhs.begin()); -#endif -} - -#if !__TBB_CPP20_COMPARISONS_PRESENT -template -bool operator!=( const concurrent_skip_list& lhs, const concurrent_skip_list& rhs ) { - return !(lhs == rhs); -} -#endif - -#if __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT -template -tbb::detail::synthesized_three_way_result -operator<=>( const concurrent_skip_list& lhs, const concurrent_skip_list& rhs ) { - return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), - rhs.begin(), rhs.end(), - tbb::detail::synthesized_three_way_comparator{}); -} -#else -template -bool operator<( const concurrent_skip_list& lhs, const concurrent_skip_list& rhs ) { - return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); -} - -template -bool operator>( const concurrent_skip_list& lhs, const concurrent_skip_list& rhs ) { - return rhs < lhs; -} - -template -bool operator<=( const concurrent_skip_list& lhs, const concurrent_skip_list& rhs ) { - return !(rhs < lhs); -} - -template -bool operator>=( const concurrent_skip_list& lhs, const concurrent_skip_list& rhs ) { - return !(lhs < rhs); -} -#endif // __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT - -// Generates a number from the interval [0, MaxLevel). -template -class concurrent_geometric_level_generator { -public: - static constexpr std::size_t max_level = MaxLevel; - // TODO: modify the algorithm to accept other values of max_level - static_assert(max_level == 32, "Incompatible max_level for rng"); - - concurrent_geometric_level_generator() : engines(std::minstd_rand::result_type(time(nullptr))) {} - - std::size_t operator()() { - // +1 is required to pass at least 1 into log2 (log2(0) is undefined) - // -1 is required to have an ability to return 0 from the generator (max_level - log2(2^31) - 1) - std::size_t result = max_level - std::size_t(tbb::detail::log2(engines.local()() + 1)) - 1; - __TBB_ASSERT(result <= max_level, nullptr); - return result; - } - -private: - tbb::enumerable_thread_specific engines; -}; - -} // namespace d2 - -} // namespace detail -} // namespace tbb - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -#pragma warning(pop) // warning 4127 is back -#endif - -#endif // __TBB_detail__concurrent_skip_list_H diff --git a/include/oneapi/tbb/detail/_concurrent_unordered_base.h b/include/oneapi/tbb/detail/_concurrent_unordered_base.h deleted file mode 100644 index ade91c3..0000000 --- a/include/oneapi/tbb/detail/_concurrent_unordered_base.h +++ /dev/null @@ -1,1514 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__concurrent_unordered_base_H -#define __TBB_detail__concurrent_unordered_base_H - -#if !defined(__TBB_concurrent_unordered_map_H) && !defined(__TBB_concurrent_unordered_set_H) -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -#include "_range_common.h" -#include "_containers_helpers.h" -#include "_segment_table.h" -#include "_hash_compare.h" -#include "_allocator_traits.h" -#include "_node_handle.h" -#include "_assert.h" -#include "_utils.h" -#include "_exception.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -#pragma warning(push) -#pragma warning(disable: 4127) // warning C4127: conditional expression is constant -#endif - -namespace tbb { -namespace detail { -namespace d1 { - -template -class concurrent_unordered_base; - -template -class solist_iterator { -private: - using node_ptr = typename Container::value_node_ptr; - template - friend class split_ordered_list; - template - friend class solist_iterator; - template - friend class concurrent_unordered_base; - template - friend bool operator==( const solist_iterator& i, const solist_iterator& j ); - template - friend bool operator!=( const solist_iterator& i, const solist_iterator& j ); -public: - using value_type = Value; - using difference_type = typename Container::difference_type; - using pointer = value_type*; - using reference = value_type&; - using iterator_category = std::forward_iterator_tag; - - solist_iterator() : my_node_ptr(nullptr) {} - solist_iterator( const solist_iterator& other ) - : my_node_ptr(other.my_node_ptr) {} - - solist_iterator& operator=( const solist_iterator& other ) { - my_node_ptr = other.my_node_ptr; - return *this; - } - - reference operator*() const { - return my_node_ptr->value(); - } - - pointer operator->() const { - return my_node_ptr->storage(); - } - - solist_iterator& operator++() { - auto next_node = my_node_ptr->next(); - while(next_node && next_node->is_dummy()) { - next_node = next_node->next(); - } - my_node_ptr = static_cast(next_node); - return *this; - } - - solist_iterator operator++(int) { - solist_iterator tmp = *this; - ++*this; - return tmp; - } - -private: - solist_iterator( node_ptr pnode ) : my_node_ptr(pnode) {} - - node_ptr get_node_ptr() const { return my_node_ptr; } - - node_ptr my_node_ptr; -}; - -template -bool operator==( const solist_iterator& i, const solist_iterator& j ) { - return i.my_node_ptr == j.my_node_ptr; -} - -template -bool operator!=( const solist_iterator& i, const solist_iterator& j ) { - return i.my_node_ptr != j.my_node_ptr; -} - -template -class list_node { -public: - using node_ptr = list_node*; - using sokey_type = SokeyType; - - list_node(sokey_type key) : my_next(nullptr), my_order_key(key) {} - - void init( sokey_type key ) { - my_order_key = key; - } - - sokey_type order_key() const { - return my_order_key; - } - - bool is_dummy() { - // The last bit of order key is unset for dummy nodes - return (my_order_key & 0x1) == 0; - } - - node_ptr next() const { - return my_next.load(std::memory_order_acquire); - } - - void set_next( node_ptr next_node ) { - my_next.store(next_node, std::memory_order_release); - } - - bool try_set_next( node_ptr expected_next, node_ptr new_next ) { - return my_next.compare_exchange_strong(expected_next, new_next); - } - -private: - std::atomic my_next; - sokey_type my_order_key; -}; // class list_node - -template -class value_node : public list_node -{ -public: - using base_type = list_node; - using sokey_type = typename base_type::sokey_type; - using value_type = ValueType; - - value_node( sokey_type ord_key ) : base_type(ord_key) {} - ~value_node() {} - value_type* storage() { - return reinterpret_cast(&my_value); - } - - value_type& value() { - return *storage(); - } - -private: - using aligned_storage_type = typename std::aligned_storage::type; - aligned_storage_type my_value; -}; // class value_node - -template -class concurrent_unordered_base { - using self_type = concurrent_unordered_base; - using traits_type = Traits; - using hash_compare_type = typename traits_type::hash_compare_type; - class unordered_segment_table; -public: - using value_type = typename traits_type::value_type; - using key_type = typename traits_type::key_type; - using allocator_type = typename traits_type::allocator_type; - -private: - using allocator_traits_type = tbb::detail::allocator_traits; - // TODO: check assert conditions for different C++ standards - static_assert(std::is_same::value, - "value_type of the container must be the same as its allocator"); - using sokey_type = std::size_t; - -public: - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - using iterator = solist_iterator; - using const_iterator = solist_iterator; - using local_iterator = iterator; - using const_local_iterator = const_iterator; - - using reference = value_type&; - using const_reference = const value_type&; - using pointer = typename allocator_traits_type::pointer; - using const_pointer = typename allocator_traits_type::const_pointer; - - using hasher = typename hash_compare_type::hasher; - using key_equal = typename hash_compare_type::key_equal; - -private: - using list_node_type = list_node; - using value_node_type = value_node; - using node_ptr = list_node_type*; - using value_node_ptr = value_node_type*; - - using value_node_allocator_type = typename allocator_traits_type::template rebind_alloc; - using node_allocator_type = typename allocator_traits_type::template rebind_alloc; - - using node_allocator_traits = tbb::detail::allocator_traits; - using value_node_allocator_traits = tbb::detail::allocator_traits; - - static constexpr size_type round_up_to_power_of_two( size_type bucket_count ) { - return size_type(1) << size_type(tbb::detail::log2(uintptr_t(bucket_count == 0 ? 1 : bucket_count) * 2 - 1)); - } - - template - using is_transparent = dependent_bool, T>; -public: - using node_type = node_handle; - - explicit concurrent_unordered_base( size_type bucket_count, const hasher& hash = hasher(), - const key_equal& equal = key_equal(), const allocator_type& alloc = allocator_type() ) - : my_size(0), - my_bucket_count(round_up_to_power_of_two(bucket_count)), - my_max_load_factor(float(initial_max_load_factor)), - my_hash_compare(hash, equal), - my_head(sokey_type(0)), - my_segments(alloc) {} - - concurrent_unordered_base() : concurrent_unordered_base(initial_bucket_count) {} - - concurrent_unordered_base( size_type bucket_count, const allocator_type& alloc ) - : concurrent_unordered_base(bucket_count, hasher(), key_equal(), alloc) {} - - concurrent_unordered_base( size_type bucket_count, const hasher& hash, const allocator_type& alloc ) - : concurrent_unordered_base(bucket_count, hash, key_equal(), alloc) {} - - explicit concurrent_unordered_base( const allocator_type& alloc ) - : concurrent_unordered_base(initial_bucket_count, hasher(), key_equal(), alloc) {} - - template - concurrent_unordered_base( InputIterator first, InputIterator last, - size_type bucket_count = initial_bucket_count, const hasher& hash = hasher(), - const key_equal& equal = key_equal(), const allocator_type& alloc = allocator_type() ) - : concurrent_unordered_base(bucket_count, hash, equal, alloc) - { - insert(first, last); - } - - template - concurrent_unordered_base( InputIterator first, InputIterator last, - size_type bucket_count, const allocator_type& alloc ) - : concurrent_unordered_base(first, last, bucket_count, hasher(), key_equal(), alloc) {} - - template - concurrent_unordered_base( InputIterator first, InputIterator last, - size_type bucket_count, const hasher& hash, const allocator_type& alloc ) - : concurrent_unordered_base(first, last, bucket_count, hash, key_equal(), alloc) {} - - concurrent_unordered_base( const concurrent_unordered_base& other ) - : my_size(other.my_size.load(std::memory_order_relaxed)), - my_bucket_count(other.my_bucket_count.load(std::memory_order_relaxed)), - my_max_load_factor(other.my_max_load_factor), - my_hash_compare(other.my_hash_compare), - my_head(other.my_head.order_key()), - my_segments(other.my_segments) - { - try_call( [&] { - internal_copy(other); - } ).on_exception( [&] { - clear(); - }); - } - - concurrent_unordered_base( const concurrent_unordered_base& other, const allocator_type& alloc ) - : my_size(other.my_size.load(std::memory_order_relaxed)), - my_bucket_count(other.my_bucket_count.load(std::memory_order_relaxed)), - my_max_load_factor(other.my_max_load_factor), - my_hash_compare(other.my_hash_compare), - my_head(other.my_head.order_key()), - my_segments(other.my_segments, alloc) - { - try_call( [&] { - internal_copy(other); - } ).on_exception( [&] { - clear(); - }); - } - - concurrent_unordered_base( concurrent_unordered_base&& other ) - : my_size(other.my_size.load(std::memory_order_relaxed)), - my_bucket_count(other.my_bucket_count.load(std::memory_order_relaxed)), - my_max_load_factor(std::move(other.my_max_load_factor)), - my_hash_compare(std::move(other.my_hash_compare)), - my_head(other.my_head.order_key()), - my_segments(std::move(other.my_segments)) - { - move_content(std::move(other)); - } - - concurrent_unordered_base( concurrent_unordered_base&& other, const allocator_type& alloc ) - : my_size(other.my_size.load(std::memory_order_relaxed)), - my_bucket_count(other.my_bucket_count.load(std::memory_order_relaxed)), - my_max_load_factor(std::move(other.my_max_load_factor)), - my_hash_compare(std::move(other.my_hash_compare)), - my_head(other.my_head.order_key()), - my_segments(std::move(other.my_segments), alloc) - { - using is_always_equal = typename allocator_traits_type::is_always_equal; - internal_move_construct_with_allocator(std::move(other), alloc, is_always_equal()); - } - - concurrent_unordered_base( std::initializer_list init, - size_type bucket_count = initial_bucket_count, - const hasher& hash = hasher(), const key_equal& equal = key_equal(), - const allocator_type& alloc = allocator_type() ) - : concurrent_unordered_base(init.begin(), init.end(), bucket_count, hash, equal, alloc) {} - - concurrent_unordered_base( std::initializer_list init, - size_type bucket_count, const allocator_type& alloc ) - : concurrent_unordered_base(init, bucket_count, hasher(), key_equal(), alloc) {} - - concurrent_unordered_base( std::initializer_list init, - size_type bucket_count, const hasher& hash, const allocator_type& alloc ) - : concurrent_unordered_base(init, bucket_count, hash, key_equal(), alloc) {} - - ~concurrent_unordered_base() { - internal_clear(); - } - - concurrent_unordered_base& operator=( const concurrent_unordered_base& other ) { - if (this != &other) { - clear(); - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - my_bucket_count.store(other.my_bucket_count.load(std::memory_order_relaxed), std::memory_order_relaxed); - my_max_load_factor = other.my_max_load_factor; - my_hash_compare = other.my_hash_compare; - my_segments = other.my_segments; - internal_copy(other); // TODO: guards for exceptions? - } - return *this; - } - - concurrent_unordered_base& operator=( concurrent_unordered_base&& other ) noexcept(unordered_segment_table::is_noexcept_assignment) { - if (this != &other) { - clear(); - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - my_bucket_count.store(other.my_bucket_count.load(std::memory_order_relaxed), std::memory_order_relaxed); - my_max_load_factor = std::move(other.my_max_load_factor); - my_hash_compare = std::move(other.my_hash_compare); - my_segments = std::move(other.my_segments); - - using pocma_type = typename allocator_traits_type::propagate_on_container_move_assignment; - using is_always_equal = typename allocator_traits_type::is_always_equal; - internal_move_assign(std::move(other), tbb::detail::disjunction()); - } - return *this; - } - - concurrent_unordered_base& operator=( std::initializer_list init ) { - clear(); - insert(init); - return *this; - } - - void swap( concurrent_unordered_base& other ) noexcept(unordered_segment_table::is_noexcept_swap) { - if (this != &other) { - using pocs_type = typename allocator_traits_type::propagate_on_container_swap; - using is_always_equal = typename allocator_traits_type::is_always_equal; - internal_swap(other, tbb::detail::disjunction()); - } - } - - allocator_type get_allocator() const noexcept { return my_segments.get_allocator(); } - - iterator begin() noexcept { return iterator(first_value_node(&my_head)); } - const_iterator begin() const noexcept { return const_iterator(first_value_node(const_cast(&my_head))); } - const_iterator cbegin() const noexcept { return const_iterator(first_value_node(const_cast(&my_head))); } - - iterator end() noexcept { return iterator(nullptr); } - const_iterator end() const noexcept { return const_iterator(nullptr); } - const_iterator cend() const noexcept { return const_iterator(nullptr); } - - __TBB_nodiscard bool empty() const noexcept { return size() == 0; } - size_type size() const noexcept { return my_size.load(std::memory_order_relaxed); } - size_type max_size() const noexcept { return allocator_traits_type::max_size(get_allocator()); } - - void clear() noexcept { - internal_clear(); - } - - std::pair insert( const value_type& value ) { - return internal_insert_value(value); - } - - std::pair insert( value_type&& value ) { - return internal_insert_value(std::move(value)); - } - - iterator insert( const_iterator, const value_type& value ) { - // Ignore hint - return insert(value).first; - } - - iterator insert( const_iterator, value_type&& value ) { - // Ignore hint - return insert(std::move(value)).first; - } - - template - void insert( InputIterator first, InputIterator last ) { - for (; first != last; ++first) { - insert(*first); - } - } - - void insert( std::initializer_list init ) { - insert(init.begin(), init.end()); - } - - std::pair insert( node_type&& nh ) { - if (!nh.empty()) { - value_node_ptr insert_node = node_handle_accessor::get_node_ptr(nh); - auto init_node = [&insert_node]( sokey_type order_key )->value_node_ptr { - insert_node->init(order_key); - return insert_node; - }; - auto insert_result = internal_insert(insert_node->value(), init_node); - if (insert_result.inserted) { - // If the insertion succeeded - set node handle to the empty state - __TBB_ASSERT(insert_result.remaining_node == nullptr, - "internal_insert_node should not return the remaining node if the insertion succeeded"); - node_handle_accessor::deactivate(nh); - } - return { iterator(insert_result.node_with_equal_key), insert_result.inserted }; - } - return {end(), false}; - } - - iterator insert( const_iterator, node_type&& nh ) { - // Ignore hint - return insert(std::move(nh)).first; - } - - template - std::pair emplace( Args&&... args ) { - // Create a node with temporary order_key 0, which will be reinitialize - // in internal_insert after the hash calculation - value_node_ptr insert_node = create_node(0, std::forward(args)...); - - auto init_node = [&insert_node]( sokey_type order_key )->value_node_ptr { - insert_node->init(order_key); - return insert_node; - }; - - auto insert_result = internal_insert(insert_node->value(), init_node); - - if (!insert_result.inserted) { - // If the insertion failed - destroy the node which was created - insert_node->init(split_order_key_regular(1)); - destroy_node(insert_node); - } - - return { iterator(insert_result.node_with_equal_key), insert_result.inserted }; - } - - template - iterator emplace_hint( const_iterator, Args&&... args ) { - // Ignore hint - return emplace(std::forward(args)...).first; - } - - iterator unsafe_erase( const_iterator pos ) { - return iterator(first_value_node(internal_erase(pos.get_node_ptr()))); - } - - iterator unsafe_erase( iterator pos ) { - return iterator(first_value_node(internal_erase(pos.get_node_ptr()))); - } - - iterator unsafe_erase( const_iterator first, const_iterator last ) { - while(first != last) { - first = unsafe_erase(first); - } - return iterator(first.get_node_ptr()); - } - - size_type unsafe_erase( const key_type& key ) { - return internal_erase_by_key(key); - } - - template - typename std::enable_if::value - && !std::is_convertible::value - && !std::is_convertible::value, - size_type>::type unsafe_erase( const K& key ) - { - return internal_erase_by_key(key); - } - - node_type unsafe_extract( const_iterator pos ) { - internal_extract(pos.get_node_ptr()); - return node_handle_accessor::construct(pos.get_node_ptr()); - } - - node_type unsafe_extract( iterator pos ) { - internal_extract(pos.get_node_ptr()); - return node_handle_accessor::construct(pos.get_node_ptr()); - } - - node_type unsafe_extract( const key_type& key ) { - iterator item = find(key); - return item == end() ? node_type() : unsafe_extract(item); - } - - template - typename std::enable_if::value - && !std::is_convertible::value - && !std::is_convertible::value, - node_type>::type unsafe_extract( const K& key ) - { - iterator item = find(key); - return item == end() ? node_type() : unsafe_extract(item); - } - - // Lookup functions - iterator find( const key_type& key ) { - value_node_ptr result = internal_find(key); - return result == nullptr ? end() : iterator(result); - } - - const_iterator find( const key_type& key ) const { - value_node_ptr result = const_cast(this)->internal_find(key); - return result == nullptr ? end() : const_iterator(result); - } - - template - typename std::enable_if::value, iterator>::type find( const K& key ) { - value_node_ptr result = internal_find(key); - return result == nullptr ? end() : iterator(result); - } - - template - typename std::enable_if::value, const_iterator>::type find( const K& key ) const { - value_node_ptr result = const_cast(this)->internal_find(key); - return result == nullptr ? end() : const_iterator(result); - } - - std::pair equal_range( const key_type& key ) { - auto result = internal_equal_range(key); - return std::make_pair(iterator(result.first), iterator(result.second)); - } - - std::pair equal_range( const key_type& key ) const { - auto result = const_cast(this)->internal_equal_range(key); - return std::make_pair(const_iterator(result.first), const_iterator(result.second)); - } - - template - typename std::enable_if::value, std::pair>::type equal_range( const K& key ) { - auto result = internal_equal_range(key); - return std::make_pair(iterator(result.first), iterator(result.second)); - } - - template - typename std::enable_if::value, std::pair>::type equal_range( const K& key ) const { - auto result = const_cast(this)->internal_equal_range(key); - return std::make_pair(iterator(result.first), iterator(result.second)); - } - - size_type count( const key_type& key ) const { - return internal_count(key); - } - - template - typename std::enable_if::value, size_type>::type count( const K& key ) const { - return internal_count(key); - } - - bool contains( const key_type& key ) const { - return find(key) != end(); - } - - template - typename std::enable_if::value, bool>::type contains( const K& key ) const { - return find(key) != end(); - } - - // Bucket interface - local_iterator unsafe_begin( size_type n ) { - return local_iterator(first_value_node(get_bucket(n))); - } - - const_local_iterator unsafe_begin( size_type n ) const { - auto bucket_begin = first_value_node(const_cast(this)->get_bucket(n)); - return const_local_iterator(bucket_begin); - } - - const_local_iterator unsafe_cbegin( size_type n ) const { - auto bucket_begin = first_value_node(const_cast(this)->get_bucket(n)); - return const_local_iterator(bucket_begin); - } - - local_iterator unsafe_end( size_type n ) { - size_type bucket_count = my_bucket_count.load(std::memory_order_relaxed); - return n != bucket_count - 1 ? unsafe_begin(get_next_bucket_index(n)) : local_iterator(nullptr); - } - - const_local_iterator unsafe_end( size_type n ) const { - size_type bucket_count = my_bucket_count.load(std::memory_order_relaxed); - return n != bucket_count - 1 ? unsafe_begin(get_next_bucket_index(n)) : const_local_iterator(nullptr); - } - - const_local_iterator unsafe_cend( size_type n ) const { - size_type bucket_count = my_bucket_count.load(std::memory_order_relaxed); - return n != bucket_count - 1 ? unsafe_begin(get_next_bucket_index(n)) : const_local_iterator(nullptr); - } - - size_type unsafe_bucket_count() const { return my_bucket_count.load(std::memory_order_relaxed); } - - size_type unsafe_max_bucket_count() const { - return max_size(); - } - - size_type unsafe_bucket_size( size_type n ) const { - return size_type(std::distance(unsafe_begin(n), unsafe_end(n))); - } - - size_type unsafe_bucket( const key_type& key ) const { - return my_hash_compare(key) % my_bucket_count.load(std::memory_order_relaxed); - } - - // Hash policy - float load_factor() const { - return float(size() / float(my_bucket_count.load(std::memory_order_acquire))); - } - - float max_load_factor() const { return my_max_load_factor; } - - void max_load_factor( float mlf ) { - if (mlf != mlf || mlf < 0) { - tbb::detail::throw_exception(exception_id::invalid_load_factor); - } - my_max_load_factor = mlf; - } // TODO: unsafe? - - void rehash( size_type bucket_count ) { - size_type current_bucket_count = my_bucket_count.load(std::memory_order_acquire); - if (current_bucket_count < bucket_count) { - // TODO: do we need do-while here? - my_bucket_count.compare_exchange_strong(current_bucket_count, round_up_to_power_of_two(bucket_count)); - } - } - - void reserve( size_type elements_count ) { - size_type current_bucket_count = my_bucket_count.load(std::memory_order_acquire); - size_type necessary_bucket_count = current_bucket_count; - - // max_load_factor() is currently unsafe, so we can assume that my_max_load_factor - // would not be changed during the calculation - // TODO: Log2 seems useful here - while (necessary_bucket_count * max_load_factor() < elements_count) { - necessary_bucket_count <<= 1; - } - - while (!my_bucket_count.compare_exchange_strong(current_bucket_count, necessary_bucket_count)) { - if (current_bucket_count >= necessary_bucket_count) - break; - } - } - - // Observers - hasher hash_function() const { return my_hash_compare.hash_function(); } - key_equal key_eq() const { return my_hash_compare.key_eq(); } - - class const_range_type { - private: - const concurrent_unordered_base& my_instance; - node_ptr my_begin_node; // may be node* const - node_ptr my_end_node; - mutable node_ptr my_midpoint_node; - public: - using size_type = typename concurrent_unordered_base::size_type; - using value_type = typename concurrent_unordered_base::value_type; - using reference = typename concurrent_unordered_base::reference; - using difference_type = typename concurrent_unordered_base::difference_type; - using iterator = typename concurrent_unordered_base::const_iterator; - - bool empty() const { return my_begin_node == my_end_node; } - - bool is_divisible() const { - return my_midpoint_node != my_end_node; - } - - size_type grainsize() const { return 1; } - - const_range_type( const_range_type& range, split ) - : my_instance(range.my_instance), - my_begin_node(range.my_midpoint_node), - my_end_node(range.my_end_node) - { - range.my_end_node = my_begin_node; - __TBB_ASSERT(!empty(), "Splitting despite the range is not divisible"); - __TBB_ASSERT(!range.empty(), "Splitting despite the range is not divisible"); - set_midpoint(); - range.set_midpoint(); - } - - iterator begin() const { return iterator(my_instance.first_value_node(my_begin_node)); } - iterator end() const { return iterator(my_instance.first_value_node(my_end_node)); } - - const_range_type( const concurrent_unordered_base& table ) - : my_instance(table), my_begin_node(my_instance.first_value_node(const_cast(&table.my_head))), my_end_node(nullptr) - { - set_midpoint(); - } - private: - void set_midpoint() const { - if (empty()) { - my_midpoint_node = my_end_node; - } else { - sokey_type invalid_key = ~sokey_type(0); - sokey_type begin_key = my_begin_node != nullptr ? my_begin_node->order_key() : invalid_key; - sokey_type end_key = my_end_node != nullptr ? my_end_node->order_key() : invalid_key; - - size_type mid_bucket = reverse_bits(begin_key + (end_key - begin_key) / 2) % - my_instance.my_bucket_count.load(std::memory_order_relaxed); - while( my_instance.my_segments[mid_bucket].load(std::memory_order_relaxed) == nullptr) { - mid_bucket = my_instance.get_parent(mid_bucket); - } - if (reverse_bits(mid_bucket) > begin_key) { - // Found a dummy node between begin and end - my_midpoint_node = my_instance.first_value_node( - my_instance.my_segments[mid_bucket].load(std::memory_order_relaxed)); - } else { - // Didn't find a dummy node between begin and end - my_midpoint_node = my_end_node; - } - } - } - }; // class const_range_type - - class range_type : public const_range_type { - public: - using iterator = typename concurrent_unordered_base::iterator; - using const_range_type::const_range_type; - - iterator begin() const { return iterator(const_range_type::begin().get_node_ptr()); } - iterator end() const { return iterator(const_range_type::end().get_node_ptr()); } - }; // class range_type - - // Parallel iteration - range_type range() { - return range_type(*this); - } - - const_range_type range() const { - return const_range_type(*this); - } -protected: - static constexpr bool allow_multimapping = traits_type::allow_multimapping; - -private: - static constexpr size_type initial_bucket_count = 8; - static constexpr float initial_max_load_factor = 4; // TODO: consider 1? - static constexpr size_type pointers_per_embedded_table = sizeof(size_type) * 8 - 1; - - class unordered_segment_table - : public segment_table, allocator_type, unordered_segment_table, pointers_per_embedded_table> - { - using self_type = unordered_segment_table; - using atomic_node_ptr = std::atomic; - using base_type = segment_table, allocator_type, unordered_segment_table, pointers_per_embedded_table>; - using segment_type = typename base_type::segment_type; - using base_allocator_type = typename base_type::allocator_type; - - using segment_allocator_type = typename allocator_traits_type::template rebind_alloc; - using segment_allocator_traits = tbb::detail::allocator_traits; - public: - // Segment table for unordered containers should not be extended in the wait- free implementation - static constexpr bool allow_table_extending = false; - static constexpr bool is_noexcept_assignment = std::is_nothrow_move_assignable::value && - std::is_nothrow_move_assignable::value && - segment_allocator_traits::is_always_equal::value; - static constexpr bool is_noexcept_swap = tbb::detail::is_nothrow_swappable::value && - tbb::detail::is_nothrow_swappable::value && - segment_allocator_traits::is_always_equal::value; - - // TODO: using base_type::base_type is not compiling on Windows and Intel Compiler - investigate - unordered_segment_table( const base_allocator_type& alloc = base_allocator_type() ) - : base_type(alloc) {} - - unordered_segment_table( const unordered_segment_table& ) = default; - - unordered_segment_table( const unordered_segment_table& other, const base_allocator_type& alloc ) - : base_type(other, alloc) {} - - unordered_segment_table( unordered_segment_table&& ) = default; - - unordered_segment_table( unordered_segment_table&& other, const base_allocator_type& alloc ) - : base_type(std::move(other), alloc) {} - - unordered_segment_table& operator=( const unordered_segment_table& ) = default; - - unordered_segment_table& operator=( unordered_segment_table&& ) = default; - - segment_type create_segment( typename base_type::segment_table_type, typename base_type::segment_index_type segment_index, size_type ) { - segment_allocator_type alloc(this->get_allocator()); - size_type seg_size = this->segment_size(segment_index); - segment_type new_segment = segment_allocator_traits::allocate(alloc, seg_size); - for (size_type i = 0; i != seg_size; ++i) { - segment_allocator_traits::construct(alloc, new_segment + i, nullptr); - } - return new_segment; - } - - segment_type nullify_segment( typename base_type::segment_table_type table, size_type segment_index ) { - segment_type target_segment = table[segment_index].load(std::memory_order_relaxed); - table[segment_index].store(nullptr, std::memory_order_relaxed); - return target_segment; - } - - // deallocate_segment is required by the segment_table base class, but - // in unordered, it is also necessary to call the destructor during deallocation - void deallocate_segment( segment_type address, size_type index ) { - destroy_segment(address, index); - } - - void destroy_segment( segment_type address, size_type index ) { - segment_allocator_type alloc(this->get_allocator()); - for (size_type i = 0; i != this->segment_size(index); ++i) { - segment_allocator_traits::destroy(alloc, address + i); - } - segment_allocator_traits::deallocate(alloc, address, this->segment_size(index)); - } - - - void copy_segment( size_type index, segment_type, segment_type to ) { - if (index == 0) { - // The first element in the first segment is embedded into the table (my_head) - // so the first pointer should not be stored here - // It would be stored during move ctor/assignment operation - to[1].store(nullptr, std::memory_order_relaxed); - } else { - for (size_type i = 0; i != this->segment_size(index); ++i) { - to[i].store(nullptr, std::memory_order_relaxed); - } - } - } - - void move_segment( size_type index, segment_type from, segment_type to ) { - if (index == 0) { - // The first element in the first segment is embedded into the table (my_head) - // so the first pointer should not be stored here - // It would be stored during move ctor/assignment operation - to[1].store(from[1].load(std::memory_order_relaxed), std::memory_order_relaxed); - } else { - for (size_type i = 0; i != this->segment_size(index); ++i) { - to[i].store(from[i].load(std::memory_order_relaxed), std::memory_order_relaxed); - from[i].store(nullptr, std::memory_order_relaxed); - } - } - } - - // allocate_long_table is required by the segment_table base class, but unused for unordered containers - typename base_type::segment_table_type allocate_long_table( const typename base_type::atomic_segment*, size_type ) { - __TBB_ASSERT(false, "This method should never been called"); - // TableType is a pointer - return nullptr; - } - - // destroy_elements is required by the segment_table base class, but unused for unordered containers - // this function call but do nothing - void destroy_elements() {} - }; // struct unordered_segment_table - - void internal_clear() { - // TODO: consider usefulness of two versions of clear() - with dummy nodes deallocation and without it - node_ptr next = my_head.next(); - node_ptr curr = next; - - my_head.set_next(nullptr); - - while (curr != nullptr) { - next = curr->next(); - destroy_node(curr); - curr = next; - } - - my_size.store(0, std::memory_order_relaxed); - my_segments.clear(); - } - - void destroy_node( node_ptr node ) { - if (node->is_dummy()) { - node_allocator_type dummy_node_allocator(my_segments.get_allocator()); - // Destroy the node - node_allocator_traits::destroy(dummy_node_allocator, node); - // Deallocate the memory - node_allocator_traits::deallocate(dummy_node_allocator, node, 1); - } else { - // GCC 11.1 issues a warning here that incorrect destructor might be called for dummy_nodes - #if (__TBB_GCC_VERSION >= 110100 && __TBB_GCC_VERSION < 130000 ) && !__clang__ && !__INTEL_COMPILER - volatile - #endif - value_node_ptr val_node = static_cast(node); - value_node_allocator_type value_node_allocator(my_segments.get_allocator()); - // Destroy the value - value_node_allocator_traits::destroy(value_node_allocator, val_node->storage()); - // Destroy the node - value_node_allocator_traits::destroy(value_node_allocator, val_node); - // Deallocate the memory - value_node_allocator_traits::deallocate(value_node_allocator, val_node, 1); - } - } - - struct internal_insert_return_type { - // If the insertion failed - the remaining_node points to the node, which was failed to insert - // This node can be allocated in process of insertion - value_node_ptr remaining_node; - // If the insertion failed - node_with_equal_key points to the node in the list with the - // key, equivalent to the inserted, otherwise it points to the node, which was inserted. - value_node_ptr node_with_equal_key; - // Insertion status - // NOTE: if it is true - remaining_node should be nullptr - bool inserted; - }; // struct internal_insert_return_type - - // Inserts the value into the split ordered list - template - std::pair internal_insert_value( ValueType&& value ) { - - auto create_value_node = [&value, this]( sokey_type order_key )->value_node_ptr { - return create_node(order_key, std::forward(value)); - }; - - auto insert_result = internal_insert(value, create_value_node); - - if (insert_result.remaining_node != nullptr) { - // If the insertion fails - destroy the node which was failed to insert if it exist - __TBB_ASSERT(!insert_result.inserted, - "remaining_node should be nullptr if the node was successfully inserted"); - destroy_node(insert_result.remaining_node); - } - - return { iterator(insert_result.node_with_equal_key), insert_result.inserted }; - } - - // Inserts the node into the split ordered list - // Creates a node using the specified callback after the place for insertion was found - // Returns internal_insert_return_type object, where: - // - If the insertion succeeded: - // - remaining_node is nullptr - // - node_with_equal_key point to the inserted node - // - inserted is true - // - If the insertion failed: - // - remaining_node points to the node, that was failed to insert if it was created. - // nullptr if the node was not created, because the requested key was already - // presented in the list - // - node_with_equal_key point to the element in the list with the key, equivalent to - // to the requested key - // - inserted is false - template - internal_insert_return_type internal_insert( ValueType&& value, CreateInsertNode create_insert_node ) { - static_assert(std::is_same::type, value_type>::value, - "Incorrect type in internal_insert"); - const key_type& key = traits_type::get_key(value); - sokey_type hash_key = sokey_type(my_hash_compare(key)); - - sokey_type order_key = split_order_key_regular(hash_key); - node_ptr prev = prepare_bucket(hash_key); - __TBB_ASSERT(prev != nullptr, "Invalid head node"); - - auto search_result = search_after(prev, order_key, key); - - if (search_result.second) { - return internal_insert_return_type{ nullptr, search_result.first, false }; - } - - value_node_ptr new_node = create_insert_node(order_key); - node_ptr curr = search_result.first; - - while (!try_insert(prev, new_node, curr)) { - search_result = search_after(prev, order_key, key); - if (search_result.second) { - return internal_insert_return_type{ new_node, search_result.first, false }; - } - curr = search_result.first; - } - - auto sz = my_size.fetch_add(1); - adjust_table_size(sz + 1, my_bucket_count.load(std::memory_order_acquire)); - return internal_insert_return_type{ nullptr, static_cast(new_node), true }; - } - - // Searches the node with the key, equivalent to key with requested order key after the node prev - // Returns the existing node and true if the node is already in the list - // Returns the first node with the order key, greater than requested and false if the node is not presented in the list - std::pair search_after( node_ptr& prev, sokey_type order_key, const key_type& key ) { - // NOTE: static_cast(curr) should be done only after we would ensure - // that the node is not a dummy node - - node_ptr curr = prev->next(); - - while (curr != nullptr && (curr->order_key() < order_key || - (curr->order_key() == order_key && !my_hash_compare(traits_type::get_key(static_cast(curr)->value()), key)))) - { - prev = curr; - curr = curr->next(); - } - - if (curr != nullptr && curr->order_key() == order_key && !allow_multimapping) { - return { static_cast(curr), true }; - } - return { static_cast(curr), false }; - } - - void adjust_table_size( size_type total_elements, size_type current_size ) { - // Grow the table by a factor of 2 if possible and needed - if ( (float(total_elements) / float(current_size)) > my_max_load_factor ) { - // Double the size of the hash only if size hash not changed in between loads - my_bucket_count.compare_exchange_strong(current_size, 2u * current_size); - } - } - - node_ptr insert_dummy_node( node_ptr parent_dummy_node, sokey_type order_key ) { - node_ptr prev_node = parent_dummy_node; - - node_ptr dummy_node = create_dummy_node(order_key); - node_ptr next_node; - - do { - next_node = prev_node->next(); - // Move forward through the list while the order key is less than requested - while (next_node != nullptr && next_node->order_key() < order_key) { - prev_node = next_node; - next_node = next_node->next(); - } - - if (next_node != nullptr && next_node->order_key() == order_key) { - // Another dummy node with the same order key was inserted by another thread - // Destroy the node and exit - destroy_node(dummy_node); - return next_node; - } - } while (!try_insert(prev_node, dummy_node, next_node)); - - return dummy_node; - } - - // Try to insert a node between prev_node and expected next - // If the next is not equal to expected next - return false - static bool try_insert( node_ptr prev_node, node_ptr new_node, node_ptr current_next_node ) { - new_node->set_next(current_next_node); - return prev_node->try_set_next(current_next_node, new_node); - } - - // Returns the bucket, associated with the hash_key - node_ptr prepare_bucket( sokey_type hash_key ) { - size_type bucket = hash_key % my_bucket_count.load(std::memory_order_acquire); - return get_bucket(bucket); - } - - // Initialize the corresponding bucket if it is not initialized - node_ptr get_bucket( size_type bucket_index ) { - if (my_segments[bucket_index].load(std::memory_order_acquire) == nullptr) { - init_bucket(bucket_index); - } - return my_segments[bucket_index].load(std::memory_order_acquire); - } - - void init_bucket( size_type bucket ) { - if (bucket == 0) { - // Atomicaly store the first bucket into my_head - node_ptr disabled = nullptr; - my_segments[0].compare_exchange_strong(disabled, &my_head); - return; - } - - size_type parent_bucket = get_parent(bucket); - - while (my_segments[parent_bucket].load(std::memory_order_acquire) == nullptr) { - // Initialize all of the parent buckets - init_bucket(parent_bucket); - } - - __TBB_ASSERT(my_segments[parent_bucket].load(std::memory_order_acquire) != nullptr, "Parent bucket should be initialized"); - node_ptr parent = my_segments[parent_bucket].load(std::memory_order_acquire); - - // Insert dummy node into the list - node_ptr dummy_node = insert_dummy_node(parent, split_order_key_dummy(bucket)); - // TODO: consider returning pair to avoid store operation if the bucket was stored by an other thread - // or move store to insert_dummy_node - // Add dummy_node into the segment table - my_segments[bucket].store(dummy_node, std::memory_order_release); - } - - node_ptr create_dummy_node( sokey_type order_key ) { - node_allocator_type dummy_node_allocator(my_segments.get_allocator()); - node_ptr dummy_node = node_allocator_traits::allocate(dummy_node_allocator, 1); - node_allocator_traits::construct(dummy_node_allocator, dummy_node, order_key); - return dummy_node; - } - - template - value_node_ptr create_node( sokey_type order_key, Args&&... args ) { - value_node_allocator_type value_node_allocator(my_segments.get_allocator()); - // Allocate memory for the value_node - value_node_ptr new_node = value_node_allocator_traits::allocate(value_node_allocator, 1); - // Construct the node - value_node_allocator_traits::construct(value_node_allocator, new_node, order_key); - - // try_call API is not convenient here due to broken - // variadic capture on GCC 4.8.5 - auto value_guard = make_raii_guard([&] { - value_node_allocator_traits::destroy(value_node_allocator, new_node); - value_node_allocator_traits::deallocate(value_node_allocator, new_node, 1); - }); - - // Construct the value in the node - value_node_allocator_traits::construct(value_node_allocator, new_node->storage(), std::forward(args)...); - value_guard.dismiss(); - return new_node; - } - - value_node_ptr first_value_node( node_ptr first_node ) const { - while (first_node != nullptr && first_node->is_dummy()) { - first_node = first_node->next(); - } - return static_cast(first_node); - } - - // Unsafe method, which removes the node from the list and returns the next node - node_ptr internal_erase( value_node_ptr node_to_erase ) { - __TBB_ASSERT(node_to_erase != nullptr, "Invalid iterator for erase"); - node_ptr next_node = node_to_erase->next(); - internal_extract(node_to_erase); - destroy_node(node_to_erase); - return next_node; - } - - template - size_type internal_erase_by_key( const K& key ) { - // TODO: consider reimplementation without equal_range - it is not effective to perform lookup over a bucket - // for each unsafe_erase call - auto eq_range = equal_range(key); - size_type erased_count = 0; - - for (auto it = eq_range.first; it != eq_range.second;) { - it = unsafe_erase(it); - ++erased_count; - } - return erased_count; - } - - // Unsafe method, which extracts the node from the list - void internal_extract( value_node_ptr node_to_extract ) { - const key_type& key = traits_type::get_key(node_to_extract->value()); - sokey_type hash_key = sokey_type(my_hash_compare(key)); - - node_ptr prev_node = prepare_bucket(hash_key); - - for (node_ptr node = prev_node->next(); node != nullptr; prev_node = node, node = node->next()) { - if (node == node_to_extract) { - unlink_node(prev_node, node, node_to_extract->next()); - my_size.store(my_size.load(std::memory_order_relaxed) - 1, std::memory_order_relaxed); - return; - } - __TBB_ASSERT(node->order_key() <= node_to_extract->order_key(), - "node, which is going to be extracted should be presented in the list"); - } - } - -protected: - template - void internal_merge( SourceType&& source ) { - static_assert(std::is_same::type::node_type>::value, - "Incompatible containers cannot be merged"); - - for (node_ptr source_prev = &source.my_head; source_prev->next() != nullptr;) { - if (!source_prev->next()->is_dummy()) { - value_node_ptr curr = static_cast(source_prev->next()); - // If the multimapping is allowed, or the key is not presented - // in the *this container - extract the node from the list - if (allow_multimapping || !contains(traits_type::get_key(curr->value()))) { - node_ptr next_node = curr->next(); - source.unlink_node(source_prev, curr, next_node); - - // Remember the old order key - sokey_type old_order_key = curr->order_key(); - - // Node handle with curr cannot be used directly in insert call, because - // the destructor of node_type will destroy curr - node_type curr_node = node_handle_accessor::construct(curr); - - // If the insertion fails - return ownership of the node to the source - if (!insert(std::move(curr_node)).second) { - __TBB_ASSERT(!allow_multimapping, "Insertion should succeed for multicontainer"); - __TBB_ASSERT(source_prev->next() == next_node, - "Concurrent operations with the source container in merge are prohibited"); - - // Initialize the node with the old order key, because the order key - // can change during the insertion - curr->init(old_order_key); - __TBB_ASSERT(old_order_key >= source_prev->order_key() && - (next_node == nullptr || old_order_key <= next_node->order_key()), - "Wrong nodes order in the source container"); - // Merge is unsafe for source container, so the insertion back can be done without compare_exchange - curr->set_next(next_node); - source_prev->set_next(curr); - source_prev = curr; - node_handle_accessor::deactivate(curr_node); - } else { - source.my_size.fetch_sub(1, std::memory_order_relaxed); - } - } else { - source_prev = curr; - } - } else { - source_prev = source_prev->next(); - } - } - } - -private: - // Unsafe method, which unlinks the node between prev and next - void unlink_node( node_ptr prev_node, node_ptr node_to_unlink, node_ptr next_node ) { - __TBB_ASSERT(prev_node->next() == node_to_unlink && - node_to_unlink->next() == next_node, - "erasing and extracting nodes from the containers are unsafe in concurrent mode"); - prev_node->set_next(next_node); - node_to_unlink->set_next(nullptr); - } - - template - value_node_ptr internal_find( const K& key ) { - sokey_type hash_key = sokey_type(my_hash_compare(key)); - sokey_type order_key = split_order_key_regular(hash_key); - - node_ptr curr = prepare_bucket(hash_key); - - while (curr != nullptr) { - if (curr->order_key() > order_key) { - // If the order key is greater than the requested order key, - // the element is not in the hash table - return nullptr; - } else if (curr->order_key() == order_key && - my_hash_compare(traits_type::get_key(static_cast(curr)->value()), key)) { - // The fact that order keys match does not mean that the element is found. - // Key function comparison has to be performed to check whether this is the - // right element. If not, keep searching while order key is the same. - return static_cast(curr); - } - curr = curr->next(); - } - - return nullptr; - } - - template - std::pair internal_equal_range( const K& key ) { - sokey_type hash_key = sokey_type(my_hash_compare(key)); - sokey_type order_key = split_order_key_regular(hash_key); - - node_ptr curr = prepare_bucket(hash_key); - - while (curr != nullptr) { - if (curr->order_key() > order_key) { - // If the order key is greater than the requested order key, - // the element is not in the hash table - return std::make_pair(nullptr, nullptr); - } else if (curr->order_key() == order_key && - my_hash_compare(traits_type::get_key(static_cast(curr)->value()), key)) { - value_node_ptr first = static_cast(curr); - node_ptr last = first; - do { - last = last->next(); - } while (allow_multimapping && last != nullptr && !last->is_dummy() && - my_hash_compare(traits_type::get_key(static_cast(last)->value()), key)); - return std::make_pair(first, first_value_node(last)); - } - curr = curr->next(); - } - return {nullptr, nullptr}; - } - - template - size_type internal_count( const K& key ) const { - if (allow_multimapping) { - // TODO: consider reimplementing the internal_equal_range with elements counting to avoid std::distance - auto eq_range = equal_range(key); - return std::distance(eq_range.first, eq_range.second); - } else { - return contains(key) ? 1 : 0; - } - } - - void internal_copy( const concurrent_unordered_base& other ) { - node_ptr last_node = &my_head; - my_segments[0].store(&my_head, std::memory_order_relaxed); - - for (node_ptr node = other.my_head.next(); node != nullptr; node = node->next()) { - node_ptr new_node; - if (!node->is_dummy()) { - // The node in the right table contains a value - new_node = create_node(node->order_key(), static_cast(node)->value()); - } else { - // The node in the right table is a dummy node - new_node = create_dummy_node(node->order_key()); - my_segments[reverse_bits(node->order_key())].store(new_node, std::memory_order_relaxed); - } - - last_node->set_next(new_node); - last_node = new_node; - } - } - - void internal_move( concurrent_unordered_base&& other ) { - node_ptr last_node = &my_head; - my_segments[0].store(&my_head, std::memory_order_relaxed); - - for (node_ptr node = other.my_head.next(); node != nullptr; node = node->next()) { - node_ptr new_node; - if (!node->is_dummy()) { - // The node in the right table contains a value - new_node = create_node(node->order_key(), std::move(static_cast(node)->value())); - } else { - // TODO: do we need to destroy a dummy node in the right container? - // The node in the right table is a dummy_node - new_node = create_dummy_node(node->order_key()); - my_segments[reverse_bits(node->order_key())].store(new_node, std::memory_order_relaxed); - } - - last_node->set_next(new_node); - last_node = new_node; - } - } - - void move_content( concurrent_unordered_base&& other ) { - // NOTE: allocators should be equal - my_head.set_next(other.my_head.next()); - other.my_head.set_next(nullptr); - my_segments[0].store(&my_head, std::memory_order_relaxed); - - other.my_bucket_count.store(initial_bucket_count, std::memory_order_relaxed); - other.my_max_load_factor = initial_max_load_factor; - other.my_size.store(0, std::memory_order_relaxed); - } - - void internal_move_construct_with_allocator( concurrent_unordered_base&& other, const allocator_type&, - /*is_always_equal = */std::true_type ) { - // Allocators are always equal - no need to compare for equality - move_content(std::move(other)); - } - - void internal_move_construct_with_allocator( concurrent_unordered_base&& other, const allocator_type& alloc, - /*is_always_equal = */std::false_type ) { - // Allocators are not always equal - if (alloc == other.my_segments.get_allocator()) { - move_content(std::move(other)); - } else { - try_call( [&] { - internal_move(std::move(other)); - } ).on_exception( [&] { - clear(); - }); - } - } - - // Move assigns the hash table to other is any instances of allocator_type are always equal - // or propagate_on_container_move_assignment is true - void internal_move_assign( concurrent_unordered_base&& other, /*is_always_equal || POCMA = */std::true_type ) { - move_content(std::move(other)); - } - - // Move assigns the hash table to other is any instances of allocator_type are not always equal - // and propagate_on_container_move_assignment is false - void internal_move_assign( concurrent_unordered_base&& other, /*is_always_equal || POCMA = */std::false_type ) { - if (my_segments.get_allocator() == other.my_segments.get_allocator()) { - move_content(std::move(other)); - } else { - // TODO: guards for exceptions - internal_move(std::move(other)); - } - } - - void internal_swap( concurrent_unordered_base& other, /*is_always_equal || POCS = */std::true_type ) { - internal_swap_fields(other); - } - - void internal_swap( concurrent_unordered_base& other, /*is_always_equal || POCS = */std::false_type ) { - __TBB_ASSERT(my_segments.get_allocator() == other.my_segments.get_allocator(), - "Swapping with unequal allocators is not allowed"); - internal_swap_fields(other); - } - - void internal_swap_fields( concurrent_unordered_base& other ) { - node_ptr first_node = my_head.next(); - my_head.set_next(other.my_head.next()); - other.my_head.set_next(first_node); - - size_type current_size = my_size.load(std::memory_order_relaxed); - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.my_size.store(current_size, std::memory_order_relaxed); - - size_type bucket_count = my_bucket_count.load(std::memory_order_relaxed); - my_bucket_count.store(other.my_bucket_count.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.my_bucket_count.store(bucket_count, std::memory_order_relaxed); - - using std::swap; - swap(my_max_load_factor, other.my_max_load_factor); - swap(my_hash_compare, other.my_hash_compare); - my_segments.swap(other.my_segments); - - // swap() method from segment table swaps all of the segments including the first segment - // We should restore it to my_head. Without it the first segment of the container will point - // to other.my_head. - my_segments[0].store(&my_head, std::memory_order_relaxed); - other.my_segments[0].store(&other.my_head, std::memory_order_relaxed); - } - - // A regular order key has its original hash value reversed and the last bit set - static constexpr sokey_type split_order_key_regular( sokey_type hash ) { - return reverse_bits(hash) | 0x1; - } - - // A dummy order key has its original hash value reversed and the last bit unset - static constexpr sokey_type split_order_key_dummy( sokey_type hash ) { - return reverse_bits(hash) & ~sokey_type(0x1); - } - - size_type get_parent( size_type bucket ) const { - // Unset bucket's most significant turned-on bit - __TBB_ASSERT(bucket != 0, "Unable to get_parent of the bucket 0"); - size_type msb = tbb::detail::log2(bucket); - return bucket & ~(size_type(1) << msb); - } - - size_type get_next_bucket_index( size_type bucket ) const { - size_type bits = tbb::detail::log2(my_bucket_count.load(std::memory_order_relaxed)); - size_type reversed_next = reverse_n_bits(bucket, bits) + 1; - return reverse_n_bits(reversed_next, bits); - } - - std::atomic my_size; - std::atomic my_bucket_count; - float my_max_load_factor; - hash_compare_type my_hash_compare; - - list_node_type my_head; // Head node for split ordered list - unordered_segment_table my_segments; // Segment table of pointers to nodes - - template - friend class solist_iterator; - - template - friend class concurrent_unordered_base; -}; // class concurrent_unordered_base - -template -bool operator==( const concurrent_unordered_base& lhs, - const concurrent_unordered_base& rhs ) { - if (&lhs == &rhs) { return true; } - if (lhs.size() != rhs.size()) { return false; } - -#if _MSC_VER - // Passing "unchecked" iterators to std::permutation with 3 parameters - // causes compiler warnings. - // The workaround is to use overload with 4 parameters, which is - // available since C++14 - minimally supported version on MSVC - return std::is_permutation(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); -#else - return std::is_permutation(lhs.begin(), lhs.end(), rhs.begin()); -#endif -} - -#if !__TBB_CPP20_COMPARISONS_PRESENT -template -bool operator!=( const concurrent_unordered_base& lhs, - const concurrent_unordered_base& rhs ) { - return !(lhs == rhs); -} -#endif - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -#pragma warning(pop) // warning 4127 is back -#endif - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__concurrent_unordered_base_H diff --git a/include/oneapi/tbb/detail/_config.h b/include/oneapi/tbb/detail/_config.h deleted file mode 100644 index ad9f0f3..0000000 --- a/include/oneapi/tbb/detail/_config.h +++ /dev/null @@ -1,529 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__config_H -#define __TBB_detail__config_H - -/** This header is supposed to contain macro definitions only. - The macros defined here are intended to control such aspects of TBB build as - - presence of compiler features - - compilation modes - - feature sets - - known compiler/platform issues -**/ - -/* Check which standard library we use. */ -#include - -#ifdef __has_include -#if __has_include() -#include -#endif -#endif - -#include "_export.h" - -#if _MSC_VER - #define __TBB_EXPORTED_FUNC __cdecl - #define __TBB_EXPORTED_METHOD __thiscall -#else - #define __TBB_EXPORTED_FUNC - #define __TBB_EXPORTED_METHOD -#endif - -#if defined(_MSVC_LANG) - #define __TBB_LANG _MSVC_LANG -#else - #define __TBB_LANG __cplusplus -#endif // _MSVC_LANG - -#define __TBB_CPP14_PRESENT (__TBB_LANG >= 201402L) -#define __TBB_CPP17_PRESENT (__TBB_LANG >= 201703L) -#define __TBB_CPP20_PRESENT (__TBB_LANG >= 202002L) - -#if __INTEL_COMPILER || _MSC_VER - #define __TBB_NOINLINE(decl) __declspec(noinline) decl -#elif __GNUC__ - #define __TBB_NOINLINE(decl) decl __attribute__ ((noinline)) -#else - #define __TBB_NOINLINE(decl) decl -#endif - -#define __TBB_STRING_AUX(x) #x -#define __TBB_STRING(x) __TBB_STRING_AUX(x) - -// Note that when ICC or Clang is in use, __TBB_GCC_VERSION might not fully match -// the actual GCC version on the system. -#define __TBB_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) - -/* Check which standard library we use. */ - -// Prior to GCC 7, GNU libstdc++ did not have a convenient version macro. -// Therefore we use different ways to detect its version. -#ifdef TBB_USE_GLIBCXX_VERSION - // The version is explicitly specified in our public TBB_USE_GLIBCXX_VERSION macro. - // Its format should match the __TBB_GCC_VERSION above, e.g. 70301 for libstdc++ coming with GCC 7.3.1. - #define __TBB_GLIBCXX_VERSION TBB_USE_GLIBCXX_VERSION -#elif _GLIBCXX_RELEASE && _GLIBCXX_RELEASE != __GNUC__ - // Reported versions of GCC and libstdc++ do not match; trust the latter - #define __TBB_GLIBCXX_VERSION (_GLIBCXX_RELEASE*10000) -#elif __GLIBCPP__ || __GLIBCXX__ - // The version macro is not defined or matches the GCC version; use __TBB_GCC_VERSION - #define __TBB_GLIBCXX_VERSION __TBB_GCC_VERSION -#endif - -#if __clang__ - // according to clang documentation, version can be vendor specific - #define __TBB_CLANG_VERSION (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) -#endif - -/** Macro helpers **/ - -#define __TBB_CONCAT_AUX(A,B) A##B -// The additional level of indirection is needed to expand macros A and B (not to get the AB macro). -// See [cpp.subst] and [cpp.concat] for more details. -#define __TBB_CONCAT(A,B) __TBB_CONCAT_AUX(A,B) -// The IGNORED argument and comma are needed to always have 2 arguments (even when A is empty). -#define __TBB_IS_MACRO_EMPTY(A,IGNORED) __TBB_CONCAT_AUX(__TBB_MACRO_EMPTY,A) -#define __TBB_MACRO_EMPTY 1 - -#if _M_X64 || _M_ARM64 - #define __TBB_W(name) name##64 -#else - #define __TBB_W(name) name -#endif - -/** User controlled TBB features & modes **/ - -#ifndef TBB_USE_DEBUG - /* - There are four cases that are supported: - 1. "_DEBUG is undefined" means "no debug"; - 2. "_DEBUG defined to something that is evaluated to 0" (including "garbage", as per [cpp.cond]) means "no debug"; - 3. "_DEBUG defined to something that is evaluated to a non-zero value" means "debug"; - 4. "_DEBUG defined to nothing (empty)" means "debug". - */ - #ifdef _DEBUG - // Check if _DEBUG is empty. - #define __TBB_IS__DEBUG_EMPTY (__TBB_IS_MACRO_EMPTY(_DEBUG,IGNORED)==__TBB_MACRO_EMPTY) - #if __TBB_IS__DEBUG_EMPTY - #define TBB_USE_DEBUG 1 - #else - #define TBB_USE_DEBUG _DEBUG - #endif // __TBB_IS__DEBUG_EMPTY - #else - #define TBB_USE_DEBUG 0 - #endif // _DEBUG -#endif // TBB_USE_DEBUG - -#ifndef TBB_USE_ASSERT - #define TBB_USE_ASSERT TBB_USE_DEBUG -#endif // TBB_USE_ASSERT - -#ifndef TBB_USE_PROFILING_TOOLS -#if TBB_USE_DEBUG - #define TBB_USE_PROFILING_TOOLS 2 -#else // TBB_USE_DEBUG - #define TBB_USE_PROFILING_TOOLS 0 -#endif // TBB_USE_DEBUG -#endif // TBB_USE_PROFILING_TOOLS - -// Exceptions support cases -#if !(__EXCEPTIONS || defined(_CPPUNWIND) || __SUNPRO_CC) - #if TBB_USE_EXCEPTIONS - #error Compilation settings do not support exception handling. Please do not set TBB_USE_EXCEPTIONS macro or set it to 0. - #elif !defined(TBB_USE_EXCEPTIONS) - #define TBB_USE_EXCEPTIONS 0 - #endif -#elif !defined(TBB_USE_EXCEPTIONS) - #define TBB_USE_EXCEPTIONS 1 -#endif - -/** Preprocessor symbols to determine HW architecture **/ - -#if _WIN32 || _WIN64 - #if defined(_M_X64) || defined(__x86_64__) // the latter for MinGW support - #define __TBB_x86_64 1 - #elif defined(_M_IA64) - #define __TBB_ipf 1 - #elif defined(_M_IX86) || defined(__i386__) // the latter for MinGW support - #define __TBB_x86_32 1 - #else - #define __TBB_generic_arch 1 - #endif -#else /* Assume generic Unix */ - #if __x86_64__ - #define __TBB_x86_64 1 - #elif __ia64__ - #define __TBB_ipf 1 - #elif __i386__||__i386 // __i386 is for Sun OS - #define __TBB_x86_32 1 - #else - #define __TBB_generic_arch 1 - #endif -#endif - -/** Windows API or POSIX API **/ - -#if _WIN32 || _WIN64 - #define __TBB_USE_WINAPI 1 -#else - #define __TBB_USE_POSIX 1 -#endif - -/** Internal TBB features & modes **/ - -/** __TBB_DYNAMIC_LOAD_ENABLED describes the system possibility to load shared libraries at run time **/ -#ifndef __TBB_DYNAMIC_LOAD_ENABLED - #define __TBB_DYNAMIC_LOAD_ENABLED 1 -#endif - -/** __TBB_WIN8UI_SUPPORT enables support of Windows* Store Apps and limit a possibility to load - shared libraries at run time only from application container **/ -#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_APP - #define __TBB_WIN8UI_SUPPORT 1 -#else - #define __TBB_WIN8UI_SUPPORT 0 -#endif - -/** __TBB_WEAK_SYMBOLS_PRESENT denotes that the system supports the weak symbol mechanism **/ -#ifndef __TBB_WEAK_SYMBOLS_PRESENT - #define __TBB_WEAK_SYMBOLS_PRESENT ( !_WIN32 && !__APPLE__ && !__sun && (__TBB_GCC_VERSION >= 40000 || __INTEL_COMPILER ) ) -#endif - -/** Presence of compiler features **/ - -#if __clang__ && !__INTEL_COMPILER - #define __TBB_USE_OPTIONAL_RTTI __has_feature(cxx_rtti) -#elif defined(_CPPRTTI) - #define __TBB_USE_OPTIONAL_RTTI 1 -#else - #define __TBB_USE_OPTIONAL_RTTI (__GXX_RTTI || __RTTI || __INTEL_RTTI__) -#endif - -/** Address sanitizer detection **/ -#ifdef __SANITIZE_ADDRESS__ - #define __TBB_USE_ADDRESS_SANITIZER 1 -#elif defined(__has_feature) -#if __has_feature(address_sanitizer) - #define __TBB_USE_ADDRESS_SANITIZER 1 -#endif -#endif - -/** Library features presence macros **/ - -#define __TBB_CPP14_INTEGER_SEQUENCE_PRESENT (__TBB_LANG >= 201402L) -#define __TBB_CPP17_INVOKE_PRESENT (__TBB_LANG >= 201703L) - -// TODO: Remove the condition(__INTEL_COMPILER > 2021) from the __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -// macro when this feature start working correctly on this compiler. -#if __INTEL_COMPILER && (!_MSC_VER || __INTEL_CXX11_MOVE__) - #define __TBB_CPP14_VARIABLE_TEMPLATES_PRESENT (__TBB_LANG >= 201402L) - #define __TBB_CPP17_DEDUCTION_GUIDES_PRESENT (__INTEL_COMPILER > 2021 && __TBB_LANG >= 201703L) - #define __TBB_CPP20_CONCEPTS_PRESENT 0 // TODO: add a mechanism for future addition -#elif __clang__ - #define __TBB_CPP14_VARIABLE_TEMPLATES_PRESENT (__has_feature(cxx_variable_templates)) - #define __TBB_CPP20_CONCEPTS_PRESENT 0 // TODO: add a mechanism for future addition - #ifdef __cpp_deduction_guides - #define __TBB_CPP17_DEDUCTION_GUIDES_PRESENT (__cpp_deduction_guides >= 201611L) - #else - #define __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 0 - #endif -#elif __GNUC__ - #define __TBB_CPP14_VARIABLE_TEMPLATES_PRESENT (__TBB_LANG >= 201402L && __TBB_GCC_VERSION >= 50000) - #define __TBB_CPP17_DEDUCTION_GUIDES_PRESENT (__cpp_deduction_guides >= 201606L) - #define __TBB_CPP20_CONCEPTS_PRESENT (__TBB_LANG >= 201709L && __TBB_GCC_VERSION >= 100201) -#elif _MSC_VER - #define __TBB_CPP14_VARIABLE_TEMPLATES_PRESENT (_MSC_FULL_VER >= 190023918 && (!__INTEL_COMPILER || __INTEL_COMPILER >= 1700)) - #define __TBB_CPP17_DEDUCTION_GUIDES_PRESENT (_MSC_VER >= 1914 && __TBB_LANG >= 201703L && (!__INTEL_COMPILER || __INTEL_COMPILER > 2021)) - #define __TBB_CPP20_CONCEPTS_PRESENT (_MSC_VER >= 1923 && __TBB_LANG >= 202002L) // TODO: INTEL_COMPILER? -#else - #define __TBB_CPP14_VARIABLE_TEMPLATES_PRESENT (__TBB_LANG >= 201402L) - #define __TBB_CPP17_DEDUCTION_GUIDES_PRESENT (__TBB_LANG >= 201703L) - #define __TBB_CPP20_CONCEPTS_PRESENT (__TBB_LANG >= 202002L) -#endif - -// GCC4.8 on RHEL7 does not support std::get_new_handler -#define __TBB_CPP11_GET_NEW_HANDLER_PRESENT (_MSC_VER >= 1900 || __TBB_GLIBCXX_VERSION >= 40900 && __GXX_EXPERIMENTAL_CXX0X__ || _LIBCPP_VERSION) -// GCC4.8 on RHEL7 does not support std::is_trivially_copyable -#define __TBB_CPP11_TYPE_PROPERTIES_PRESENT (_LIBCPP_VERSION || _MSC_VER >= 1700 || (__TBB_GLIBCXX_VERSION >= 50000 && __GXX_EXPERIMENTAL_CXX0X__)) - -#define __TBB_CPP17_MEMORY_RESOURCE_PRESENT (_MSC_VER >= 1913 && (__TBB_LANG > 201402L) || \ - __TBB_GLIBCXX_VERSION >= 90000 && __TBB_LANG >= 201703L) -#define __TBB_CPP17_HW_INTERFERENCE_SIZE_PRESENT (_MSC_VER >= 1911) -#define __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT (__TBB_LANG >= 201703L) -#define __TBB_CPP17_ALLOCATOR_IS_ALWAYS_EQUAL_PRESENT (__TBB_LANG >= 201703L) -#define __TBB_CPP17_IS_SWAPPABLE_PRESENT (__TBB_LANG >= 201703L) - -#if defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_three_way_comparison) - #define __TBB_CPP20_COMPARISONS_PRESENT ((__cpp_impl_three_way_comparison >= 201907L) && (__cpp_lib_three_way_comparison >= 201907L)) -#else - #define __TBB_CPP20_COMPARISONS_PRESENT 0 -#endif - -#define __TBB_RESUMABLE_TASKS (!__TBB_WIN8UI_SUPPORT && !__ANDROID__ && !__QNXNTO__ && (!__linux__ || __GLIBC__)) - -/* This macro marks incomplete code or comments describing ideas which are considered for the future. - * See also for plain comment with TODO and FIXME marks for small improvement opportunities. - */ -#define __TBB_TODO 0 - -/* Check which standard library we use. */ -/* __TBB_SYMBOL is defined only while processing exported symbols list where C++ is not allowed. */ -#if !defined(__TBB_SYMBOL) && !__TBB_CONFIG_PREPROC_ONLY - #include -#endif - -/** Target OS is either iOS* or iOS* simulator **/ -#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ - #define __TBB_IOS 1 -#endif - -#if __APPLE__ - #if __INTEL_COMPILER && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1099 \ - && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101000 - // ICC does not correctly set the macro if -mmacosx-min-version is not specified - #define __TBB_MACOS_TARGET_VERSION (100000 + 10*(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - 1000)) - #else - #define __TBB_MACOS_TARGET_VERSION __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - #endif -#endif - -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) - #define __TBB_GCC_WARNING_IGNORED_ATTRIBUTES_PRESENT (__TBB_GCC_VERSION >= 60100) -#endif - -#if __GNUC__ && !__INTEL_COMPILER && !__clang__ - #define __TBB_GCC_PARAMETER_PACK_IN_LAMBDAS_BROKEN (__TBB_GCC_VERSION <= 40805) -#endif - -#define __TBB_CPP17_FALLTHROUGH_PRESENT (__TBB_LANG >= 201703L) -#define __TBB_CPP17_NODISCARD_PRESENT (__TBB_LANG >= 201703L) -#define __TBB_FALLTHROUGH_PRESENT (__TBB_GCC_VERSION >= 70000 && !__INTEL_COMPILER) - -#if __TBB_CPP17_FALLTHROUGH_PRESENT - #define __TBB_fallthrough [[fallthrough]] -#elif __TBB_FALLTHROUGH_PRESENT - #define __TBB_fallthrough __attribute__ ((fallthrough)) -#else - #define __TBB_fallthrough -#endif - -#if __TBB_CPP17_NODISCARD_PRESENT - #define __TBB_nodiscard [[nodiscard]] -#elif __clang__ || __GNUC__ - #define __TBB_nodiscard __attribute__((warn_unused_result)) -#else - #define __TBB_nodiscard -#endif - -#define __TBB_CPP17_UNCAUGHT_EXCEPTIONS_PRESENT (_MSC_VER >= 1900 || __GLIBCXX__ && __cpp_lib_uncaught_exceptions \ - || _LIBCPP_VERSION >= 3700 && (!__TBB_MACOS_TARGET_VERSION || __TBB_MACOS_TARGET_VERSION >= 101200)) - -#define __TBB_TSX_INTRINSICS_PRESENT (__RTM__ || __INTEL_COMPILER || (_MSC_VER>=1700 && (__TBB_x86_64 || __TBB_x86_32))) - -#define __TBB_WAITPKG_INTRINSICS_PRESENT ((__INTEL_COMPILER >= 1900 || __TBB_GCC_VERSION >= 110000 || __TBB_CLANG_VERSION >= 120000) \ - && (_WIN32 || _WIN64 || __unix__ || __APPLE__) && (__TBB_x86_32 || __TBB_x86_64) && !__ANDROID__) - -/** Internal TBB features & modes **/ - -/** __TBB_SOURCE_DIRECTLY_INCLUDED is a mode used in whitebox testing when - it's necessary to test internal functions not exported from TBB DLLs -**/ -#if (_WIN32||_WIN64) && (__TBB_SOURCE_DIRECTLY_INCLUDED || TBB_USE_PREVIEW_BINARY) - #define __TBB_NO_IMPLICIT_LINKAGE 1 - #define __TBBMALLOC_NO_IMPLICIT_LINKAGE 1 -#endif - -#if (__TBB_BUILD || __TBBMALLOC_BUILD || __TBBMALLOCPROXY_BUILD || __TBBBIND_BUILD) && !defined(__TBB_NO_IMPLICIT_LINKAGE) - #define __TBB_NO_IMPLICIT_LINKAGE 1 -#endif - -#if _MSC_VER - #if !__TBB_NO_IMPLICIT_LINKAGE - #ifdef _DEBUG - #pragma comment(lib, "tbb12_debug.lib") - #else - #pragma comment(lib, "tbb12.lib") - #endif - #endif -#endif - -#ifndef __TBB_SCHEDULER_OBSERVER - #define __TBB_SCHEDULER_OBSERVER 1 -#endif /* __TBB_SCHEDULER_OBSERVER */ - -#ifndef __TBB_FP_CONTEXT - #define __TBB_FP_CONTEXT 1 -#endif /* __TBB_FP_CONTEXT */ - -#define __TBB_RECYCLE_TO_ENQUEUE __TBB_BUILD // keep non-official - -#ifndef __TBB_ARENA_OBSERVER - #define __TBB_ARENA_OBSERVER __TBB_SCHEDULER_OBSERVER -#endif /* __TBB_ARENA_OBSERVER */ - -#ifndef __TBB_ARENA_BINDING - #define __TBB_ARENA_BINDING 1 -#endif - -#ifndef __TBB_ENQUEUE_ENFORCED_CONCURRENCY - #define __TBB_ENQUEUE_ENFORCED_CONCURRENCY 1 -#endif - -#if !defined(__TBB_SURVIVE_THREAD_SWITCH) && \ - (_WIN32 || _WIN64 || __APPLE__ || (defined(__unix__) && !__ANDROID__)) - #define __TBB_SURVIVE_THREAD_SWITCH 1 -#endif /* __TBB_SURVIVE_THREAD_SWITCH */ - -#ifndef TBB_PREVIEW_FLOW_GRAPH_FEATURES - #define TBB_PREVIEW_FLOW_GRAPH_FEATURES __TBB_CPF_BUILD -#endif - -#ifndef __TBB_DEFAULT_PARTITIONER - #define __TBB_DEFAULT_PARTITIONER tbb::auto_partitioner -#endif - -#ifndef __TBB_FLOW_TRACE_CODEPTR - #define __TBB_FLOW_TRACE_CODEPTR __TBB_CPF_BUILD -#endif - -// Intel(R) C++ Compiler starts analyzing usages of the deprecated content at the template -// instantiation site, which is too late for suppression of the corresponding messages for internal -// stuff. -#if !defined(__INTEL_COMPILER) && (!defined(TBB_SUPPRESS_DEPRECATED_MESSAGES) || (TBB_SUPPRESS_DEPRECATED_MESSAGES == 0)) - #if (__TBB_LANG >= 201402L && (!defined(_MSC_VER) || _MSC_VER >= 1920)) - #define __TBB_DEPRECATED [[deprecated]] - #define __TBB_DEPRECATED_MSG(msg) [[deprecated(msg)]] - #elif _MSC_VER - #define __TBB_DEPRECATED __declspec(deprecated) - #define __TBB_DEPRECATED_MSG(msg) __declspec(deprecated(msg)) - #elif (__GNUC__ && __TBB_GCC_VERSION >= 40805) || __clang__ - #define __TBB_DEPRECATED __attribute__((deprecated)) - #define __TBB_DEPRECATED_MSG(msg) __attribute__((deprecated(msg))) - #endif -#endif // !defined(TBB_SUPPRESS_DEPRECATED_MESSAGES) || (TBB_SUPPRESS_DEPRECATED_MESSAGES == 0) - -#if !defined(__TBB_DEPRECATED) - #define __TBB_DEPRECATED - #define __TBB_DEPRECATED_MSG(msg) -#elif !defined(__TBB_SUPPRESS_INTERNAL_DEPRECATED_MESSAGES) - // Suppress deprecated messages from self - #define __TBB_SUPPRESS_INTERNAL_DEPRECATED_MESSAGES 1 -#endif - -#if defined(TBB_SUPPRESS_DEPRECATED_MESSAGES) && (TBB_SUPPRESS_DEPRECATED_MESSAGES == 0) - #define __TBB_DEPRECATED_VERBOSE __TBB_DEPRECATED - #define __TBB_DEPRECATED_VERBOSE_MSG(msg) __TBB_DEPRECATED_MSG(msg) -#else - #define __TBB_DEPRECATED_VERBOSE - #define __TBB_DEPRECATED_VERBOSE_MSG(msg) -#endif // (TBB_SUPPRESS_DEPRECATED_MESSAGES == 0) - -#if (!defined(TBB_SUPPRESS_DEPRECATED_MESSAGES) || (TBB_SUPPRESS_DEPRECATED_MESSAGES == 0)) && !(__TBB_LANG >= 201103L || _MSC_VER >= 1900) - #pragma message("TBB Warning: Support for C++98/03 is deprecated. Please use the compiler that supports C++11 features at least.") -#endif - -#ifdef _VARIADIC_MAX - #define __TBB_VARIADIC_MAX _VARIADIC_MAX -#else - #if _MSC_VER == 1700 - #define __TBB_VARIADIC_MAX 5 // VS11 setting, issue resolved in VS12 - #elif _MSC_VER == 1600 - #define __TBB_VARIADIC_MAX 10 // VS10 setting - #else - #define __TBB_VARIADIC_MAX 15 - #endif -#endif - -#if __SANITIZE_THREAD__ - #define __TBB_USE_THREAD_SANITIZER 1 -#elif defined(__has_feature) -#if __has_feature(thread_sanitizer) - #define __TBB_USE_THREAD_SANITIZER 1 -#endif -#endif - -#ifndef __TBB_USE_SANITIZERS -#define __TBB_USE_SANITIZERS (__TBB_USE_THREAD_SANITIZER || __TBB_USE_ADDRESS_SANITIZER) -#endif - -#ifndef __TBB_RESUMABLE_TASKS_USE_THREADS -#define __TBB_RESUMABLE_TASKS_USE_THREADS __TBB_USE_SANITIZERS -#endif - -#ifndef __TBB_USE_CONSTRAINTS -#define __TBB_USE_CONSTRAINTS 1 -#endif - -#ifndef __TBB_STRICT_CONSTRAINTS -#define __TBB_STRICT_CONSTRAINTS 1 -#endif - -#if __TBB_CPP20_CONCEPTS_PRESENT && __TBB_USE_CONSTRAINTS - #define __TBB_requires(...) requires __VA_ARGS__ -#else // __TBB_CPP20_CONCEPTS_PRESENT - #define __TBB_requires(...) -#endif // __TBB_CPP20_CONCEPTS_PRESENT - -/** Macros of the form __TBB_XXX_BROKEN denote known issues that are caused by - the bugs in compilers, standard or OS specific libraries. They should be - removed as soon as the corresponding bugs are fixed or the buggy OS/compiler - versions go out of the support list. -**/ - -// Some STL containers not support allocator traits in old GCC versions -#if __GXX_EXPERIMENTAL_CXX0X__ && __TBB_GLIBCXX_VERSION <= 50301 - #define TBB_ALLOCATOR_TRAITS_BROKEN 1 -#endif - -// GCC 4.8 C++ standard library implements std::this_thread::yield as no-op. -#if __TBB_GLIBCXX_VERSION >= 40800 && __TBB_GLIBCXX_VERSION < 40900 - #define __TBB_GLIBCXX_THIS_THREAD_YIELD_BROKEN 1 -#endif - -/** End of __TBB_XXX_BROKEN macro section **/ - -#if defined(_MSC_VER) && _MSC_VER>=1500 && !defined(__INTEL_COMPILER) - // A macro to suppress erroneous or benign "unreachable code" MSVC warning (4702) - #define __TBB_MSVC_UNREACHABLE_CODE_IGNORED 1 -#endif - -// Many OS versions (Android 4.0.[0-3] for example) need workaround for dlopen to avoid non-recursive loader lock hang -// Setting the workaround for all compile targets ($APP_PLATFORM) below Android 4.4 (android-19) -#if __ANDROID__ - #include -#endif - -#define __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING (TBB_PREVIEW_FLOW_GRAPH_FEATURES) - -#ifndef __TBB_PREVIEW_CRITICAL_TASKS -#define __TBB_PREVIEW_CRITICAL_TASKS 1 -#endif - -#ifndef __TBB_PREVIEW_FLOW_GRAPH_NODE_SET -#define __TBB_PREVIEW_FLOW_GRAPH_NODE_SET (TBB_PREVIEW_FLOW_GRAPH_FEATURES) -#endif - -#if TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS -#define __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS 1 -#endif - -#if TBB_PREVIEW_TASK_GROUP_EXTENSIONS || __TBB_BUILD -#define __TBB_PREVIEW_TASK_GROUP_EXTENSIONS 1 -#endif - -#endif // __TBB_detail__config_H diff --git a/include/oneapi/tbb/detail/_containers_helpers.h b/include/oneapi/tbb/detail/_containers_helpers.h deleted file mode 100644 index 4dca07f..0000000 --- a/include/oneapi/tbb/detail/_containers_helpers.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__containers_helpers_H -#define __TBB_detail__containers_helpers_H - -#include "_template_helpers.h" -#include "_allocator_traits.h" -#include -#include -#include - -namespace tbb { -namespace detail { -inline namespace d0 { - -template -struct comp_is_transparent : std::false_type {}; - -template -struct comp_is_transparent> : std::true_type {}; - -template -struct has_transparent_key_equal : std::false_type { using type = KeyEqual; }; - -template -struct has_transparent_key_equal> : std::true_type { - using type = typename Hasher::transparent_key_equal; - static_assert(comp_is_transparent::value, "Hash::transparent_key_equal::is_transparent is not valid or does not denote a type."); - static_assert((std::is_same>::value || - std::is_same::value), "KeyEqual is a different type than equal_to or Hash::transparent_key_equal."); - }; - -struct is_iterator_impl { -template -using iter_traits_category = typename std::iterator_traits::iterator_category; - -template -using input_iter_category = typename std::enable_if>::value>::type; -}; // struct is_iterator_impl - -template -using is_input_iterator = supports; - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -template -inline constexpr bool is_input_iterator_v = is_input_iterator::value; -#endif - -} // inline namespace d0 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__containers_helpers_H diff --git a/include/oneapi/tbb/detail/_exception.h b/include/oneapi/tbb/detail/_exception.h deleted file mode 100644 index 21c6118..0000000 --- a/include/oneapi/tbb/detail/_exception.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__exception_H -#define __TBB__exception_H - -#include "_config.h" - -#include // std::bad_alloc -#include // std::exception -#include // std::runtime_error - -namespace tbb { -namespace detail { -inline namespace d0 { -enum class exception_id { - bad_alloc = 1, - bad_last_alloc, - user_abort, - nonpositive_step, - out_of_range, - reservation_length_error, - missing_wait, - invalid_load_factor, - invalid_key, - bad_tagged_msg_cast, - unsafe_wait, - last_entry -}; -} // namespace d0 - -#if _MSC_VER - #pragma warning(disable: 4275) -#endif - -namespace r1 { -//! Exception for concurrent containers -class TBB_EXPORT bad_last_alloc : public std::bad_alloc { -public: - const char* __TBB_EXPORTED_METHOD what() const noexcept(true) override; -}; - -//! Exception for user-initiated abort -class TBB_EXPORT user_abort : public std::exception { -public: - const char* __TBB_EXPORTED_METHOD what() const noexcept(true) override; -}; - -//! Exception for missing wait on structured_task_group -class TBB_EXPORT missing_wait : public std::exception { -public: - const char* __TBB_EXPORTED_METHOD what() const noexcept(true) override; -}; - -//! Exception for impossible finalization of task_sheduler_handle -class TBB_EXPORT unsafe_wait : public std::runtime_error { -public: - unsafe_wait(const char* msg) : std::runtime_error(msg) {} -}; - -//! Gathers all throw operators in one place. -/** Its purpose is to minimize code bloat that can be caused by throw operators - scattered in multiple places, especially in templates. **/ -TBB_EXPORT void __TBB_EXPORTED_FUNC throw_exception ( exception_id ); -} // namespace r1 - -inline namespace d0 { -using r1::throw_exception; -} // namespace d0 - -} // namespace detail -} // namespace tbb - -#endif // __TBB__exception_H - diff --git a/include/oneapi/tbb/detail/_export.h b/include/oneapi/tbb/detail/_export.h deleted file mode 100644 index 4c01522..0000000 --- a/include/oneapi/tbb/detail/_export.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__export_H -#define __TBB_detail__export_H - -#if defined(__MINGW32__) - #define _EXPORT __declspec(dllexport) -#elif defined(_WIN32) || defined(__unix__) || defined(__APPLE__) // Use .def files for these - #define _EXPORT -#else - #error "Unknown platform/compiler" -#endif - -#if __TBB_BUILD - #define TBB_EXPORT _EXPORT -#else - #define TBB_EXPORT -#endif - -#if __TBBMALLOC_BUILD - #define TBBMALLOC_EXPORT _EXPORT -#else - #define TBBMALLOC_EXPORT -#endif - -#if __TBBBIND_BUILD - #define TBBBIND_EXPORT _EXPORT -#else - #define TBBBIND_EXPORT -#endif - -#endif diff --git a/include/oneapi/tbb/detail/_flow_graph_body_impl.h b/include/oneapi/tbb/detail/_flow_graph_body_impl.h deleted file mode 100644 index 8ac1121..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_body_impl.h +++ /dev/null @@ -1,385 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__flow_graph_body_impl_H -#define __TBB__flow_graph_body_impl_H - -#ifndef __TBB_flow_graph_H -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -// included in namespace tbb::detail::d1 (in flow_graph.h) - -typedef std::uint64_t tag_value; - - -// TODO revamp: find out if there is already helper for has_policy. -template struct Policy {}; - -template struct has_policy; - -template -struct has_policy : - std::integral_constant::value || - has_policy::value> {}; - -template -struct has_policy : - std::integral_constant::value> {}; - -template -struct has_policy > : has_policy {}; - -namespace graph_policy_namespace { - - struct rejecting { }; - struct reserving { }; - struct queueing { }; - struct lightweight { }; - - // K == type of field used for key-matching. Each tag-matching port will be provided - // functor that, given an object accepted by the port, will return the - /// field of type K being used for matching. - template::type > > - __TBB_requires(tbb::detail::hash_compare) - struct key_matching { - typedef K key_type; - typedef typename std::decay::type base_key_type; - typedef KHash hash_compare_type; - }; - - // old tag_matching join's new specifier - typedef key_matching tag_matching; - - // Aliases for Policy combinations - typedef Policy queueing_lightweight; - typedef Policy rejecting_lightweight; - -} // namespace graph_policy_namespace - -// -------------- function_body containers ---------------------- - -//! A functor that takes no input and generates a value of type Output -template< typename Output > -class input_body : no_assign { -public: - virtual ~input_body() {} - virtual Output operator()(flow_control& fc) = 0; - virtual input_body* clone() = 0; -}; - -//! The leaf for input_body -template< typename Output, typename Body> -class input_body_leaf : public input_body { -public: - input_body_leaf( const Body &_body ) : body(_body) { } - Output operator()(flow_control& fc) override { return body(fc); } - input_body_leaf* clone() override { - return new input_body_leaf< Output, Body >(body); - } - Body get_body() { return body; } -private: - Body body; -}; - -//! A functor that takes an Input and generates an Output -template< typename Input, typename Output > -class function_body : no_assign { -public: - virtual ~function_body() {} - virtual Output operator()(const Input &input) = 0; - virtual function_body* clone() = 0; -}; - -//! the leaf for function_body -template -class function_body_leaf : public function_body< Input, Output > { -public: - function_body_leaf( const B &_body ) : body(_body) { } - Output operator()(const Input &i) override { return tbb::detail::invoke(body,i); } - B get_body() { return body; } - function_body_leaf* clone() override { - return new function_body_leaf< Input, Output, B >(body); - } -private: - B body; -}; - -//! the leaf for function_body specialized for Input and output of continue_msg -template -class function_body_leaf< continue_msg, continue_msg, B> : public function_body< continue_msg, continue_msg > { -public: - function_body_leaf( const B &_body ) : body(_body) { } - continue_msg operator()( const continue_msg &i ) override { - body(i); - return i; - } - B get_body() { return body; } - function_body_leaf* clone() override { - return new function_body_leaf< continue_msg, continue_msg, B >(body); - } -private: - B body; -}; - -//! the leaf for function_body specialized for Output of continue_msg -template -class function_body_leaf< Input, continue_msg, B> : public function_body< Input, continue_msg > { -public: - function_body_leaf( const B &_body ) : body(_body) { } - continue_msg operator()(const Input &i) override { - body(i); - return continue_msg(); - } - B get_body() { return body; } - function_body_leaf* clone() override { - return new function_body_leaf< Input, continue_msg, B >(body); - } -private: - B body; -}; - -//! the leaf for function_body specialized for Input of continue_msg -template -class function_body_leaf< continue_msg, Output, B > : public function_body< continue_msg, Output > { -public: - function_body_leaf( const B &_body ) : body(_body) { } - Output operator()(const continue_msg &i) override { - return body(i); - } - B get_body() { return body; } - function_body_leaf* clone() override { - return new function_body_leaf< continue_msg, Output, B >(body); - } -private: - B body; -}; - -//! function_body that takes an Input and a set of output ports -template -class multifunction_body : no_assign { -public: - virtual ~multifunction_body () {} - virtual void operator()(const Input &/* input*/, OutputSet &/*oset*/) = 0; - virtual multifunction_body* clone() = 0; - virtual void* get_body_ptr() = 0; -}; - -//! leaf for multifunction. OutputSet can be a std::tuple or a vector. -template -class multifunction_body_leaf : public multifunction_body { -public: - multifunction_body_leaf(const B &_body) : body(_body) { } - void operator()(const Input &input, OutputSet &oset) override { - tbb::detail::invoke(body, input, oset); // body may explicitly put() to one or more of oset. - } - void* get_body_ptr() override { return &body; } - multifunction_body_leaf* clone() override { - return new multifunction_body_leaf(body); - } - -private: - B body; -}; - -// ------ function bodies for hash_buffers and key-matching joins. - -template -class type_to_key_function_body : no_assign { - public: - virtual ~type_to_key_function_body() {} - virtual Output operator()(const Input &input) = 0; // returns an Output - virtual type_to_key_function_body* clone() = 0; -}; - -// specialization for ref output -template -class type_to_key_function_body : no_assign { - public: - virtual ~type_to_key_function_body() {} - virtual const Output & operator()(const Input &input) = 0; // returns a const Output& - virtual type_to_key_function_body* clone() = 0; -}; - -template -class type_to_key_function_body_leaf : public type_to_key_function_body { -public: - type_to_key_function_body_leaf( const B &_body ) : body(_body) { } - Output operator()(const Input &i) override { return tbb::detail::invoke(body, i); } - type_to_key_function_body_leaf* clone() override { - return new type_to_key_function_body_leaf< Input, Output, B>(body); - } -private: - B body; -}; - -template -class type_to_key_function_body_leaf : public type_to_key_function_body< Input, Output&> { -public: - type_to_key_function_body_leaf( const B &_body ) : body(_body) { } - const Output& operator()(const Input &i) override { - return tbb::detail::invoke(body, i); - } - type_to_key_function_body_leaf* clone() override { - return new type_to_key_function_body_leaf< Input, Output&, B>(body); - } -private: - B body; -}; - -// --------------------------- end of function_body containers ------------------------ - -// --------------------------- node task bodies --------------------------------------- - -//! A task that calls a node's forward_task function -template< typename NodeType > -class forward_task_bypass : public graph_task { - NodeType &my_node; -public: - forward_task_bypass( graph& g, small_object_allocator& allocator, NodeType &n - , node_priority_t node_priority = no_priority - ) : graph_task(g, allocator, node_priority), - my_node(n) {} - - task* execute(execution_data& ed) override { - graph_task* next_task = my_node.forward_task(); - if (SUCCESSFULLY_ENQUEUED == next_task) - next_task = nullptr; - else if (next_task) - next_task = prioritize_task(my_node.graph_reference(), *next_task); - finalize(ed); - return next_task; - } - - task* cancel(execution_data& ed) override { - finalize(ed); - return nullptr; - } -}; - -//! A task that calls a node's apply_body_bypass function, passing in an input of type Input -// return the task* unless it is SUCCESSFULLY_ENQUEUED, in which case return nullptr -template< typename NodeType, typename Input > -class apply_body_task_bypass : public graph_task { - NodeType &my_node; - Input my_input; -public: - - apply_body_task_bypass( graph& g, small_object_allocator& allocator, NodeType &n, const Input &i - , node_priority_t node_priority = no_priority - ) : graph_task(g, allocator, node_priority), - my_node(n), my_input(i) {} - - task* execute(execution_data& ed) override { - graph_task* next_task = my_node.apply_body_bypass( my_input ); - if (SUCCESSFULLY_ENQUEUED == next_task) - next_task = nullptr; - else if (next_task) - next_task = prioritize_task(my_node.graph_reference(), *next_task); - finalize(ed); - return next_task; - } - - task* cancel(execution_data& ed) override { - finalize(ed); - return nullptr; - } -}; - -//! A task that calls a node's apply_body_bypass function with no input -template< typename NodeType > -class input_node_task_bypass : public graph_task { - NodeType &my_node; -public: - input_node_task_bypass( graph& g, small_object_allocator& allocator, NodeType &n ) - : graph_task(g, allocator), my_node(n) {} - - task* execute(execution_data& ed) override { - graph_task* next_task = my_node.apply_body_bypass( ); - if (SUCCESSFULLY_ENQUEUED == next_task) - next_task = nullptr; - else if (next_task) - next_task = prioritize_task(my_node.graph_reference(), *next_task); - finalize(ed); - return next_task; - } - - task* cancel(execution_data& ed) override { - finalize(ed); - return nullptr; - } -}; - -// ------------------------ end of node task bodies ----------------------------------- - -template -class threshold_regulator; - -template -class threshold_regulator::value>::type> - : public receiver, no_copy -{ - T* my_node; -protected: - - graph_task* try_put_task( const DecrementType& value ) override { - graph_task* result = my_node->decrement_counter( value ); - if( !result ) - result = SUCCESSFULLY_ENQUEUED; - return result; - } - - graph& graph_reference() const override { - return my_node->my_graph; - } - - template friend class limiter_node; - void reset_receiver( reset_flags ) {} - -public: - threshold_regulator(T* owner) : my_node(owner) { - // Do not work with the passed pointer here as it may not be fully initialized yet - } -}; - -template -class threshold_regulator : public continue_receiver, no_copy { - - T *my_node; - - graph_task* execute() override { - return my_node->decrement_counter( 1 ); - } - -protected: - - graph& graph_reference() const override { - return my_node->my_graph; - } - -public: - - typedef continue_msg input_type; - typedef continue_msg output_type; - threshold_regulator(T* owner) - : continue_receiver( /*number_of_predecessors=*/0, no_priority ), my_node(owner) - { - // Do not work with the passed pointer here as it may not be fully initialized yet - } -}; - -#endif // __TBB__flow_graph_body_impl_H diff --git a/include/oneapi/tbb/detail/_flow_graph_cache_impl.h b/include/oneapi/tbb/detail/_flow_graph_cache_impl.h deleted file mode 100644 index 059f198..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_cache_impl.h +++ /dev/null @@ -1,434 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__flow_graph_cache_impl_H -#define __TBB__flow_graph_cache_impl_H - -#ifndef __TBB_flow_graph_H -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -// included in namespace tbb::detail::d1 (in flow_graph.h) - -//! A node_cache maintains a std::queue of elements of type T. Each operation is protected by a lock. -template< typename T, typename M=spin_mutex > -class node_cache { - public: - - typedef size_t size_type; - - bool empty() { - typename mutex_type::scoped_lock lock( my_mutex ); - return internal_empty(); - } - - void add( T &n ) { - typename mutex_type::scoped_lock lock( my_mutex ); - internal_push(n); - } - - void remove( T &n ) { - typename mutex_type::scoped_lock lock( my_mutex ); - for ( size_t i = internal_size(); i != 0; --i ) { - T &s = internal_pop(); - if ( &s == &n ) - break; // only remove one predecessor per request - internal_push(s); - } - } - - void clear() { - while( !my_q.empty()) (void)my_q.pop(); - } - -protected: - - typedef M mutex_type; - mutex_type my_mutex; - std::queue< T * > my_q; - - // Assumes lock is held - inline bool internal_empty( ) { - return my_q.empty(); - } - - // Assumes lock is held - inline size_type internal_size( ) { - return my_q.size(); - } - - // Assumes lock is held - inline void internal_push( T &n ) { - my_q.push(&n); - } - - // Assumes lock is held - inline T &internal_pop() { - T *v = my_q.front(); - my_q.pop(); - return *v; - } - -}; - -//! A cache of predecessors that only supports try_get -template< typename T, typename M=spin_mutex > -class predecessor_cache : public node_cache< sender, M > { -public: - typedef M mutex_type; - typedef T output_type; - typedef sender predecessor_type; - typedef receiver successor_type; - - predecessor_cache( successor_type* owner ) : my_owner( owner ) { - __TBB_ASSERT( my_owner, "predecessor_cache should have an owner." ); - // Do not work with the passed pointer here as it may not be fully initialized yet - } - - bool get_item( output_type& v ) { - - bool msg = false; - - do { - predecessor_type *src; - { - typename mutex_type::scoped_lock lock(this->my_mutex); - if ( this->internal_empty() ) { - break; - } - src = &this->internal_pop(); - } - - // Try to get from this sender - msg = src->try_get( v ); - - if (msg == false) { - // Relinquish ownership of the edge - register_successor(*src, *my_owner); - } else { - // Retain ownership of the edge - this->add(*src); - } - } while ( msg == false ); - return msg; - } - - // If we are removing arcs (rf_clear_edges), call clear() rather than reset(). - void reset() { - for(;;) { - predecessor_type *src; - { - if (this->internal_empty()) break; - src = &this->internal_pop(); - } - register_successor(*src, *my_owner); - } - } - -protected: - successor_type* my_owner; -}; - -//! An cache of predecessors that supports requests and reservations -template< typename T, typename M=spin_mutex > -class reservable_predecessor_cache : public predecessor_cache< T, M > { -public: - typedef M mutex_type; - typedef T output_type; - typedef sender predecessor_type; - typedef receiver successor_type; - - reservable_predecessor_cache( successor_type* owner ) - : predecessor_cache(owner), reserved_src(nullptr) - { - // Do not work with the passed pointer here as it may not be fully initialized yet - } - - bool try_reserve( output_type &v ) { - bool msg = false; - - do { - predecessor_type* pred = nullptr; - { - typename mutex_type::scoped_lock lock(this->my_mutex); - if ( reserved_src.load(std::memory_order_relaxed) || this->internal_empty() ) - return false; - - pred = &this->internal_pop(); - reserved_src.store(pred, std::memory_order_relaxed); - } - - // Try to get from this sender - msg = pred->try_reserve( v ); - - if (msg == false) { - typename mutex_type::scoped_lock lock(this->my_mutex); - // Relinquish ownership of the edge - register_successor( *pred, *this->my_owner ); - reserved_src.store(nullptr, std::memory_order_relaxed); - } else { - // Retain ownership of the edge - this->add( *pred); - } - } while ( msg == false ); - - return msg; - } - - bool try_release() { - reserved_src.load(std::memory_order_relaxed)->try_release(); - reserved_src.store(nullptr, std::memory_order_relaxed); - return true; - } - - bool try_consume() { - reserved_src.load(std::memory_order_relaxed)->try_consume(); - reserved_src.store(nullptr, std::memory_order_relaxed); - return true; - } - - void reset() { - reserved_src.store(nullptr, std::memory_order_relaxed); - predecessor_cache::reset(); - } - - void clear() { - reserved_src.store(nullptr, std::memory_order_relaxed); - predecessor_cache::clear(); - } - -private: - std::atomic reserved_src; -}; - - -//! An abstract cache of successors -template -class successor_cache : no_copy { -protected: - - typedef M mutex_type; - mutex_type my_mutex; - - typedef receiver successor_type; - typedef receiver* pointer_type; - typedef sender owner_type; - // TODO revamp: introduce heapified collection of successors for strict priorities - typedef std::list< pointer_type > successors_type; - successors_type my_successors; - - owner_type* my_owner; - -public: - successor_cache( owner_type* owner ) : my_owner(owner) { - // Do not work with the passed pointer here as it may not be fully initialized yet - } - - virtual ~successor_cache() {} - - void register_successor( successor_type& r ) { - typename mutex_type::scoped_lock l(my_mutex, true); - if( r.priority() != no_priority ) - my_successors.push_front( &r ); - else - my_successors.push_back( &r ); - } - - void remove_successor( successor_type& r ) { - typename mutex_type::scoped_lock l(my_mutex, true); - for ( typename successors_type::iterator i = my_successors.begin(); - i != my_successors.end(); ++i ) { - if ( *i == & r ) { - my_successors.erase(i); - break; - } - } - } - - bool empty() { - typename mutex_type::scoped_lock l(my_mutex, false); - return my_successors.empty(); - } - - void clear() { - my_successors.clear(); - } - - virtual graph_task* try_put_task( const T& t ) = 0; -}; // successor_cache - -//! An abstract cache of successors, specialized to continue_msg -template -class successor_cache< continue_msg, M > : no_copy { -protected: - - typedef M mutex_type; - mutex_type my_mutex; - - typedef receiver successor_type; - typedef receiver* pointer_type; - typedef sender owner_type; - typedef std::list< pointer_type > successors_type; - successors_type my_successors; - owner_type* my_owner; - -public: - successor_cache( sender* owner ) : my_owner(owner) { - // Do not work with the passed pointer here as it may not be fully initialized yet - } - - virtual ~successor_cache() {} - - void register_successor( successor_type& r ) { - typename mutex_type::scoped_lock l(my_mutex, true); - if( r.priority() != no_priority ) - my_successors.push_front( &r ); - else - my_successors.push_back( &r ); - __TBB_ASSERT( my_owner, "Cache of successors must have an owner." ); - if ( r.is_continue_receiver() ) { - r.register_predecessor( *my_owner ); - } - } - - void remove_successor( successor_type& r ) { - typename mutex_type::scoped_lock l(my_mutex, true); - for ( successors_type::iterator i = my_successors.begin(); i != my_successors.end(); ++i ) { - if ( *i == &r ) { - __TBB_ASSERT(my_owner, "Cache of successors must have an owner."); - // TODO: check if we need to test for continue_receiver before removing from r. - r.remove_predecessor( *my_owner ); - my_successors.erase(i); - break; - } - } - } - - bool empty() { - typename mutex_type::scoped_lock l(my_mutex, false); - return my_successors.empty(); - } - - void clear() { - my_successors.clear(); - } - - virtual graph_task* try_put_task( const continue_msg& t ) = 0; -}; // successor_cache< continue_msg > - -//! A cache of successors that are broadcast to -template -class broadcast_cache : public successor_cache { - typedef successor_cache base_type; - typedef M mutex_type; - typedef typename successor_cache::successors_type successors_type; - -public: - - broadcast_cache( typename base_type::owner_type* owner ): base_type(owner) { - // Do not work with the passed pointer here as it may not be fully initialized yet - } - - // as above, but call try_put_task instead, and return the last task we received (if any) - graph_task* try_put_task( const T &t ) override { - graph_task * last_task = nullptr; - typename mutex_type::scoped_lock l(this->my_mutex, /*write=*/true); - typename successors_type::iterator i = this->my_successors.begin(); - while ( i != this->my_successors.end() ) { - graph_task *new_task = (*i)->try_put_task(t); - // workaround for icc bug - graph& graph_ref = (*i)->graph_reference(); - last_task = combine_tasks(graph_ref, last_task, new_task); // enqueue if necessary - if(new_task) { - ++i; - } - else { // failed - if ( (*i)->register_predecessor(*this->my_owner) ) { - i = this->my_successors.erase(i); - } else { - ++i; - } - } - } - return last_task; - } - - // call try_put_task and return list of received tasks - bool gather_successful_try_puts( const T &t, graph_task_list& tasks ) { - bool is_at_least_one_put_successful = false; - typename mutex_type::scoped_lock l(this->my_mutex, /*write=*/true); - typename successors_type::iterator i = this->my_successors.begin(); - while ( i != this->my_successors.end() ) { - graph_task * new_task = (*i)->try_put_task(t); - if(new_task) { - ++i; - if(new_task != SUCCESSFULLY_ENQUEUED) { - tasks.push_back(*new_task); - } - is_at_least_one_put_successful = true; - } - else { // failed - if ( (*i)->register_predecessor(*this->my_owner) ) { - i = this->my_successors.erase(i); - } else { - ++i; - } - } - } - return is_at_least_one_put_successful; - } -}; - -//! A cache of successors that are put in a round-robin fashion -template -class round_robin_cache : public successor_cache { - typedef successor_cache base_type; - typedef size_t size_type; - typedef M mutex_type; - typedef typename successor_cache::successors_type successors_type; - -public: - - round_robin_cache( typename base_type::owner_type* owner ): base_type(owner) { - // Do not work with the passed pointer here as it may not be fully initialized yet - } - - size_type size() { - typename mutex_type::scoped_lock l(this->my_mutex, false); - return this->my_successors.size(); - } - - graph_task* try_put_task( const T &t ) override { - typename mutex_type::scoped_lock l(this->my_mutex, /*write=*/true); - typename successors_type::iterator i = this->my_successors.begin(); - while ( i != this->my_successors.end() ) { - graph_task* new_task = (*i)->try_put_task(t); - if ( new_task ) { - return new_task; - } else { - if ( (*i)->register_predecessor(*this->my_owner) ) { - i = this->my_successors.erase(i); - } - else { - ++i; - } - } - } - return nullptr; - } -}; - -#endif // __TBB__flow_graph_cache_impl_H diff --git a/include/oneapi/tbb/detail/_flow_graph_impl.h b/include/oneapi/tbb/detail/_flow_graph_impl.h deleted file mode 100644 index 8207667..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_impl.h +++ /dev/null @@ -1,476 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_flow_graph_impl_H -#define __TBB_flow_graph_impl_H - -// #include "../config.h" -#include "_task.h" -#include "../task_group.h" -#include "../task_arena.h" -#include "../flow_graph_abstractions.h" - -#include "../concurrent_priority_queue.h" - -#include - -namespace tbb { -namespace detail { - -namespace d1 { - -class graph_task; -static graph_task* const SUCCESSFULLY_ENQUEUED = (graph_task*)-1; -typedef unsigned int node_priority_t; -static const node_priority_t no_priority = node_priority_t(0); - -class graph; -class graph_node; - -template -class graph_iterator { - friend class graph; - friend class graph_node; -public: - typedef size_t size_type; - typedef GraphNodeType value_type; - typedef GraphNodeType* pointer; - typedef GraphNodeType& reference; - typedef const GraphNodeType& const_reference; - typedef std::forward_iterator_tag iterator_category; - - //! Copy constructor - graph_iterator(const graph_iterator& other) : - my_graph(other.my_graph), current_node(other.current_node) - {} - - //! Assignment - graph_iterator& operator=(const graph_iterator& other) { - if (this != &other) { - my_graph = other.my_graph; - current_node = other.current_node; - } - return *this; - } - - //! Dereference - reference operator*() const; - - //! Dereference - pointer operator->() const; - - //! Equality - bool operator==(const graph_iterator& other) const { - return ((my_graph == other.my_graph) && (current_node == other.current_node)); - } - -#if !__TBB_CPP20_COMPARISONS_PRESENT - //! Inequality - bool operator!=(const graph_iterator& other) const { return !(operator==(other)); } -#endif - - //! Pre-increment - graph_iterator& operator++() { - internal_forward(); - return *this; - } - - //! Post-increment - graph_iterator operator++(int) { - graph_iterator result = *this; - operator++(); - return result; - } - -private: - // the graph over which we are iterating - GraphContainerType *my_graph; - // pointer into my_graph's my_nodes list - pointer current_node; - - //! Private initializing constructor for begin() and end() iterators - graph_iterator(GraphContainerType *g, bool begin); - void internal_forward(); -}; // class graph_iterator - -// flags to modify the behavior of the graph reset(). Can be combined. -enum reset_flags { - rf_reset_protocol = 0, - rf_reset_bodies = 1 << 0, // delete the current node body, reset to a copy of the initial node body. - rf_clear_edges = 1 << 1 // delete edges -}; - -void activate_graph(graph& g); -void deactivate_graph(graph& g); -bool is_graph_active(graph& g); -graph_task* prioritize_task(graph& g, graph_task& arena_task); -void spawn_in_graph_arena(graph& g, graph_task& arena_task); -void enqueue_in_graph_arena(graph &g, graph_task& arena_task); - -class graph; - -//! Base class for tasks generated by graph nodes. -class graph_task : public task { -public: - graph_task(graph& g, small_object_allocator& allocator - , node_priority_t node_priority = no_priority - ) - : my_graph(g) - , priority(node_priority) - , my_allocator(allocator) - {} - graph& my_graph; // graph instance the task belongs to - // TODO revamp: rename to my_priority - node_priority_t priority; - template - void destruct_and_deallocate(const execution_data& ed); -protected: - template - void finalize(const execution_data& ed); -private: - // To organize task_list - graph_task* my_next{ nullptr }; - small_object_allocator my_allocator; - // TODO revamp: elaborate internal interfaces to avoid friends declarations - friend class graph_task_list; - friend graph_task* prioritize_task(graph& g, graph_task& gt); -}; - -struct graph_task_comparator { - bool operator()(const graph_task* left, const graph_task* right) { - return left->priority < right->priority; - } -}; - -typedef tbb::concurrent_priority_queue graph_task_priority_queue_t; - -class priority_task_selector : public task { -public: - priority_task_selector(graph_task_priority_queue_t& priority_queue, small_object_allocator& allocator) - : my_priority_queue(priority_queue), my_allocator(allocator), my_task() {} - task* execute(execution_data& ed) override { - next_task(); - __TBB_ASSERT(my_task, nullptr); - task* t_next = my_task->execute(ed); - my_allocator.delete_object(this, ed); - return t_next; - } - task* cancel(execution_data& ed) override { - if (!my_task) { - next_task(); - } - __TBB_ASSERT(my_task, nullptr); - task* t_next = my_task->cancel(ed); - my_allocator.delete_object(this, ed); - return t_next; - } -private: - void next_task() { - // TODO revamp: hold functors in priority queue instead of real tasks - bool result = my_priority_queue.try_pop(my_task); - __TBB_ASSERT_EX(result, "Number of critical tasks for scheduler and tasks" - " in graph's priority queue mismatched"); - __TBB_ASSERT(my_task && my_task != SUCCESSFULLY_ENQUEUED, - "Incorrect task submitted to graph priority queue"); - __TBB_ASSERT(my_task->priority != no_priority, - "Tasks from graph's priority queue must have priority"); - } - - graph_task_priority_queue_t& my_priority_queue; - small_object_allocator my_allocator; - graph_task* my_task; -}; - -template class run_and_put_task; -template class run_task; - -//******************************************************************************** -// graph tasks helpers -//******************************************************************************** - -//! The list of graph tasks -class graph_task_list : no_copy { -private: - graph_task* my_first; - graph_task** my_next_ptr; -public: - //! Construct empty list - graph_task_list() : my_first(nullptr), my_next_ptr(&my_first) {} - - //! True if list is empty; false otherwise. - bool empty() const { return !my_first; } - - //! Push task onto back of list. - void push_back(graph_task& task) { - task.my_next = nullptr; - *my_next_ptr = &task; - my_next_ptr = &task.my_next; - } - - //! Pop the front task from the list. - graph_task& pop_front() { - __TBB_ASSERT(!empty(), "attempt to pop item from empty task_list"); - graph_task* result = my_first; - my_first = result->my_next; - if (!my_first) { - my_next_ptr = &my_first; - } - return *result; - } -}; - -//! The graph class -/** This class serves as a handle to the graph */ -class graph : no_copy, public graph_proxy { - friend class graph_node; - - void prepare_task_arena(bool reinit = false) { - if (reinit) { - __TBB_ASSERT(my_task_arena, "task arena is nullptr"); - my_task_arena->terminate(); - my_task_arena->initialize(task_arena::attach()); - } - else { - __TBB_ASSERT(my_task_arena == nullptr, "task arena is not nullptr"); - my_task_arena = new task_arena(task_arena::attach()); - } - if (!my_task_arena->is_active()) // failed to attach - my_task_arena->initialize(); // create a new, default-initialized arena - __TBB_ASSERT(my_task_arena->is_active(), "task arena is not active"); - } - -public: - //! Constructs a graph with isolated task_group_context - graph(); - - //! Constructs a graph with use_this_context as context - explicit graph(task_group_context& use_this_context); - - //! Destroys the graph. - /** Calls wait_for_all, then destroys the root task and context. */ - ~graph(); - - //! Used to register that an external entity may still interact with the graph. - /** The graph will not return from wait_for_all until a matching number of release_wait calls is - made. */ - void reserve_wait() override; - - //! Deregisters an external entity that may have interacted with the graph. - /** The graph will not return from wait_for_all until all the number of reserve_wait calls - matches the number of release_wait calls. */ - void release_wait() override; - - //! Wait until graph is idle and the number of release_wait calls equals to the number of - //! reserve_wait calls. - /** The waiting thread will go off and steal work while it is blocked in the wait_for_all. */ - void wait_for_all() { - cancelled = false; - caught_exception = false; - try_call([this] { - my_task_arena->execute([this] { - wait(my_wait_context, *my_context); - }); - cancelled = my_context->is_group_execution_cancelled(); - }).on_exception([this] { - my_context->reset(); - caught_exception = true; - cancelled = true; - }); - // TODO: the "if" condition below is just a work-around to support the concurrent wait - // mode. The cancellation and exception mechanisms are still broken in this mode. - // Consider using task group not to re-implement the same functionality. - if (!(my_context->traits() & task_group_context::concurrent_wait)) { - my_context->reset(); // consistent with behavior in catch() - } - } - - // TODO revamp: consider adding getter for task_group_context. - - // ITERATORS - template - friend class graph_iterator; - - // Graph iterator typedefs - typedef graph_iterator iterator; - typedef graph_iterator const_iterator; - - // Graph iterator constructors - //! start iterator - iterator begin(); - //! end iterator - iterator end(); - //! start const iterator - const_iterator begin() const; - //! end const iterator - const_iterator end() const; - //! start const iterator - const_iterator cbegin() const; - //! end const iterator - const_iterator cend() const; - - // thread-unsafe state reset. - void reset(reset_flags f = rf_reset_protocol); - - //! cancels execution of the associated task_group_context - void cancel(); - - //! return status of graph execution - bool is_cancelled() { return cancelled; } - bool exception_thrown() { return caught_exception; } - -private: - wait_context my_wait_context; - task_group_context *my_context; - bool own_context; - bool cancelled; - bool caught_exception; - bool my_is_active; - - graph_node *my_nodes, *my_nodes_last; - - tbb::spin_mutex nodelist_mutex; - void register_node(graph_node *n); - void remove_node(graph_node *n); - - task_arena* my_task_arena; - - graph_task_priority_queue_t my_priority_queue; - - friend void activate_graph(graph& g); - friend void deactivate_graph(graph& g); - friend bool is_graph_active(graph& g); - friend graph_task* prioritize_task(graph& g, graph_task& arena_task); - friend void spawn_in_graph_arena(graph& g, graph_task& arena_task); - friend void enqueue_in_graph_arena(graph &g, graph_task& arena_task); - - friend class task_arena_base; - -}; // class graph - -template -inline void graph_task::destruct_and_deallocate(const execution_data& ed) { - auto allocator = my_allocator; - // TODO: investigate if direct call of derived destructor gives any benefits. - this->~graph_task(); - allocator.deallocate(static_cast(this), ed); -} - -template -inline void graph_task::finalize(const execution_data& ed) { - graph& g = my_graph; - destruct_and_deallocate(ed); - g.release_wait(); -} - -//******************************************************************************** -// end of graph tasks helpers -//******************************************************************************** - - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET -class get_graph_helper; -#endif - -//! The base of all graph nodes. -class graph_node : no_copy { - friend class graph; - template - friend class graph_iterator; - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - friend class get_graph_helper; -#endif - -protected: - graph& my_graph; - graph& graph_reference() const { - // TODO revamp: propagate graph_reference() method to all the reference places. - return my_graph; - } - graph_node* next = nullptr; - graph_node* prev = nullptr; -public: - explicit graph_node(graph& g); - - virtual ~graph_node(); - -protected: - // performs the reset on an individual node. - virtual void reset_node(reset_flags f = rf_reset_protocol) = 0; -}; // class graph_node - -inline void activate_graph(graph& g) { - g.my_is_active = true; -} - -inline void deactivate_graph(graph& g) { - g.my_is_active = false; -} - -inline bool is_graph_active(graph& g) { - return g.my_is_active; -} - -inline graph_task* prioritize_task(graph& g, graph_task& gt) { - if( no_priority == gt.priority ) - return > - - //! Non-preemptive priority pattern. The original task is submitted as a work item to the - //! priority queue, and a new critical task is created to take and execute a work item with - //! the highest known priority. The reference counting responsibility is transferred (via - //! allocate_continuation) to the new task. - task* critical_task = gt.my_allocator.new_object(g.my_priority_queue, gt.my_allocator); - __TBB_ASSERT( critical_task, "bad_alloc?" ); - g.my_priority_queue.push(>); - using tbb::detail::d1::submit; - submit( *critical_task, *g.my_task_arena, *g.my_context, /*as_critical=*/true ); - return nullptr; -} - -//! Spawns a task inside graph arena -inline void spawn_in_graph_arena(graph& g, graph_task& arena_task) { - if (is_graph_active(g)) { - task* gt = prioritize_task(g, arena_task); - if( !gt ) - return; - - __TBB_ASSERT(g.my_task_arena && g.my_task_arena->is_active(), nullptr); - submit( *gt, *g.my_task_arena, *g.my_context -#if __TBB_PREVIEW_CRITICAL_TASKS - , /*as_critical=*/false -#endif - ); - } -} - -// TODO revamp: unify *_in_graph_arena functions - -//! Enqueues a task inside graph arena -inline void enqueue_in_graph_arena(graph &g, graph_task& arena_task) { - if (is_graph_active(g)) { - __TBB_ASSERT( g.my_task_arena && g.my_task_arena->is_active(), "Is graph's arena initialized and active?" ); - - // TODO revamp: decide on the approach that does not postpone critical task - if( task* gt = prioritize_task(g, arena_task) ) - submit( *gt, *g.my_task_arena, *g.my_context, /*as_critical=*/false); - } -} - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB_flow_graph_impl_H diff --git a/include/oneapi/tbb/detail/_flow_graph_indexer_impl.h b/include/oneapi/tbb/detail/_flow_graph_indexer_impl.h deleted file mode 100644 index f4f55a6..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_indexer_impl.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__flow_graph_indexer_impl_H -#define __TBB__flow_graph_indexer_impl_H - -#ifndef __TBB_flow_graph_H -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -// included in namespace tbb::detail::d1 - -#include "_flow_graph_types_impl.h" - - // Output of the indexer_node is a tbb::flow::tagged_msg, and will be of - // the form tagged_msg - // where the value of tag will indicate which result was put to the - // successor. - - template - graph_task* do_try_put(const T &v, void *p) { - typename IndexerNodeBaseType::output_type o(K, v); - return reinterpret_cast(p)->try_put_task(&o); - } - - template - struct indexer_helper { - template - static inline void set_indexer_node_pointer(PortTuple &my_input, IndexerNodeBaseType *p, graph& g) { - typedef typename std::tuple_element::type T; - graph_task* (*indexer_node_put_task)(const T&, void *) = do_try_put; - std::get(my_input).set_up(p, indexer_node_put_task, g); - indexer_helper::template set_indexer_node_pointer(my_input, p, g); - } - }; - - template - struct indexer_helper { - template - static inline void set_indexer_node_pointer(PortTuple &my_input, IndexerNodeBaseType *p, graph& g) { - typedef typename std::tuple_element<0, TupleTypes>::type T; - graph_task* (*indexer_node_put_task)(const T&, void *) = do_try_put; - std::get<0>(my_input).set_up(p, indexer_node_put_task, g); - } - }; - - template - class indexer_input_port : public receiver { - private: - void* my_indexer_ptr; - typedef graph_task* (* forward_function_ptr)(T const &, void* ); - forward_function_ptr my_try_put_task; - graph* my_graph; - public: - void set_up(void* p, forward_function_ptr f, graph& g) { - my_indexer_ptr = p; - my_try_put_task = f; - my_graph = &g; - } - - protected: - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - graph_task* try_put_task(const T &v) override { - return my_try_put_task(v, my_indexer_ptr); - } - - graph& graph_reference() const override { - return *my_graph; - } - }; - - template - class indexer_node_FE { - public: - static const int N = std::tuple_size::value; - typedef OutputType output_type; - typedef InputTuple input_type; - - // Some versions of Intel(R) C++ Compiler fail to generate an implicit constructor for the class which has std::tuple as a member. - indexer_node_FE() : my_inputs() {} - - input_type &input_ports() { return my_inputs; } - protected: - input_type my_inputs; - }; - - //! indexer_node_base - template - class indexer_node_base : public graph_node, public indexer_node_FE, - public sender { - protected: - using graph_node::my_graph; - public: - static const size_t N = std::tuple_size::value; - typedef OutputType output_type; - typedef StructTypes tuple_types; - typedef typename sender::successor_type successor_type; - typedef indexer_node_FE input_ports_type; - - private: - // ----------- Aggregator ------------ - enum op_type { reg_succ, rem_succ, try__put_task - }; - typedef indexer_node_base class_type; - - class indexer_node_base_operation : public aggregated_operation { - public: - char type; - union { - output_type const *my_arg; - successor_type *my_succ; - graph_task* bypass_t; - }; - indexer_node_base_operation(const output_type* e, op_type t) : - type(char(t)), my_arg(e) {} - indexer_node_base_operation(const successor_type &s, op_type t) : type(char(t)), - my_succ(const_cast(&s)) {} - }; - - typedef aggregating_functor handler_type; - friend class aggregating_functor; - aggregator my_aggregator; - - void handle_operations(indexer_node_base_operation* op_list) { - indexer_node_base_operation *current; - while(op_list) { - current = op_list; - op_list = op_list->next; - switch(current->type) { - - case reg_succ: - my_successors.register_successor(*(current->my_succ)); - current->status.store( SUCCEEDED, std::memory_order_release); - break; - - case rem_succ: - my_successors.remove_successor(*(current->my_succ)); - current->status.store( SUCCEEDED, std::memory_order_release); - break; - case try__put_task: { - current->bypass_t = my_successors.try_put_task(*(current->my_arg)); - current->status.store( SUCCEEDED, std::memory_order_release); // return of try_put_task actual return value - } - break; - } - } - } - // ---------- end aggregator ----------- - public: - indexer_node_base(graph& g) : graph_node(g), input_ports_type(), my_successors(this) { - indexer_helper::set_indexer_node_pointer(this->my_inputs, this, g); - my_aggregator.initialize_handler(handler_type(this)); - } - - indexer_node_base(const indexer_node_base& other) - : graph_node(other.my_graph), input_ports_type(), sender(), my_successors(this) - { - indexer_helper::set_indexer_node_pointer(this->my_inputs, this, other.my_graph); - my_aggregator.initialize_handler(handler_type(this)); - } - - bool register_successor(successor_type &r) override { - indexer_node_base_operation op_data(r, reg_succ); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - bool remove_successor( successor_type &r) override { - indexer_node_base_operation op_data(r, rem_succ); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - graph_task* try_put_task(output_type const *v) { // not a virtual method in this class - indexer_node_base_operation op_data(v, try__put_task); - my_aggregator.execute(&op_data); - return op_data.bypass_t; - } - - protected: - void reset_node(reset_flags f) override { - if(f & rf_clear_edges) { - my_successors.clear(); - } - } - - private: - broadcast_cache my_successors; - }; //indexer_node_base - - - template struct input_types; - - template - struct input_types<1, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef tagged_msg type; - }; - - template - struct input_types<2, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef typename std::tuple_element<1, InputTuple>::type second_type; - typedef tagged_msg type; - }; - - template - struct input_types<3, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef typename std::tuple_element<1, InputTuple>::type second_type; - typedef typename std::tuple_element<2, InputTuple>::type third_type; - typedef tagged_msg type; - }; - - template - struct input_types<4, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef typename std::tuple_element<1, InputTuple>::type second_type; - typedef typename std::tuple_element<2, InputTuple>::type third_type; - typedef typename std::tuple_element<3, InputTuple>::type fourth_type; - typedef tagged_msg type; - }; - - template - struct input_types<5, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef typename std::tuple_element<1, InputTuple>::type second_type; - typedef typename std::tuple_element<2, InputTuple>::type third_type; - typedef typename std::tuple_element<3, InputTuple>::type fourth_type; - typedef typename std::tuple_element<4, InputTuple>::type fifth_type; - typedef tagged_msg type; - }; - - template - struct input_types<6, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef typename std::tuple_element<1, InputTuple>::type second_type; - typedef typename std::tuple_element<2, InputTuple>::type third_type; - typedef typename std::tuple_element<3, InputTuple>::type fourth_type; - typedef typename std::tuple_element<4, InputTuple>::type fifth_type; - typedef typename std::tuple_element<5, InputTuple>::type sixth_type; - typedef tagged_msg type; - }; - - template - struct input_types<7, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef typename std::tuple_element<1, InputTuple>::type second_type; - typedef typename std::tuple_element<2, InputTuple>::type third_type; - typedef typename std::tuple_element<3, InputTuple>::type fourth_type; - typedef typename std::tuple_element<4, InputTuple>::type fifth_type; - typedef typename std::tuple_element<5, InputTuple>::type sixth_type; - typedef typename std::tuple_element<6, InputTuple>::type seventh_type; - typedef tagged_msg type; - }; - - - template - struct input_types<8, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef typename std::tuple_element<1, InputTuple>::type second_type; - typedef typename std::tuple_element<2, InputTuple>::type third_type; - typedef typename std::tuple_element<3, InputTuple>::type fourth_type; - typedef typename std::tuple_element<4, InputTuple>::type fifth_type; - typedef typename std::tuple_element<5, InputTuple>::type sixth_type; - typedef typename std::tuple_element<6, InputTuple>::type seventh_type; - typedef typename std::tuple_element<7, InputTuple>::type eighth_type; - typedef tagged_msg type; - }; - - - template - struct input_types<9, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef typename std::tuple_element<1, InputTuple>::type second_type; - typedef typename std::tuple_element<2, InputTuple>::type third_type; - typedef typename std::tuple_element<3, InputTuple>::type fourth_type; - typedef typename std::tuple_element<4, InputTuple>::type fifth_type; - typedef typename std::tuple_element<5, InputTuple>::type sixth_type; - typedef typename std::tuple_element<6, InputTuple>::type seventh_type; - typedef typename std::tuple_element<7, InputTuple>::type eighth_type; - typedef typename std::tuple_element<8, InputTuple>::type nineth_type; - typedef tagged_msg type; - }; - - template - struct input_types<10, InputTuple> { - typedef typename std::tuple_element<0, InputTuple>::type first_type; - typedef typename std::tuple_element<1, InputTuple>::type second_type; - typedef typename std::tuple_element<2, InputTuple>::type third_type; - typedef typename std::tuple_element<3, InputTuple>::type fourth_type; - typedef typename std::tuple_element<4, InputTuple>::type fifth_type; - typedef typename std::tuple_element<5, InputTuple>::type sixth_type; - typedef typename std::tuple_element<6, InputTuple>::type seventh_type; - typedef typename std::tuple_element<7, InputTuple>::type eighth_type; - typedef typename std::tuple_element<8, InputTuple>::type nineth_type; - typedef typename std::tuple_element<9, InputTuple>::type tenth_type; - typedef tagged_msg type; - }; - - // type generators - template - struct indexer_types : public input_types::value, OutputTuple> { - static const int N = std::tuple_size::value; - typedef typename input_types::type output_type; - typedef typename wrap_tuple_elements::type input_ports_type; - typedef indexer_node_FE indexer_FE_type; - typedef indexer_node_base indexer_base_type; - }; - - template - class unfolded_indexer_node : public indexer_types::indexer_base_type { - public: - typedef typename indexer_types::input_ports_type input_ports_type; - typedef OutputTuple tuple_types; - typedef typename indexer_types::output_type output_type; - private: - typedef typename indexer_types::indexer_base_type base_type; - public: - unfolded_indexer_node(graph& g) : base_type(g) {} - unfolded_indexer_node(const unfolded_indexer_node &other) : base_type(other) {} - }; - -#endif /* __TBB__flow_graph_indexer_impl_H */ diff --git a/include/oneapi/tbb/detail/_flow_graph_item_buffer_impl.h b/include/oneapi/tbb/detail/_flow_graph_item_buffer_impl.h deleted file mode 100644 index 423033b..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_item_buffer_impl.h +++ /dev/null @@ -1,279 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__flow_graph_item_buffer_impl_H -#define __TBB__flow_graph_item_buffer_impl_H - -#ifndef __TBB_flow_graph_H -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -#include "_aligned_space.h" - -// in namespace tbb::flow::interfaceX (included in _flow_graph_node_impl.h) - -//! Expandable buffer of items. The possible operations are push, pop, -//* tests for empty and so forth. No mutual exclusion is built in. -//* objects are constructed into and explicitly-destroyed. get_my_item gives -// a read-only reference to the item in the buffer. set_my_item may be called -// with either an empty or occupied slot. - -template > -class item_buffer { -public: - typedef T item_type; - enum buffer_item_state { no_item=0, has_item=1, reserved_item=2 }; -protected: - typedef size_t size_type; - typedef std::pair aligned_space_item; - typedef aligned_space buffer_item_type; - typedef typename allocator_traits::template rebind_alloc allocator_type; - buffer_item_type *my_array; - size_type my_array_size; - static const size_type initial_buffer_size = 4; - size_type my_head; - size_type my_tail; - - bool buffer_empty() const { return my_head == my_tail; } - - aligned_space_item &item(size_type i) { - __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].begin()->second))%alignment_of::value), nullptr); - __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].begin()->first))%alignment_of::value), nullptr); - return *my_array[i & (my_array_size - 1) ].begin(); - } - - const aligned_space_item &item(size_type i) const { - __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].begin()->second))%alignment_of::value), nullptr); - __TBB_ASSERT(!(size_type(&(my_array[i&(my_array_size-1)].begin()->first))%alignment_of::value), nullptr); - return *my_array[i & (my_array_size-1)].begin(); - } - - bool my_item_valid(size_type i) const { return (i < my_tail) && (i >= my_head) && (item(i).second != no_item); } -#if TBB_USE_ASSERT - bool my_item_reserved(size_type i) const { return item(i).second == reserved_item; } -#endif - - // object management in buffer - const item_type &get_my_item(size_t i) const { - __TBB_ASSERT(my_item_valid(i),"attempt to get invalid item"); - item_type* itm = const_cast(reinterpret_cast(&item(i).first)); - return *itm; - } - - // may be called with an empty slot or a slot that has already been constructed into. - void set_my_item(size_t i, const item_type &o) { - if(item(i).second != no_item) { - destroy_item(i); - } - new(&(item(i).first)) item_type(o); - item(i).second = has_item; - } - - // destructively-fetch an object from the buffer - void fetch_item(size_t i, item_type &o) { - __TBB_ASSERT(my_item_valid(i), "Trying to fetch an empty slot"); - o = get_my_item(i); // could have std::move assign semantics - destroy_item(i); - } - - // move an existing item from one slot to another. The moved-to slot must be unoccupied, - // the moved-from slot must exist and not be reserved. The after, from will be empty, - // to will be occupied but not reserved - void move_item(size_t to, size_t from) { - __TBB_ASSERT(!my_item_valid(to), "Trying to move to a non-empty slot"); - __TBB_ASSERT(my_item_valid(from), "Trying to move from an empty slot"); - set_my_item(to, get_my_item(from)); // could have std::move semantics - destroy_item(from); - - } - - // put an item in an empty slot. Return true if successful, else false - bool place_item(size_t here, const item_type &me) { -#if !TBB_DEPRECATED_SEQUENCER_DUPLICATES - if(my_item_valid(here)) return false; -#endif - set_my_item(here, me); - return true; - } - - // could be implemented with std::move semantics - void swap_items(size_t i, size_t j) { - __TBB_ASSERT(my_item_valid(i) && my_item_valid(j), "attempt to swap invalid item(s)"); - item_type temp = get_my_item(i); - set_my_item(i, get_my_item(j)); - set_my_item(j, temp); - } - - void destroy_item(size_type i) { - __TBB_ASSERT(my_item_valid(i), "destruction of invalid item"); - item(i).first.~item_type(); - item(i).second = no_item; - } - - // returns the front element - const item_type& front() const - { - __TBB_ASSERT(my_item_valid(my_head), "attempt to fetch head non-item"); - return get_my_item(my_head); - } - - // returns the back element - const item_type& back() const - { - __TBB_ASSERT(my_item_valid(my_tail - 1), "attempt to fetch head non-item"); - return get_my_item(my_tail - 1); - } - - // following methods are for reservation of the front of a buffer. - void reserve_item(size_type i) { __TBB_ASSERT(my_item_valid(i) && !my_item_reserved(i), "item cannot be reserved"); item(i).second = reserved_item; } - void release_item(size_type i) { __TBB_ASSERT(my_item_reserved(i), "item is not reserved"); item(i).second = has_item; } - - void destroy_front() { destroy_item(my_head); ++my_head; } - void destroy_back() { destroy_item(my_tail-1); --my_tail; } - - // we have to be able to test against a new tail value without changing my_tail - // grow_array doesn't work if we change my_tail when the old array is too small - size_type size(size_t new_tail = 0) { return (new_tail ? new_tail : my_tail) - my_head; } - size_type capacity() { return my_array_size; } - // sequencer_node does not use this method, so we don't - // need a version that passes in the new_tail value. - bool buffer_full() { return size() >= capacity(); } - - //! Grows the internal array. - void grow_my_array( size_t minimum_size ) { - // test that we haven't made the structure inconsistent. - __TBB_ASSERT(capacity() >= my_tail - my_head, "total items exceed capacity"); - size_type new_size = my_array_size ? 2*my_array_size : initial_buffer_size; - while( new_sizesecond = no_item; } - - for( size_type i=my_head; ifirst); - (void)new(new_space) item_type(get_my_item(i)); - new_array[i&(new_size-1)].begin()->second = item(i).second; - } - } - - clean_up_buffer(/*reset_pointers*/false); - - my_array = new_array; - my_array_size = new_size; - } - - bool push_back(item_type &v) { - if(buffer_full()) { - grow_my_array(size() + 1); - } - set_my_item(my_tail, v); - ++my_tail; - return true; - } - - bool pop_back(item_type &v) { - if (!my_item_valid(my_tail-1)) { - return false; - } - v = this->back(); - destroy_back(); - return true; - } - - bool pop_front(item_type &v) { - if(!my_item_valid(my_head)) { - return false; - } - v = this->front(); - destroy_front(); - return true; - } - - // This is used both for reset and for grow_my_array. In the case of grow_my_array - // we want to retain the values of the head and tail. - void clean_up_buffer(bool reset_pointers) { - if (my_array) { - for( size_type i=my_head; i > -class reservable_item_buffer : public item_buffer { -protected: - using item_buffer::my_item_valid; - using item_buffer::my_head; - -public: - reservable_item_buffer() : item_buffer(), my_reserved(false) {} - void reset() {my_reserved = false; item_buffer::reset(); } -protected: - - bool reserve_front(T &v) { - if(my_reserved || !my_item_valid(this->my_head)) return false; - my_reserved = true; - // reserving the head - v = this->front(); - this->reserve_item(this->my_head); - return true; - } - - void consume_front() { - __TBB_ASSERT(my_reserved, "Attempt to consume a non-reserved item"); - this->destroy_front(); - my_reserved = false; - } - - void release_front() { - __TBB_ASSERT(my_reserved, "Attempt to release a non-reserved item"); - this->release_item(this->my_head); - my_reserved = false; - } - - bool my_reserved; -}; - -#endif // __TBB__flow_graph_item_buffer_impl_H diff --git a/include/oneapi/tbb/detail/_flow_graph_join_impl.h b/include/oneapi/tbb/detail/_flow_graph_join_impl.h deleted file mode 100644 index 5515421..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_join_impl.h +++ /dev/null @@ -1,1708 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__flow_graph_join_impl_H -#define __TBB__flow_graph_join_impl_H - -#ifndef __TBB_flow_graph_H -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -// included into namespace tbb::detail::d1 - - struct forwarding_base : no_assign { - forwarding_base(graph &g) : graph_ref(g) {} - virtual ~forwarding_base() {} - graph& graph_ref; - }; - - struct queueing_forwarding_base : forwarding_base { - using forwarding_base::forwarding_base; - // decrement_port_count may create a forwarding task. If we cannot handle the task - // ourselves, ask decrement_port_count to deal with it. - virtual graph_task* decrement_port_count(bool handle_task) = 0; - }; - - struct reserving_forwarding_base : forwarding_base { - using forwarding_base::forwarding_base; - // decrement_port_count may create a forwarding task. If we cannot handle the task - // ourselves, ask decrement_port_count to deal with it. - virtual graph_task* decrement_port_count() = 0; - virtual void increment_port_count() = 0; - }; - - // specialization that lets us keep a copy of the current_key for building results. - // KeyType can be a reference type. - template - struct matching_forwarding_base : public forwarding_base { - typedef typename std::decay::type current_key_type; - matching_forwarding_base(graph &g) : forwarding_base(g) { } - virtual graph_task* increment_key_count(current_key_type const & /*t*/) = 0; - current_key_type current_key; // so ports can refer to FE's desired items - }; - - template< int N > - struct join_helper { - - template< typename TupleType, typename PortType > - static inline void set_join_node_pointer(TupleType &my_input, PortType *port) { - std::get( my_input ).set_join_node_pointer(port); - join_helper::set_join_node_pointer( my_input, port ); - } - template< typename TupleType > - static inline void consume_reservations( TupleType &my_input ) { - std::get( my_input ).consume(); - join_helper::consume_reservations( my_input ); - } - - template< typename TupleType > - static inline void release_my_reservation( TupleType &my_input ) { - std::get( my_input ).release(); - } - - template - static inline void release_reservations( TupleType &my_input) { - join_helper::release_reservations(my_input); - release_my_reservation(my_input); - } - - template< typename InputTuple, typename OutputTuple > - static inline bool reserve( InputTuple &my_input, OutputTuple &out) { - if ( !std::get( my_input ).reserve( std::get( out ) ) ) return false; - if ( !join_helper::reserve( my_input, out ) ) { - release_my_reservation( my_input ); - return false; - } - return true; - } - - template - static inline bool get_my_item( InputTuple &my_input, OutputTuple &out) { - bool res = std::get(my_input).get_item(std::get(out) ); // may fail - return join_helper::get_my_item(my_input, out) && res; // do get on other inputs before returning - } - - template - static inline bool get_items(InputTuple &my_input, OutputTuple &out) { - return get_my_item(my_input, out); - } - - template - static inline void reset_my_port(InputTuple &my_input) { - join_helper::reset_my_port(my_input); - std::get(my_input).reset_port(); - } - - template - static inline void reset_ports(InputTuple& my_input) { - reset_my_port(my_input); - } - - template - static inline void set_key_functors(InputTuple &my_input, KeyFuncTuple &my_key_funcs) { - std::get(my_input).set_my_key_func(std::get(my_key_funcs)); - std::get(my_key_funcs) = nullptr; - join_helper::set_key_functors(my_input, my_key_funcs); - } - - template< typename KeyFuncTuple> - static inline void copy_key_functors(KeyFuncTuple &my_inputs, KeyFuncTuple &other_inputs) { - __TBB_ASSERT( - std::get(other_inputs).get_my_key_func(), - "key matching join node should not be instantiated without functors." - ); - std::get(my_inputs).set_my_key_func(std::get(other_inputs).get_my_key_func()->clone()); - join_helper::copy_key_functors(my_inputs, other_inputs); - } - - template - static inline void reset_inputs(InputTuple &my_input, reset_flags f) { - join_helper::reset_inputs(my_input, f); - std::get(my_input).reset_receiver(f); - } - }; // join_helper - - template< > - struct join_helper<1> { - - template< typename TupleType, typename PortType > - static inline void set_join_node_pointer(TupleType &my_input, PortType *port) { - std::get<0>( my_input ).set_join_node_pointer(port); - } - - template< typename TupleType > - static inline void consume_reservations( TupleType &my_input ) { - std::get<0>( my_input ).consume(); - } - - template< typename TupleType > - static inline void release_my_reservation( TupleType &my_input ) { - std::get<0>( my_input ).release(); - } - - template - static inline void release_reservations( TupleType &my_input) { - release_my_reservation(my_input); - } - - template< typename InputTuple, typename OutputTuple > - static inline bool reserve( InputTuple &my_input, OutputTuple &out) { - return std::get<0>( my_input ).reserve( std::get<0>( out ) ); - } - - template - static inline bool get_my_item( InputTuple &my_input, OutputTuple &out) { - return std::get<0>(my_input).get_item(std::get<0>(out)); - } - - template - static inline bool get_items(InputTuple &my_input, OutputTuple &out) { - return get_my_item(my_input, out); - } - - template - static inline void reset_my_port(InputTuple &my_input) { - std::get<0>(my_input).reset_port(); - } - - template - static inline void reset_ports(InputTuple& my_input) { - reset_my_port(my_input); - } - - template - static inline void set_key_functors(InputTuple &my_input, KeyFuncTuple &my_key_funcs) { - std::get<0>(my_input).set_my_key_func(std::get<0>(my_key_funcs)); - std::get<0>(my_key_funcs) = nullptr; - } - - template< typename KeyFuncTuple> - static inline void copy_key_functors(KeyFuncTuple &my_inputs, KeyFuncTuple &other_inputs) { - __TBB_ASSERT( - std::get<0>(other_inputs).get_my_key_func(), - "key matching join node should not be instantiated without functors." - ); - std::get<0>(my_inputs).set_my_key_func(std::get<0>(other_inputs).get_my_key_func()->clone()); - } - template - static inline void reset_inputs(InputTuple &my_input, reset_flags f) { - std::get<0>(my_input).reset_receiver(f); - } - }; // join_helper<1> - - //! The two-phase join port - template< typename T > - class reserving_port : public receiver { - public: - typedef T input_type; - typedef typename receiver::predecessor_type predecessor_type; - - private: - // ----------- Aggregator ------------ - enum op_type { reg_pred, rem_pred, res_item, rel_res, con_res - }; - typedef reserving_port class_type; - - class reserving_port_operation : public aggregated_operation { - public: - char type; - union { - T *my_arg; - predecessor_type *my_pred; - }; - reserving_port_operation(const T& e, op_type t) : - type(char(t)), my_arg(const_cast(&e)) {} - reserving_port_operation(const predecessor_type &s, op_type t) : type(char(t)), - my_pred(const_cast(&s)) {} - reserving_port_operation(op_type t) : type(char(t)) {} - }; - - typedef aggregating_functor handler_type; - friend class aggregating_functor; - aggregator my_aggregator; - - void handle_operations(reserving_port_operation* op_list) { - reserving_port_operation *current; - bool was_missing_predecessors = false; - while(op_list) { - current = op_list; - op_list = op_list->next; - switch(current->type) { - case reg_pred: - was_missing_predecessors = my_predecessors.empty(); - my_predecessors.add(*(current->my_pred)); - if ( was_missing_predecessors ) { - (void) my_join->decrement_port_count(); // may try to forward - } - current->status.store( SUCCEEDED, std::memory_order_release); - break; - case rem_pred: - if ( !my_predecessors.empty() ) { - my_predecessors.remove(*(current->my_pred)); - if ( my_predecessors.empty() ) // was the last predecessor - my_join->increment_port_count(); - } - // TODO: consider returning failure if there were no predecessors to remove - current->status.store( SUCCEEDED, std::memory_order_release ); - break; - case res_item: - if ( reserved ) { - current->status.store( FAILED, std::memory_order_release); - } - else if ( my_predecessors.try_reserve( *(current->my_arg) ) ) { - reserved = true; - current->status.store( SUCCEEDED, std::memory_order_release); - } else { - if ( my_predecessors.empty() ) { - my_join->increment_port_count(); - } - current->status.store( FAILED, std::memory_order_release); - } - break; - case rel_res: - reserved = false; - my_predecessors.try_release( ); - current->status.store( SUCCEEDED, std::memory_order_release); - break; - case con_res: - reserved = false; - my_predecessors.try_consume( ); - current->status.store( SUCCEEDED, std::memory_order_release); - break; - } - } - } - - protected: - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - graph_task* try_put_task( const T & ) override { - return nullptr; - } - - graph& graph_reference() const override { - return my_join->graph_ref; - } - - public: - - //! Constructor - reserving_port() : my_join(nullptr), my_predecessors(this), reserved(false) { - my_aggregator.initialize_handler(handler_type(this)); - } - - // copy constructor - reserving_port(const reserving_port& /* other */) = delete; - - void set_join_node_pointer(reserving_forwarding_base *join) { - my_join = join; - } - - //! Add a predecessor - bool register_predecessor( predecessor_type &src ) override { - reserving_port_operation op_data(src, reg_pred); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - //! Remove a predecessor - bool remove_predecessor( predecessor_type &src ) override { - reserving_port_operation op_data(src, rem_pred); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - //! Reserve an item from the port - bool reserve( T &v ) { - reserving_port_operation op_data(v, res_item); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - //! Release the port - void release( ) { - reserving_port_operation op_data(rel_res); - my_aggregator.execute(&op_data); - } - - //! Complete use of the port - void consume( ) { - reserving_port_operation op_data(con_res); - my_aggregator.execute(&op_data); - } - - void reset_receiver( reset_flags f) { - if(f & rf_clear_edges) my_predecessors.clear(); - else - my_predecessors.reset(); - reserved = false; - __TBB_ASSERT(!(f&rf_clear_edges) || my_predecessors.empty(), "port edges not removed"); - } - - private: -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - friend class get_graph_helper; -#endif - - reserving_forwarding_base *my_join; - reservable_predecessor_cache< T, null_mutex > my_predecessors; - bool reserved; - }; // reserving_port - - //! queueing join_port - template - class queueing_port : public receiver, public item_buffer { - public: - typedef T input_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef queueing_port class_type; - - // ----------- Aggregator ------------ - private: - enum op_type { get__item, res_port, try__put_task - }; - - class queueing_port_operation : public aggregated_operation { - public: - char type; - T my_val; - T* my_arg; - graph_task* bypass_t; - // constructor for value parameter - queueing_port_operation(const T& e, op_type t) : - type(char(t)), my_val(e), my_arg(nullptr) - , bypass_t(nullptr) - {} - // constructor for pointer parameter - queueing_port_operation(const T* p, op_type t) : - type(char(t)), my_arg(const_cast(p)) - , bypass_t(nullptr) - {} - // constructor with no parameter - queueing_port_operation(op_type t) : type(char(t)), my_arg(nullptr) - , bypass_t(nullptr) - {} - }; - - typedef aggregating_functor handler_type; - friend class aggregating_functor; - aggregator my_aggregator; - - void handle_operations(queueing_port_operation* op_list) { - queueing_port_operation *current; - bool was_empty; - while(op_list) { - current = op_list; - op_list = op_list->next; - switch(current->type) { - case try__put_task: { - graph_task* rtask = nullptr; - was_empty = this->buffer_empty(); - this->push_back(current->my_val); - if (was_empty) rtask = my_join->decrement_port_count(false); - else - rtask = SUCCESSFULLY_ENQUEUED; - current->bypass_t = rtask; - current->status.store( SUCCEEDED, std::memory_order_release); - } - break; - case get__item: - if(!this->buffer_empty()) { - __TBB_ASSERT(current->my_arg, nullptr); - *(current->my_arg) = this->front(); - current->status.store( SUCCEEDED, std::memory_order_release); - } - else { - current->status.store( FAILED, std::memory_order_release); - } - break; - case res_port: - __TBB_ASSERT(this->my_item_valid(this->my_head), "No item to reset"); - this->destroy_front(); - if(this->my_item_valid(this->my_head)) { - (void)my_join->decrement_port_count(true); - } - current->status.store( SUCCEEDED, std::memory_order_release); - break; - } - } - } - // ------------ End Aggregator --------------- - - protected: - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - graph_task* try_put_task(const T &v) override { - queueing_port_operation op_data(v, try__put_task); - my_aggregator.execute(&op_data); - __TBB_ASSERT(op_data.status == SUCCEEDED || !op_data.bypass_t, "inconsistent return from aggregator"); - if(!op_data.bypass_t) return SUCCESSFULLY_ENQUEUED; - return op_data.bypass_t; - } - - graph& graph_reference() const override { - return my_join->graph_ref; - } - - public: - - //! Constructor - queueing_port() : item_buffer() { - my_join = nullptr; - my_aggregator.initialize_handler(handler_type(this)); - } - - //! copy constructor - queueing_port(const queueing_port& /* other */) = delete; - - //! record parent for tallying available items - void set_join_node_pointer(queueing_forwarding_base *join) { - my_join = join; - } - - bool get_item( T &v ) { - queueing_port_operation op_data(&v, get__item); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - // reset_port is called when item is accepted by successor, but - // is initiated by join_node. - void reset_port() { - queueing_port_operation op_data(res_port); - my_aggregator.execute(&op_data); - return; - } - - void reset_receiver(reset_flags) { - item_buffer::reset(); - } - - private: -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - friend class get_graph_helper; -#endif - - queueing_forwarding_base *my_join; - }; // queueing_port - -#include "_flow_graph_tagged_buffer_impl.h" - - template - struct count_element { - K my_key; - size_t my_value; - }; - - // method to access the key in the counting table - // the ref has already been removed from K - template< typename K > - struct key_to_count_functor { - typedef count_element table_item_type; - const K& operator()(const table_item_type& v) { return v.my_key; } - }; - - // the ports can have only one template parameter. We wrap the types needed in - // a traits type - template< class TraitsType > - class key_matching_port : - public receiver, - public hash_buffer< typename TraitsType::K, typename TraitsType::T, typename TraitsType::TtoK, - typename TraitsType::KHash > { - public: - typedef TraitsType traits; - typedef key_matching_port class_type; - typedef typename TraitsType::T input_type; - typedef typename TraitsType::K key_type; - typedef typename std::decay::type noref_key_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef typename TraitsType::TtoK type_to_key_func_type; - typedef typename TraitsType::KHash hash_compare_type; - typedef hash_buffer< key_type, input_type, type_to_key_func_type, hash_compare_type > buffer_type; - - private: -// ----------- Aggregator ------------ - private: - enum op_type { try__put, get__item, res_port - }; - - class key_matching_port_operation : public aggregated_operation { - public: - char type; - input_type my_val; - input_type *my_arg; - // constructor for value parameter - key_matching_port_operation(const input_type& e, op_type t) : - type(char(t)), my_val(e), my_arg(nullptr) {} - // constructor for pointer parameter - key_matching_port_operation(const input_type* p, op_type t) : - type(char(t)), my_arg(const_cast(p)) {} - // constructor with no parameter - key_matching_port_operation(op_type t) : type(char(t)), my_arg(nullptr) {} - }; - - typedef aggregating_functor handler_type; - friend class aggregating_functor; - aggregator my_aggregator; - - void handle_operations(key_matching_port_operation* op_list) { - key_matching_port_operation *current; - while(op_list) { - current = op_list; - op_list = op_list->next; - switch(current->type) { - case try__put: { - bool was_inserted = this->insert_with_key(current->my_val); - // return failure if a duplicate insertion occurs - current->status.store( was_inserted ? SUCCEEDED : FAILED, std::memory_order_release); - } - break; - case get__item: - // use current_key from FE for item - __TBB_ASSERT(current->my_arg, nullptr); - if(!this->find_with_key(my_join->current_key, *(current->my_arg))) { - __TBB_ASSERT(false, "Failed to find item corresponding to current_key."); - } - current->status.store( SUCCEEDED, std::memory_order_release); - break; - case res_port: - // use current_key from FE for item - this->delete_with_key(my_join->current_key); - current->status.store( SUCCEEDED, std::memory_order_release); - break; - } - } - } -// ------------ End Aggregator --------------- - protected: - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - graph_task* try_put_task(const input_type& v) override { - key_matching_port_operation op_data(v, try__put); - graph_task* rtask = nullptr; - my_aggregator.execute(&op_data); - if(op_data.status == SUCCEEDED) { - rtask = my_join->increment_key_count((*(this->get_key_func()))(v)); // may spawn - // rtask has to reflect the return status of the try_put - if(!rtask) rtask = SUCCESSFULLY_ENQUEUED; - } - return rtask; - } - - graph& graph_reference() const override { - return my_join->graph_ref; - } - - public: - - key_matching_port() : receiver(), buffer_type() { - my_join = nullptr; - my_aggregator.initialize_handler(handler_type(this)); - } - - // copy constructor - key_matching_port(const key_matching_port& /*other*/) = delete; -#if __INTEL_COMPILER <= 2021 - // Suppress superfluous diagnostic about virtual keyword absence in a destructor of an inherited - // class while the parent class has the virtual keyword for the destrocutor. - virtual -#endif - ~key_matching_port() { } - - void set_join_node_pointer(forwarding_base *join) { - my_join = dynamic_cast*>(join); - } - - void set_my_key_func(type_to_key_func_type *f) { this->set_key_func(f); } - - type_to_key_func_type* get_my_key_func() { return this->get_key_func(); } - - bool get_item( input_type &v ) { - // aggregator uses current_key from FE for Key - key_matching_port_operation op_data(&v, get__item); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - // reset_port is called when item is accepted by successor, but - // is initiated by join_node. - void reset_port() { - key_matching_port_operation op_data(res_port); - my_aggregator.execute(&op_data); - return; - } - - void reset_receiver(reset_flags ) { - buffer_type::reset(); - } - - private: - // my_join forwarding base used to count number of inputs that - // received key. - matching_forwarding_base *my_join; - }; // key_matching_port - - using namespace graph_policy_namespace; - - template - class join_node_base; - - //! join_node_FE : implements input port policy - template - class join_node_FE; - - template - class join_node_FE : public reserving_forwarding_base { - private: - static const int N = std::tuple_size::value; - typedef OutputTuple output_type; - typedef InputTuple input_type; - typedef join_node_base base_node_type; // for forwarding - public: - join_node_FE(graph &g) : reserving_forwarding_base(g), my_node(nullptr) { - ports_with_no_inputs = N; - join_helper::set_join_node_pointer(my_inputs, this); - } - - join_node_FE(const join_node_FE& other) : reserving_forwarding_base((other.reserving_forwarding_base::graph_ref)), my_node(nullptr) { - ports_with_no_inputs = N; - join_helper::set_join_node_pointer(my_inputs, this); - } - - void set_my_node(base_node_type *new_my_node) { my_node = new_my_node; } - - void increment_port_count() override { - ++ports_with_no_inputs; - } - - // if all input_ports have predecessors, spawn forward to try and consume tuples - graph_task* decrement_port_count() override { - if(ports_with_no_inputs.fetch_sub(1) == 1) { - if(is_graph_active(this->graph_ref)) { - small_object_allocator allocator{}; - typedef forward_task_bypass task_type; - graph_task* t = allocator.new_object(graph_ref, allocator, *my_node); - graph_ref.reserve_wait(); - spawn_in_graph_arena(this->graph_ref, *t); - } - } - return nullptr; - } - - input_type &input_ports() { return my_inputs; } - - protected: - - void reset( reset_flags f) { - // called outside of parallel contexts - ports_with_no_inputs = N; - join_helper::reset_inputs(my_inputs, f); - } - - // all methods on input ports should be called under mutual exclusion from join_node_base. - - bool tuple_build_may_succeed() { - return !ports_with_no_inputs; - } - - bool try_to_make_tuple(output_type &out) { - if(ports_with_no_inputs) return false; - return join_helper::reserve(my_inputs, out); - } - - void tuple_accepted() { - join_helper::consume_reservations(my_inputs); - } - void tuple_rejected() { - join_helper::release_reservations(my_inputs); - } - - input_type my_inputs; - base_node_type *my_node; - std::atomic ports_with_no_inputs; - }; // join_node_FE - - template - class join_node_FE : public queueing_forwarding_base { - public: - static const int N = std::tuple_size::value; - typedef OutputTuple output_type; - typedef InputTuple input_type; - typedef join_node_base base_node_type; // for forwarding - - join_node_FE(graph &g) : queueing_forwarding_base(g), my_node(nullptr) { - ports_with_no_items = N; - join_helper::set_join_node_pointer(my_inputs, this); - } - - join_node_FE(const join_node_FE& other) : queueing_forwarding_base((other.queueing_forwarding_base::graph_ref)), my_node(nullptr) { - ports_with_no_items = N; - join_helper::set_join_node_pointer(my_inputs, this); - } - - // needed for forwarding - void set_my_node(base_node_type *new_my_node) { my_node = new_my_node; } - - void reset_port_count() { - ports_with_no_items = N; - } - - // if all input_ports have items, spawn forward to try and consume tuples - graph_task* decrement_port_count(bool handle_task) override - { - if(ports_with_no_items.fetch_sub(1) == 1) { - if(is_graph_active(this->graph_ref)) { - small_object_allocator allocator{}; - typedef forward_task_bypass task_type; - graph_task* t = allocator.new_object(graph_ref, allocator, *my_node); - graph_ref.reserve_wait(); - if( !handle_task ) - return t; - spawn_in_graph_arena(this->graph_ref, *t); - } - } - return nullptr; - } - - input_type &input_ports() { return my_inputs; } - - protected: - - void reset( reset_flags f) { - reset_port_count(); - join_helper::reset_inputs(my_inputs, f ); - } - - // all methods on input ports should be called under mutual exclusion from join_node_base. - - bool tuple_build_may_succeed() { - return !ports_with_no_items; - } - - bool try_to_make_tuple(output_type &out) { - if(ports_with_no_items) return false; - return join_helper::get_items(my_inputs, out); - } - - void tuple_accepted() { - reset_port_count(); - join_helper::reset_ports(my_inputs); - } - void tuple_rejected() { - // nothing to do. - } - - input_type my_inputs; - base_node_type *my_node; - std::atomic ports_with_no_items; - }; // join_node_FE - - // key_matching join front-end. - template - class join_node_FE, InputTuple, OutputTuple> : public matching_forwarding_base, - // buffer of key value counts - public hash_buffer< // typedefed below to key_to_count_buffer_type - typename std::decay::type&, // force ref type on K - count_element::type>, - type_to_key_function_body< - count_element::type>, - typename std::decay::type& >, - KHash >, - // buffer of output items - public item_buffer { - public: - static const int N = std::tuple_size::value; - typedef OutputTuple output_type; - typedef InputTuple input_type; - typedef K key_type; - typedef typename std::decay::type unref_key_type; - typedef KHash key_hash_compare; - // must use K without ref. - typedef count_element count_element_type; - // method that lets us refer to the key of this type. - typedef key_to_count_functor key_to_count_func; - typedef type_to_key_function_body< count_element_type, unref_key_type&> TtoK_function_body_type; - typedef type_to_key_function_body_leaf TtoK_function_body_leaf_type; - // this is the type of the special table that keeps track of the number of discrete - // elements corresponding to each key that we've seen. - typedef hash_buffer< unref_key_type&, count_element_type, TtoK_function_body_type, key_hash_compare > - key_to_count_buffer_type; - typedef item_buffer output_buffer_type; - typedef join_node_base, InputTuple, OutputTuple> base_node_type; // for forwarding - typedef matching_forwarding_base forwarding_base_type; - -// ----------- Aggregator ------------ - // the aggregator is only needed to serialize the access to the hash table. - // and the output_buffer_type base class - private: - enum op_type { res_count, inc_count, may_succeed, try_make }; - typedef join_node_FE, InputTuple, OutputTuple> class_type; - - class key_matching_FE_operation : public aggregated_operation { - public: - char type; - unref_key_type my_val; - output_type* my_output; - graph_task* bypass_t; - // constructor for value parameter - key_matching_FE_operation(const unref_key_type& e , op_type t) : type(char(t)), my_val(e), - my_output(nullptr), bypass_t(nullptr) {} - key_matching_FE_operation(output_type *p, op_type t) : type(char(t)), my_output(p), bypass_t(nullptr) {} - // constructor with no parameter - key_matching_FE_operation(op_type t) : type(char(t)), my_output(nullptr), bypass_t(nullptr) {} - }; - - typedef aggregating_functor handler_type; - friend class aggregating_functor; - aggregator my_aggregator; - - // called from aggregator, so serialized - // returns a task pointer if the a task would have been enqueued but we asked that - // it be returned. Otherwise returns nullptr. - graph_task* fill_output_buffer(unref_key_type &t) { - output_type l_out; - graph_task* rtask = nullptr; - bool do_fwd = this->buffer_empty() && is_graph_active(this->graph_ref); - this->current_key = t; - this->delete_with_key(this->current_key); // remove the key - if(join_helper::get_items(my_inputs, l_out)) { // <== call back - this->push_back(l_out); - if(do_fwd) { // we enqueue if receiving an item from predecessor, not if successor asks for item - small_object_allocator allocator{}; - typedef forward_task_bypass task_type; - rtask = allocator.new_object(this->graph_ref, allocator, *my_node); - this->graph_ref.reserve_wait(); - do_fwd = false; - } - // retire the input values - join_helper::reset_ports(my_inputs); // <== call back - } - else { - __TBB_ASSERT(false, "should have had something to push"); - } - return rtask; - } - - void handle_operations(key_matching_FE_operation* op_list) { - key_matching_FE_operation *current; - while(op_list) { - current = op_list; - op_list = op_list->next; - switch(current->type) { - case res_count: // called from BE - { - this->destroy_front(); - current->status.store( SUCCEEDED, std::memory_order_release); - } - break; - case inc_count: { // called from input ports - count_element_type *p = nullptr; - unref_key_type &t = current->my_val; - if(!(this->find_ref_with_key(t,p))) { - count_element_type ev; - ev.my_key = t; - ev.my_value = 0; - this->insert_with_key(ev); - bool found = this->find_ref_with_key(t, p); - __TBB_ASSERT_EX(found, "should find key after inserting it"); - } - if(++(p->my_value) == size_t(N)) { - current->bypass_t = fill_output_buffer(t); - } - } - current->status.store( SUCCEEDED, std::memory_order_release); - break; - case may_succeed: // called from BE - current->status.store( this->buffer_empty() ? FAILED : SUCCEEDED, std::memory_order_release); - break; - case try_make: // called from BE - if(this->buffer_empty()) { - current->status.store( FAILED, std::memory_order_release); - } - else { - *(current->my_output) = this->front(); - current->status.store( SUCCEEDED, std::memory_order_release); - } - break; - } - } - } -// ------------ End Aggregator --------------- - - public: - template - join_node_FE(graph &g, FunctionTuple &TtoK_funcs) : forwarding_base_type(g), my_node(nullptr) { - join_helper::set_join_node_pointer(my_inputs, this); - join_helper::set_key_functors(my_inputs, TtoK_funcs); - my_aggregator.initialize_handler(handler_type(this)); - TtoK_function_body_type *cfb = new TtoK_function_body_leaf_type(key_to_count_func()); - this->set_key_func(cfb); - } - - join_node_FE(const join_node_FE& other) : forwarding_base_type((other.forwarding_base_type::graph_ref)), key_to_count_buffer_type(), - output_buffer_type() { - my_node = nullptr; - join_helper::set_join_node_pointer(my_inputs, this); - join_helper::copy_key_functors(my_inputs, const_cast(other.my_inputs)); - my_aggregator.initialize_handler(handler_type(this)); - TtoK_function_body_type *cfb = new TtoK_function_body_leaf_type(key_to_count_func()); - this->set_key_func(cfb); - } - - // needed for forwarding - void set_my_node(base_node_type *new_my_node) { my_node = new_my_node; } - - void reset_port_count() { // called from BE - key_matching_FE_operation op_data(res_count); - my_aggregator.execute(&op_data); - return; - } - - // if all input_ports have items, spawn forward to try and consume tuples - // return a task if we are asked and did create one. - graph_task *increment_key_count(unref_key_type const & t) override { // called from input_ports - key_matching_FE_operation op_data(t, inc_count); - my_aggregator.execute(&op_data); - return op_data.bypass_t; - } - - input_type &input_ports() { return my_inputs; } - - protected: - - void reset( reset_flags f ) { - // called outside of parallel contexts - join_helper::reset_inputs(my_inputs, f); - - key_to_count_buffer_type::reset(); - output_buffer_type::reset(); - } - - // all methods on input ports should be called under mutual exclusion from join_node_base. - - bool tuple_build_may_succeed() { // called from back-end - key_matching_FE_operation op_data(may_succeed); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - // cannot lock while calling back to input_ports. current_key will only be set - // and reset under the aggregator, so it will remain consistent. - bool try_to_make_tuple(output_type &out) { - key_matching_FE_operation op_data(&out,try_make); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - void tuple_accepted() { - reset_port_count(); // reset current_key after ports reset. - } - - void tuple_rejected() { - // nothing to do. - } - - input_type my_inputs; // input ports - base_node_type *my_node; - }; // join_node_FE, InputTuple, OutputTuple> - - //! join_node_base - template - class join_node_base : public graph_node, public join_node_FE, - public sender { - protected: - using graph_node::my_graph; - public: - typedef OutputTuple output_type; - - typedef typename sender::successor_type successor_type; - typedef join_node_FE input_ports_type; - using input_ports_type::tuple_build_may_succeed; - using input_ports_type::try_to_make_tuple; - using input_ports_type::tuple_accepted; - using input_ports_type::tuple_rejected; - - private: - // ----------- Aggregator ------------ - enum op_type { reg_succ, rem_succ, try__get, do_fwrd, do_fwrd_bypass - }; - typedef join_node_base class_type; - - class join_node_base_operation : public aggregated_operation { - public: - char type; - union { - output_type *my_arg; - successor_type *my_succ; - }; - graph_task* bypass_t; - join_node_base_operation(const output_type& e, op_type t) : type(char(t)), - my_arg(const_cast(&e)), bypass_t(nullptr) {} - join_node_base_operation(const successor_type &s, op_type t) : type(char(t)), - my_succ(const_cast(&s)), bypass_t(nullptr) {} - join_node_base_operation(op_type t) : type(char(t)), bypass_t(nullptr) {} - }; - - typedef aggregating_functor handler_type; - friend class aggregating_functor; - bool forwarder_busy; - aggregator my_aggregator; - - void handle_operations(join_node_base_operation* op_list) { - join_node_base_operation *current; - while(op_list) { - current = op_list; - op_list = op_list->next; - switch(current->type) { - case reg_succ: { - my_successors.register_successor(*(current->my_succ)); - if(tuple_build_may_succeed() && !forwarder_busy && is_graph_active(my_graph)) { - small_object_allocator allocator{}; - typedef forward_task_bypass< join_node_base > task_type; - graph_task* t = allocator.new_object(my_graph, allocator, *this); - my_graph.reserve_wait(); - spawn_in_graph_arena(my_graph, *t); - forwarder_busy = true; - } - current->status.store( SUCCEEDED, std::memory_order_release); - } - break; - case rem_succ: - my_successors.remove_successor(*(current->my_succ)); - current->status.store( SUCCEEDED, std::memory_order_release); - break; - case try__get: - if(tuple_build_may_succeed()) { - if(try_to_make_tuple(*(current->my_arg))) { - tuple_accepted(); - current->status.store( SUCCEEDED, std::memory_order_release); - } - else current->status.store( FAILED, std::memory_order_release); - } - else current->status.store( FAILED, std::memory_order_release); - break; - case do_fwrd_bypass: { - bool build_succeeded; - graph_task *last_task = nullptr; - output_type out; - // forwarding must be exclusive, because try_to_make_tuple and tuple_accepted - // are separate locked methods in the FE. We could conceivably fetch the front - // of the FE queue, then be swapped out, have someone else consume the FE's - // object, then come back, forward, and then try to remove it from the queue - // again. Without reservation of the FE, the methods accessing it must be locked. - // We could remember the keys of the objects we forwarded, and then remove - // them from the input ports after forwarding is complete? - if(tuple_build_may_succeed()) { // checks output queue of FE - do { - build_succeeded = try_to_make_tuple(out); // fetch front_end of queue - if(build_succeeded) { - graph_task *new_task = my_successors.try_put_task(out); - last_task = combine_tasks(my_graph, last_task, new_task); - if(new_task) { - tuple_accepted(); - } - else { - tuple_rejected(); - build_succeeded = false; - } - } - } while(build_succeeded); - } - current->bypass_t = last_task; - current->status.store( SUCCEEDED, std::memory_order_release); - forwarder_busy = false; - } - break; - } - } - } - // ---------- end aggregator ----------- - public: - join_node_base(graph &g) - : graph_node(g), input_ports_type(g), forwarder_busy(false), my_successors(this) - { - input_ports_type::set_my_node(this); - my_aggregator.initialize_handler(handler_type(this)); - } - - join_node_base(const join_node_base& other) : - graph_node(other.graph_node::my_graph), input_ports_type(other), - sender(), forwarder_busy(false), my_successors(this) - { - input_ports_type::set_my_node(this); - my_aggregator.initialize_handler(handler_type(this)); - } - - template - join_node_base(graph &g, FunctionTuple f) - : graph_node(g), input_ports_type(g, f), forwarder_busy(false), my_successors(this) - { - input_ports_type::set_my_node(this); - my_aggregator.initialize_handler(handler_type(this)); - } - - bool register_successor(successor_type &r) override { - join_node_base_operation op_data(r, reg_succ); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - bool remove_successor( successor_type &r) override { - join_node_base_operation op_data(r, rem_succ); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - bool try_get( output_type &v) override { - join_node_base_operation op_data(v, try__get); - my_aggregator.execute(&op_data); - return op_data.status == SUCCEEDED; - } - - protected: - void reset_node(reset_flags f) override { - input_ports_type::reset(f); - if(f & rf_clear_edges) my_successors.clear(); - } - - private: - broadcast_cache my_successors; - - friend class forward_task_bypass< join_node_base >; - graph_task *forward_task() { - join_node_base_operation op_data(do_fwrd_bypass); - my_aggregator.execute(&op_data); - return op_data.bypass_t; - } - - }; // join_node_base - - // join base class type generator - template class PT, typename OutputTuple, typename JP> - struct join_base { - typedef join_node_base::type, OutputTuple> type; - }; - - template - struct join_base > { - typedef key_matching key_traits_type; - typedef K key_type; - typedef KHash key_hash_compare; - typedef join_node_base< key_traits_type, - // ports type - typename wrap_key_tuple_elements::type, - OutputTuple > type; - }; - - //! unfolded_join_node : passes input_ports_type to join_node_base. We build the input port type - // using tuple_element. The class PT is the port type (reserving_port, queueing_port, key_matching_port) - // and should match the typename. - - template class PT, typename OutputTuple, typename JP> - class unfolded_join_node : public join_base::type { - public: - typedef typename wrap_tuple_elements::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base base_type; - public: - unfolded_join_node(graph &g) : base_type(g) {} - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; - -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - template - struct key_from_message_body { - K operator()(const T& t) const { - return key_from_message(t); - } - }; - // Adds const to reference type - template - struct key_from_message_body { - const K& operator()(const T& t) const { - return key_from_message(t); - } - }; -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - // key_matching unfolded_join_node. This must be a separate specialization because the constructors - // differ. - - template - class unfolded_join_node<2,key_matching_port,OutputTuple,key_matching > : public - join_base<2,key_matching_port,OutputTuple,key_matching >::type { - typedef typename std::tuple_element<0, OutputTuple>::type T0; - typedef typename std::tuple_element<1, OutputTuple>::type T1; - public: - typedef typename wrap_key_tuple_elements<2,key_matching_port,key_matching,OutputTuple>::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base, input_ports_type, output_type > base_type; - typedef type_to_key_function_body *f0_p; - typedef type_to_key_function_body *f1_p; - typedef std::tuple< f0_p, f1_p > func_initializer_type; - public: -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - unfolded_join_node(graph &g) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()) - ) ) { - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - template - unfolded_join_node(graph &g, Body0 body0, Body1 body1) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf(body0), - new type_to_key_function_body_leaf(body1) - ) ) { - static_assert(std::tuple_size::value == 2, "wrong number of body initializers"); - } - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; - - template - class unfolded_join_node<3,key_matching_port,OutputTuple,key_matching > : public - join_base<3,key_matching_port,OutputTuple,key_matching >::type { - typedef typename std::tuple_element<0, OutputTuple>::type T0; - typedef typename std::tuple_element<1, OutputTuple>::type T1; - typedef typename std::tuple_element<2, OutputTuple>::type T2; - public: - typedef typename wrap_key_tuple_elements<3,key_matching_port,key_matching,OutputTuple>::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base, input_ports_type, output_type > base_type; - typedef type_to_key_function_body *f0_p; - typedef type_to_key_function_body *f1_p; - typedef type_to_key_function_body *f2_p; - typedef std::tuple< f0_p, f1_p, f2_p > func_initializer_type; - public: -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - unfolded_join_node(graph &g) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()) - ) ) { - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - template - unfolded_join_node(graph &g, Body0 body0, Body1 body1, Body2 body2) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf(body0), - new type_to_key_function_body_leaf(body1), - new type_to_key_function_body_leaf(body2) - ) ) { - static_assert(std::tuple_size::value == 3, "wrong number of body initializers"); - } - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; - - template - class unfolded_join_node<4,key_matching_port,OutputTuple,key_matching > : public - join_base<4,key_matching_port,OutputTuple,key_matching >::type { - typedef typename std::tuple_element<0, OutputTuple>::type T0; - typedef typename std::tuple_element<1, OutputTuple>::type T1; - typedef typename std::tuple_element<2, OutputTuple>::type T2; - typedef typename std::tuple_element<3, OutputTuple>::type T3; - public: - typedef typename wrap_key_tuple_elements<4,key_matching_port,key_matching,OutputTuple>::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base, input_ports_type, output_type > base_type; - typedef type_to_key_function_body *f0_p; - typedef type_to_key_function_body *f1_p; - typedef type_to_key_function_body *f2_p; - typedef type_to_key_function_body *f3_p; - typedef std::tuple< f0_p, f1_p, f2_p, f3_p > func_initializer_type; - public: -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - unfolded_join_node(graph &g) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()) - ) ) { - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - template - unfolded_join_node(graph &g, Body0 body0, Body1 body1, Body2 body2, Body3 body3) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf(body0), - new type_to_key_function_body_leaf(body1), - new type_to_key_function_body_leaf(body2), - new type_to_key_function_body_leaf(body3) - ) ) { - static_assert(std::tuple_size::value == 4, "wrong number of body initializers"); - } - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; - - template - class unfolded_join_node<5,key_matching_port,OutputTuple,key_matching > : public - join_base<5,key_matching_port,OutputTuple,key_matching >::type { - typedef typename std::tuple_element<0, OutputTuple>::type T0; - typedef typename std::tuple_element<1, OutputTuple>::type T1; - typedef typename std::tuple_element<2, OutputTuple>::type T2; - typedef typename std::tuple_element<3, OutputTuple>::type T3; - typedef typename std::tuple_element<4, OutputTuple>::type T4; - public: - typedef typename wrap_key_tuple_elements<5,key_matching_port,key_matching,OutputTuple>::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base , input_ports_type, output_type > base_type; - typedef type_to_key_function_body *f0_p; - typedef type_to_key_function_body *f1_p; - typedef type_to_key_function_body *f2_p; - typedef type_to_key_function_body *f3_p; - typedef type_to_key_function_body *f4_p; - typedef std::tuple< f0_p, f1_p, f2_p, f3_p, f4_p > func_initializer_type; - public: -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - unfolded_join_node(graph &g) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()) - ) ) { - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - template - unfolded_join_node(graph &g, Body0 body0, Body1 body1, Body2 body2, Body3 body3, Body4 body4) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf(body0), - new type_to_key_function_body_leaf(body1), - new type_to_key_function_body_leaf(body2), - new type_to_key_function_body_leaf(body3), - new type_to_key_function_body_leaf(body4) - ) ) { - static_assert(std::tuple_size::value == 5, "wrong number of body initializers"); - } - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; - -#if __TBB_VARIADIC_MAX >= 6 - template - class unfolded_join_node<6,key_matching_port,OutputTuple,key_matching > : public - join_base<6,key_matching_port,OutputTuple,key_matching >::type { - typedef typename std::tuple_element<0, OutputTuple>::type T0; - typedef typename std::tuple_element<1, OutputTuple>::type T1; - typedef typename std::tuple_element<2, OutputTuple>::type T2; - typedef typename std::tuple_element<3, OutputTuple>::type T3; - typedef typename std::tuple_element<4, OutputTuple>::type T4; - typedef typename std::tuple_element<5, OutputTuple>::type T5; - public: - typedef typename wrap_key_tuple_elements<6,key_matching_port,key_matching,OutputTuple>::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base , input_ports_type, output_type > base_type; - typedef type_to_key_function_body *f0_p; - typedef type_to_key_function_body *f1_p; - typedef type_to_key_function_body *f2_p; - typedef type_to_key_function_body *f3_p; - typedef type_to_key_function_body *f4_p; - typedef type_to_key_function_body *f5_p; - typedef std::tuple< f0_p, f1_p, f2_p, f3_p, f4_p, f5_p > func_initializer_type; - public: -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - unfolded_join_node(graph &g) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()) - ) ) { - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - template - unfolded_join_node(graph &g, Body0 body0, Body1 body1, Body2 body2, Body3 body3, Body4 body4, Body5 body5) - : base_type(g, func_initializer_type( - new type_to_key_function_body_leaf(body0), - new type_to_key_function_body_leaf(body1), - new type_to_key_function_body_leaf(body2), - new type_to_key_function_body_leaf(body3), - new type_to_key_function_body_leaf(body4), - new type_to_key_function_body_leaf(body5) - ) ) { - static_assert(std::tuple_size::value == 6, "wrong number of body initializers"); - } - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; -#endif - -#if __TBB_VARIADIC_MAX >= 7 - template - class unfolded_join_node<7,key_matching_port,OutputTuple,key_matching > : public - join_base<7,key_matching_port,OutputTuple,key_matching >::type { - typedef typename std::tuple_element<0, OutputTuple>::type T0; - typedef typename std::tuple_element<1, OutputTuple>::type T1; - typedef typename std::tuple_element<2, OutputTuple>::type T2; - typedef typename std::tuple_element<3, OutputTuple>::type T3; - typedef typename std::tuple_element<4, OutputTuple>::type T4; - typedef typename std::tuple_element<5, OutputTuple>::type T5; - typedef typename std::tuple_element<6, OutputTuple>::type T6; - public: - typedef typename wrap_key_tuple_elements<7,key_matching_port,key_matching,OutputTuple>::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base , input_ports_type, output_type > base_type; - typedef type_to_key_function_body *f0_p; - typedef type_to_key_function_body *f1_p; - typedef type_to_key_function_body *f2_p; - typedef type_to_key_function_body *f3_p; - typedef type_to_key_function_body *f4_p; - typedef type_to_key_function_body *f5_p; - typedef type_to_key_function_body *f6_p; - typedef std::tuple< f0_p, f1_p, f2_p, f3_p, f4_p, f5_p, f6_p > func_initializer_type; - public: -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - unfolded_join_node(graph &g) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()) - ) ) { - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - template - unfolded_join_node(graph &g, Body0 body0, Body1 body1, Body2 body2, Body3 body3, Body4 body4, - Body5 body5, Body6 body6) : base_type(g, func_initializer_type( - new type_to_key_function_body_leaf(body0), - new type_to_key_function_body_leaf(body1), - new type_to_key_function_body_leaf(body2), - new type_to_key_function_body_leaf(body3), - new type_to_key_function_body_leaf(body4), - new type_to_key_function_body_leaf(body5), - new type_to_key_function_body_leaf(body6) - ) ) { - static_assert(std::tuple_size::value == 7, "wrong number of body initializers"); - } - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; -#endif - -#if __TBB_VARIADIC_MAX >= 8 - template - class unfolded_join_node<8,key_matching_port,OutputTuple,key_matching > : public - join_base<8,key_matching_port,OutputTuple,key_matching >::type { - typedef typename std::tuple_element<0, OutputTuple>::type T0; - typedef typename std::tuple_element<1, OutputTuple>::type T1; - typedef typename std::tuple_element<2, OutputTuple>::type T2; - typedef typename std::tuple_element<3, OutputTuple>::type T3; - typedef typename std::tuple_element<4, OutputTuple>::type T4; - typedef typename std::tuple_element<5, OutputTuple>::type T5; - typedef typename std::tuple_element<6, OutputTuple>::type T6; - typedef typename std::tuple_element<7, OutputTuple>::type T7; - public: - typedef typename wrap_key_tuple_elements<8,key_matching_port,key_matching,OutputTuple>::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base , input_ports_type, output_type > base_type; - typedef type_to_key_function_body *f0_p; - typedef type_to_key_function_body *f1_p; - typedef type_to_key_function_body *f2_p; - typedef type_to_key_function_body *f3_p; - typedef type_to_key_function_body *f4_p; - typedef type_to_key_function_body *f5_p; - typedef type_to_key_function_body *f6_p; - typedef type_to_key_function_body *f7_p; - typedef std::tuple< f0_p, f1_p, f2_p, f3_p, f4_p, f5_p, f6_p, f7_p > func_initializer_type; - public: -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - unfolded_join_node(graph &g) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()) - ) ) { - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - template - unfolded_join_node(graph &g, Body0 body0, Body1 body1, Body2 body2, Body3 body3, Body4 body4, - Body5 body5, Body6 body6, Body7 body7) : base_type(g, func_initializer_type( - new type_to_key_function_body_leaf(body0), - new type_to_key_function_body_leaf(body1), - new type_to_key_function_body_leaf(body2), - new type_to_key_function_body_leaf(body3), - new type_to_key_function_body_leaf(body4), - new type_to_key_function_body_leaf(body5), - new type_to_key_function_body_leaf(body6), - new type_to_key_function_body_leaf(body7) - ) ) { - static_assert(std::tuple_size::value == 8, "wrong number of body initializers"); - } - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; -#endif - -#if __TBB_VARIADIC_MAX >= 9 - template - class unfolded_join_node<9,key_matching_port,OutputTuple,key_matching > : public - join_base<9,key_matching_port,OutputTuple,key_matching >::type { - typedef typename std::tuple_element<0, OutputTuple>::type T0; - typedef typename std::tuple_element<1, OutputTuple>::type T1; - typedef typename std::tuple_element<2, OutputTuple>::type T2; - typedef typename std::tuple_element<3, OutputTuple>::type T3; - typedef typename std::tuple_element<4, OutputTuple>::type T4; - typedef typename std::tuple_element<5, OutputTuple>::type T5; - typedef typename std::tuple_element<6, OutputTuple>::type T6; - typedef typename std::tuple_element<7, OutputTuple>::type T7; - typedef typename std::tuple_element<8, OutputTuple>::type T8; - public: - typedef typename wrap_key_tuple_elements<9,key_matching_port,key_matching,OutputTuple>::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base , input_ports_type, output_type > base_type; - typedef type_to_key_function_body *f0_p; - typedef type_to_key_function_body *f1_p; - typedef type_to_key_function_body *f2_p; - typedef type_to_key_function_body *f3_p; - typedef type_to_key_function_body *f4_p; - typedef type_to_key_function_body *f5_p; - typedef type_to_key_function_body *f6_p; - typedef type_to_key_function_body *f7_p; - typedef type_to_key_function_body *f8_p; - typedef std::tuple< f0_p, f1_p, f2_p, f3_p, f4_p, f5_p, f6_p, f7_p, f8_p > func_initializer_type; - public: -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - unfolded_join_node(graph &g) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()) - ) ) { - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - template - unfolded_join_node(graph &g, Body0 body0, Body1 body1, Body2 body2, Body3 body3, Body4 body4, - Body5 body5, Body6 body6, Body7 body7, Body8 body8) : base_type(g, func_initializer_type( - new type_to_key_function_body_leaf(body0), - new type_to_key_function_body_leaf(body1), - new type_to_key_function_body_leaf(body2), - new type_to_key_function_body_leaf(body3), - new type_to_key_function_body_leaf(body4), - new type_to_key_function_body_leaf(body5), - new type_to_key_function_body_leaf(body6), - new type_to_key_function_body_leaf(body7), - new type_to_key_function_body_leaf(body8) - ) ) { - static_assert(std::tuple_size::value == 9, "wrong number of body initializers"); - } - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; -#endif - -#if __TBB_VARIADIC_MAX >= 10 - template - class unfolded_join_node<10,key_matching_port,OutputTuple,key_matching > : public - join_base<10,key_matching_port,OutputTuple,key_matching >::type { - typedef typename std::tuple_element<0, OutputTuple>::type T0; - typedef typename std::tuple_element<1, OutputTuple>::type T1; - typedef typename std::tuple_element<2, OutputTuple>::type T2; - typedef typename std::tuple_element<3, OutputTuple>::type T3; - typedef typename std::tuple_element<4, OutputTuple>::type T4; - typedef typename std::tuple_element<5, OutputTuple>::type T5; - typedef typename std::tuple_element<6, OutputTuple>::type T6; - typedef typename std::tuple_element<7, OutputTuple>::type T7; - typedef typename std::tuple_element<8, OutputTuple>::type T8; - typedef typename std::tuple_element<9, OutputTuple>::type T9; - public: - typedef typename wrap_key_tuple_elements<10,key_matching_port,key_matching,OutputTuple>::type input_ports_type; - typedef OutputTuple output_type; - private: - typedef join_node_base , input_ports_type, output_type > base_type; - typedef type_to_key_function_body *f0_p; - typedef type_to_key_function_body *f1_p; - typedef type_to_key_function_body *f2_p; - typedef type_to_key_function_body *f3_p; - typedef type_to_key_function_body *f4_p; - typedef type_to_key_function_body *f5_p; - typedef type_to_key_function_body *f6_p; - typedef type_to_key_function_body *f7_p; - typedef type_to_key_function_body *f8_p; - typedef type_to_key_function_body *f9_p; - typedef std::tuple< f0_p, f1_p, f2_p, f3_p, f4_p, f5_p, f6_p, f7_p, f8_p, f9_p > func_initializer_type; - public: -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - unfolded_join_node(graph &g) : base_type(g, - func_initializer_type( - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()), - new type_to_key_function_body_leaf >(key_from_message_body()) - ) ) { - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - template - unfolded_join_node(graph &g, Body0 body0, Body1 body1, Body2 body2, Body3 body3, Body4 body4, - Body5 body5, Body6 body6, Body7 body7, Body8 body8, Body9 body9) : base_type(g, func_initializer_type( - new type_to_key_function_body_leaf(body0), - new type_to_key_function_body_leaf(body1), - new type_to_key_function_body_leaf(body2), - new type_to_key_function_body_leaf(body3), - new type_to_key_function_body_leaf(body4), - new type_to_key_function_body_leaf(body5), - new type_to_key_function_body_leaf(body6), - new type_to_key_function_body_leaf(body7), - new type_to_key_function_body_leaf(body8), - new type_to_key_function_body_leaf(body9) - ) ) { - static_assert(std::tuple_size::value == 10, "wrong number of body initializers"); - } - unfolded_join_node(const unfolded_join_node &other) : base_type(other) {} - }; -#endif - - //! templated function to refer to input ports of the join node - template - typename std::tuple_element::type &input_port(JNT &jn) { - return std::get(jn.input_ports()); - } - -#endif // __TBB__flow_graph_join_impl_H diff --git a/include/oneapi/tbb/detail/_flow_graph_node_impl.h b/include/oneapi/tbb/detail/_flow_graph_node_impl.h deleted file mode 100644 index b79c53d..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_node_impl.h +++ /dev/null @@ -1,774 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__flow_graph_node_impl_H -#define __TBB__flow_graph_node_impl_H - -#ifndef __TBB_flow_graph_H -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -#include "_flow_graph_item_buffer_impl.h" - -template< typename T, typename A > -class function_input_queue : public item_buffer { -public: - bool empty() const { - return this->buffer_empty(); - } - - const T& front() const { - return this->item_buffer::front(); - } - - void pop() { - this->destroy_front(); - } - - bool push( T& t ) { - return this->push_back( t ); - } -}; - -//! Input and scheduling for a function node that takes a type Input as input -// The only up-ref is apply_body_impl, which should implement the function -// call and any handling of the result. -template< typename Input, typename Policy, typename A, typename ImplType > -class function_input_base : public receiver, no_assign { - enum op_type {reg_pred, rem_pred, try_fwd, tryput_bypass, app_body_bypass, occupy_concurrency - }; - typedef function_input_base class_type; - -public: - - //! The input type of this receiver - typedef Input input_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef predecessor_cache predecessor_cache_type; - typedef function_input_queue input_queue_type; - typedef typename allocator_traits::template rebind_alloc allocator_type; - static_assert(!has_policy::value || !has_policy::value, ""); - - //! Constructor for function_input_base - function_input_base( graph &g, size_t max_concurrency, node_priority_t a_priority, bool is_no_throw ) - : my_graph_ref(g), my_max_concurrency(max_concurrency) - , my_concurrency(0), my_priority(a_priority), my_is_no_throw(is_no_throw) - , my_queue(!has_policy::value ? new input_queue_type() : nullptr) - , my_predecessors(this) - , forwarder_busy(false) - { - my_aggregator.initialize_handler(handler_type(this)); - } - - //! Copy constructor - function_input_base( const function_input_base& src ) - : function_input_base(src.my_graph_ref, src.my_max_concurrency, src.my_priority, src.my_is_no_throw) {} - - //! Destructor - // The queue is allocated by the constructor for {multi}function_node. - // TODO: pass the graph_buffer_policy to the base so it can allocate the queue instead. - // This would be an interface-breaking change. - virtual ~function_input_base() { - delete my_queue; - my_queue = nullptr; - } - - graph_task* try_put_task( const input_type& t) override { - if ( my_is_no_throw ) - return try_put_task_impl(t, has_policy()); - else - return try_put_task_impl(t, std::false_type()); - } - - //! Adds src to the list of cached predecessors. - bool register_predecessor( predecessor_type &src ) override { - operation_type op_data(reg_pred); - op_data.r = &src; - my_aggregator.execute(&op_data); - return true; - } - - //! Removes src from the list of cached predecessors. - bool remove_predecessor( predecessor_type &src ) override { - operation_type op_data(rem_pred); - op_data.r = &src; - my_aggregator.execute(&op_data); - return true; - } - -protected: - - void reset_function_input_base( reset_flags f) { - my_concurrency = 0; - if(my_queue) { - my_queue->reset(); - } - reset_receiver(f); - forwarder_busy = false; - } - - graph& my_graph_ref; - const size_t my_max_concurrency; - size_t my_concurrency; - node_priority_t my_priority; - const bool my_is_no_throw; - input_queue_type *my_queue; - predecessor_cache my_predecessors; - - void reset_receiver( reset_flags f) { - if( f & rf_clear_edges) my_predecessors.clear(); - else - my_predecessors.reset(); - __TBB_ASSERT(!(f & rf_clear_edges) || my_predecessors.empty(), "function_input_base reset failed"); - } - - graph& graph_reference() const override { - return my_graph_ref; - } - - graph_task* try_get_postponed_task(const input_type& i) { - operation_type op_data(i, app_body_bypass); // tries to pop an item or get_item - my_aggregator.execute(&op_data); - return op_data.bypass_t; - } - -private: - - friend class apply_body_task_bypass< class_type, input_type >; - friend class forward_task_bypass< class_type >; - - class operation_type : public aggregated_operation< operation_type > { - public: - char type; - union { - input_type *elem; - predecessor_type *r; - }; - graph_task* bypass_t; - operation_type(const input_type& e, op_type t) : - type(char(t)), elem(const_cast(&e)), bypass_t(nullptr) {} - operation_type(op_type t) : type(char(t)), r(nullptr), bypass_t(nullptr) {} - }; - - bool forwarder_busy; - typedef aggregating_functor handler_type; - friend class aggregating_functor; - aggregator< handler_type, operation_type > my_aggregator; - - graph_task* perform_queued_requests() { - graph_task* new_task = nullptr; - if(my_queue) { - if(!my_queue->empty()) { - ++my_concurrency; - new_task = create_body_task(my_queue->front()); - - my_queue->pop(); - } - } - else { - input_type i; - if(my_predecessors.get_item(i)) { - ++my_concurrency; - new_task = create_body_task(i); - } - } - return new_task; - } - void handle_operations(operation_type *op_list) { - operation_type* tmp; - while (op_list) { - tmp = op_list; - op_list = op_list->next; - switch (tmp->type) { - case reg_pred: - my_predecessors.add(*(tmp->r)); - tmp->status.store(SUCCEEDED, std::memory_order_release); - if (!forwarder_busy) { - forwarder_busy = true; - spawn_forward_task(); - } - break; - case rem_pred: - my_predecessors.remove(*(tmp->r)); - tmp->status.store(SUCCEEDED, std::memory_order_release); - break; - case app_body_bypass: { - tmp->bypass_t = nullptr; - __TBB_ASSERT(my_max_concurrency != 0, nullptr); - --my_concurrency; - if(my_concurrencybypass_t = perform_queued_requests(); - tmp->status.store(SUCCEEDED, std::memory_order_release); - } - break; - case tryput_bypass: internal_try_put_task(tmp); break; - case try_fwd: internal_forward(tmp); break; - case occupy_concurrency: - if (my_concurrency < my_max_concurrency) { - ++my_concurrency; - tmp->status.store(SUCCEEDED, std::memory_order_release); - } else { - tmp->status.store(FAILED, std::memory_order_release); - } - break; - } - } - } - - //! Put to the node, but return the task instead of enqueueing it - void internal_try_put_task(operation_type *op) { - __TBB_ASSERT(my_max_concurrency != 0, nullptr); - if (my_concurrency < my_max_concurrency) { - ++my_concurrency; - graph_task * new_task = create_body_task(*(op->elem)); - op->bypass_t = new_task; - op->status.store(SUCCEEDED, std::memory_order_release); - } else if ( my_queue && my_queue->push(*(op->elem)) ) { - op->bypass_t = SUCCESSFULLY_ENQUEUED; - op->status.store(SUCCEEDED, std::memory_order_release); - } else { - op->bypass_t = nullptr; - op->status.store(FAILED, std::memory_order_release); - } - } - - //! Creates tasks for postponed messages if available and if concurrency allows - void internal_forward(operation_type *op) { - op->bypass_t = nullptr; - if (my_concurrency < my_max_concurrency) - op->bypass_t = perform_queued_requests(); - if(op->bypass_t) - op->status.store(SUCCEEDED, std::memory_order_release); - else { - forwarder_busy = false; - op->status.store(FAILED, std::memory_order_release); - } - } - - graph_task* internal_try_put_bypass( const input_type& t ) { - operation_type op_data(t, tryput_bypass); - my_aggregator.execute(&op_data); - if( op_data.status == SUCCEEDED ) { - return op_data.bypass_t; - } - return nullptr; - } - - graph_task* try_put_task_impl( const input_type& t, /*lightweight=*/std::true_type ) { - if( my_max_concurrency == 0 ) { - return apply_body_bypass(t); - } else { - operation_type check_op(t, occupy_concurrency); - my_aggregator.execute(&check_op); - if( check_op.status == SUCCEEDED ) { - return apply_body_bypass(t); - } - return internal_try_put_bypass(t); - } - } - - graph_task* try_put_task_impl( const input_type& t, /*lightweight=*/std::false_type ) { - if( my_max_concurrency == 0 ) { - return create_body_task(t); - } else { - return internal_try_put_bypass(t); - } - } - - //! Applies the body to the provided input - // then decides if more work is available - graph_task* apply_body_bypass( const input_type &i ) { - return static_cast(this)->apply_body_impl_bypass(i); - } - - //! allocates a task to apply a body - graph_task* create_body_task( const input_type &input ) { - if (!is_graph_active(my_graph_ref)) { - return nullptr; - } - // TODO revamp: extract helper for common graph task allocation part - small_object_allocator allocator{}; - typedef apply_body_task_bypass task_type; - graph_task* t = allocator.new_object( my_graph_ref, allocator, *this, input, my_priority ); - graph_reference().reserve_wait(); - return t; - } - - //! This is executed by an enqueued task, the "forwarder" - graph_task* forward_task() { - operation_type op_data(try_fwd); - graph_task* rval = nullptr; - do { - op_data.status = WAIT; - my_aggregator.execute(&op_data); - if(op_data.status == SUCCEEDED) { - graph_task* ttask = op_data.bypass_t; - __TBB_ASSERT( ttask && ttask != SUCCESSFULLY_ENQUEUED, nullptr); - rval = combine_tasks(my_graph_ref, rval, ttask); - } - } while (op_data.status == SUCCEEDED); - return rval; - } - - inline graph_task* create_forward_task() { - if (!is_graph_active(my_graph_ref)) { - return nullptr; - } - small_object_allocator allocator{}; - typedef forward_task_bypass task_type; - graph_task* t = allocator.new_object( graph_reference(), allocator, *this, my_priority ); - graph_reference().reserve_wait(); - return t; - } - - //! Spawns a task that calls forward() - inline void spawn_forward_task() { - graph_task* tp = create_forward_task(); - if(tp) { - spawn_in_graph_arena(graph_reference(), *tp); - } - } - - node_priority_t priority() const override { return my_priority; } -}; // function_input_base - -//! Implements methods for a function node that takes a type Input as input and sends -// a type Output to its successors. -template< typename Input, typename Output, typename Policy, typename A> -class function_input : public function_input_base > { -public: - typedef Input input_type; - typedef Output output_type; - typedef function_body function_body_type; - typedef function_input my_class; - typedef function_input_base base_type; - typedef function_input_queue input_queue_type; - - // constructor - template - function_input( - graph &g, size_t max_concurrency, Body& body, node_priority_t a_priority ) - : base_type(g, max_concurrency, a_priority, noexcept(tbb::detail::invoke(body, input_type()))) - , my_body( new function_body_leaf< input_type, output_type, Body>(body) ) - , my_init_body( new function_body_leaf< input_type, output_type, Body>(body) ) { - } - - //! Copy constructor - function_input( const function_input& src ) : - base_type(src), - my_body( src.my_init_body->clone() ), - my_init_body(src.my_init_body->clone() ) { - } -#if __INTEL_COMPILER <= 2021 - // Suppress superfluous diagnostic about virtual keyword absence in a destructor of an inherited - // class while the parent class has the virtual keyword for the destrocutor. - virtual -#endif - ~function_input() { - delete my_body; - delete my_init_body; - } - - template< typename Body > - Body copy_function_object() { - function_body_type &body_ref = *this->my_body; - return dynamic_cast< function_body_leaf & >(body_ref).get_body(); - } - - output_type apply_body_impl( const input_type& i) { - // There is an extra copied needed to capture the - // body execution without the try_put - fgt_begin_body( my_body ); - output_type v = tbb::detail::invoke(*my_body, i); - fgt_end_body( my_body ); - return v; - } - - //TODO: consider moving into the base class - graph_task* apply_body_impl_bypass( const input_type &i) { - output_type v = apply_body_impl(i); - graph_task* postponed_task = nullptr; - if( base_type::my_max_concurrency != 0 ) { - postponed_task = base_type::try_get_postponed_task(i); - __TBB_ASSERT( !postponed_task || postponed_task != SUCCESSFULLY_ENQUEUED, nullptr); - } - if( postponed_task ) { - // make the task available for other workers since we do not know successors' - // execution policy - spawn_in_graph_arena(base_type::graph_reference(), *postponed_task); - } - graph_task* successor_task = successors().try_put_task(v); -#if _MSC_VER && !__INTEL_COMPILER -#pragma warning (push) -#pragma warning (disable: 4127) /* suppress conditional expression is constant */ -#endif - if(has_policy::value) { -#if _MSC_VER && !__INTEL_COMPILER -#pragma warning (pop) -#endif - if(!successor_task) { - // Return confirmative status since current - // node's body has been executed anyway - successor_task = SUCCESSFULLY_ENQUEUED; - } - } - return successor_task; - } - -protected: - - void reset_function_input(reset_flags f) { - base_type::reset_function_input_base(f); - if(f & rf_reset_bodies) { - function_body_type *tmp = my_init_body->clone(); - delete my_body; - my_body = tmp; - } - } - - function_body_type *my_body; - function_body_type *my_init_body; - virtual broadcast_cache &successors() = 0; - -}; // function_input - - -// helper templates to clear the successor edges of the output ports of an multifunction_node -template struct clear_element { - template static void clear_this(P &p) { - (void)std::get(p).successors().clear(); - clear_element::clear_this(p); - } -#if TBB_USE_ASSERT - template static bool this_empty(P &p) { - if(std::get(p).successors().empty()) - return clear_element::this_empty(p); - return false; - } -#endif -}; - -template<> struct clear_element<1> { - template static void clear_this(P &p) { - (void)std::get<0>(p).successors().clear(); - } -#if TBB_USE_ASSERT - template static bool this_empty(P &p) { - return std::get<0>(p).successors().empty(); - } -#endif -}; - -template -struct init_output_ports { - template - static OutputTuple call(graph& g, const std::tuple&) { - return OutputTuple(Args(g)...); - } -}; // struct init_output_ports - -//! Implements methods for a function node that takes a type Input as input -// and has a tuple of output ports specified. -template< typename Input, typename OutputPortSet, typename Policy, typename A> -class multifunction_input : public function_input_base > { -public: - static const int N = std::tuple_size::value; - typedef Input input_type; - typedef OutputPortSet output_ports_type; - typedef multifunction_body multifunction_body_type; - typedef multifunction_input my_class; - typedef function_input_base base_type; - typedef function_input_queue input_queue_type; - - // constructor - template - multifunction_input(graph &g, size_t max_concurrency,Body& body, node_priority_t a_priority ) - : base_type(g, max_concurrency, a_priority, noexcept(tbb::detail::invoke(body, input_type(), my_output_ports))) - , my_body( new multifunction_body_leaf(body) ) - , my_init_body( new multifunction_body_leaf(body) ) - , my_output_ports(init_output_ports::call(g, my_output_ports)){ - } - - //! Copy constructor - multifunction_input( const multifunction_input& src ) : - base_type(src), - my_body( src.my_init_body->clone() ), - my_init_body(src.my_init_body->clone() ), - my_output_ports( init_output_ports::call(src.my_graph_ref, my_output_ports) ) { - } - - ~multifunction_input() { - delete my_body; - delete my_init_body; - } - - template< typename Body > - Body copy_function_object() { - multifunction_body_type &body_ref = *this->my_body; - return *static_cast(dynamic_cast< multifunction_body_leaf & >(body_ref).get_body_ptr()); - } - - // for multifunction nodes we do not have a single successor as such. So we just tell - // the task we were successful. - //TODO: consider moving common parts with implementation in function_input into separate function - graph_task* apply_body_impl_bypass( const input_type &i ) { - fgt_begin_body( my_body ); - (*my_body)(i, my_output_ports); - fgt_end_body( my_body ); - graph_task* ttask = nullptr; - if(base_type::my_max_concurrency != 0) { - ttask = base_type::try_get_postponed_task(i); - } - return ttask ? ttask : SUCCESSFULLY_ENQUEUED; - } - - output_ports_type &output_ports(){ return my_output_ports; } - -protected: - - void reset(reset_flags f) { - base_type::reset_function_input_base(f); - if(f & rf_clear_edges)clear_element::clear_this(my_output_ports); - if(f & rf_reset_bodies) { - multifunction_body_type* tmp = my_init_body->clone(); - delete my_body; - my_body = tmp; - } - __TBB_ASSERT(!(f & rf_clear_edges) || clear_element::this_empty(my_output_ports), "multifunction_node reset failed"); - } - - multifunction_body_type *my_body; - multifunction_body_type *my_init_body; - output_ports_type my_output_ports; - -}; // multifunction_input - -// template to refer to an output port of a multifunction_node -template -typename std::tuple_element::type &output_port(MOP &op) { - return std::get(op.output_ports()); -} - -inline void check_task_and_spawn(graph& g, graph_task* t) { - if (t && t != SUCCESSFULLY_ENQUEUED) { - spawn_in_graph_arena(g, *t); - } -} - -// helper structs for split_node -template -struct emit_element { - template - static graph_task* emit_this(graph& g, const T &t, P &p) { - // TODO: consider to collect all the tasks in task_list and spawn them all at once - graph_task* last_task = std::get(p).try_put_task(std::get(t)); - check_task_and_spawn(g, last_task); - return emit_element::emit_this(g,t,p); - } -}; - -template<> -struct emit_element<1> { - template - static graph_task* emit_this(graph& g, const T &t, P &p) { - graph_task* last_task = std::get<0>(p).try_put_task(std::get<0>(t)); - check_task_and_spawn(g, last_task); - return SUCCESSFULLY_ENQUEUED; - } -}; - -//! Implements methods for an executable node that takes continue_msg as input -template< typename Output, typename Policy> -class continue_input : public continue_receiver { -public: - - //! The input type of this receiver - typedef continue_msg input_type; - - //! The output type of this receiver - typedef Output output_type; - typedef function_body function_body_type; - typedef continue_input class_type; - - template< typename Body > - continue_input( graph &g, Body& body, node_priority_t a_priority ) - : continue_receiver(/*number_of_predecessors=*/0, a_priority) - , my_graph_ref(g) - , my_body( new function_body_leaf< input_type, output_type, Body>(body) ) - , my_init_body( new function_body_leaf< input_type, output_type, Body>(body) ) - { } - - template< typename Body > - continue_input( graph &g, int number_of_predecessors, - Body& body, node_priority_t a_priority ) - : continue_receiver( number_of_predecessors, a_priority ) - , my_graph_ref(g) - , my_body( new function_body_leaf< input_type, output_type, Body>(body) ) - , my_init_body( new function_body_leaf< input_type, output_type, Body>(body) ) - { } - - continue_input( const continue_input& src ) : continue_receiver(src), - my_graph_ref(src.my_graph_ref), - my_body( src.my_init_body->clone() ), - my_init_body( src.my_init_body->clone() ) {} - - ~continue_input() { - delete my_body; - delete my_init_body; - } - - template< typename Body > - Body copy_function_object() { - function_body_type &body_ref = *my_body; - return dynamic_cast< function_body_leaf & >(body_ref).get_body(); - } - - void reset_receiver( reset_flags f) override { - continue_receiver::reset_receiver(f); - if(f & rf_reset_bodies) { - function_body_type *tmp = my_init_body->clone(); - delete my_body; - my_body = tmp; - } - } - -protected: - - graph& my_graph_ref; - function_body_type *my_body; - function_body_type *my_init_body; - - virtual broadcast_cache &successors() = 0; - - friend class apply_body_task_bypass< class_type, continue_msg >; - - //! Applies the body to the provided input - graph_task* apply_body_bypass( input_type ) { - // There is an extra copied needed to capture the - // body execution without the try_put - fgt_begin_body( my_body ); - output_type v = (*my_body)( continue_msg() ); - fgt_end_body( my_body ); - return successors().try_put_task( v ); - } - - graph_task* execute() override { - if(!is_graph_active(my_graph_ref)) { - return nullptr; - } -#if _MSC_VER && !__INTEL_COMPILER -#pragma warning (push) -#pragma warning (disable: 4127) /* suppress conditional expression is constant */ -#endif - if(has_policy::value) { -#if _MSC_VER && !__INTEL_COMPILER -#pragma warning (pop) -#endif - return apply_body_bypass( continue_msg() ); - } - else { - small_object_allocator allocator{}; - typedef apply_body_task_bypass task_type; - graph_task* t = allocator.new_object( graph_reference(), allocator, *this, continue_msg(), my_priority ); - graph_reference().reserve_wait(); - return t; - } - } - - graph& graph_reference() const override { - return my_graph_ref; - } -}; // continue_input - -//! Implements methods for both executable and function nodes that puts Output to its successors -template< typename Output > -class function_output : public sender { -public: - - template friend struct clear_element; - typedef Output output_type; - typedef typename sender::successor_type successor_type; - typedef broadcast_cache broadcast_cache_type; - - function_output(graph& g) : my_successors(this), my_graph_ref(g) {} - function_output(const function_output& other) = delete; - - //! Adds a new successor to this node - bool register_successor( successor_type &r ) override { - successors().register_successor( r ); - return true; - } - - //! Removes a successor from this node - bool remove_successor( successor_type &r ) override { - successors().remove_successor( r ); - return true; - } - - broadcast_cache_type &successors() { return my_successors; } - - graph& graph_reference() const { return my_graph_ref; } -protected: - broadcast_cache_type my_successors; - graph& my_graph_ref; -}; // function_output - -template< typename Output > -class multifunction_output : public function_output { -public: - typedef Output output_type; - typedef function_output base_type; - using base_type::my_successors; - - multifunction_output(graph& g) : base_type(g) {} - multifunction_output(const multifunction_output& other) : base_type(other.my_graph_ref) {} - - bool try_put(const output_type &i) { - graph_task *res = try_put_task(i); - if( !res ) return false; - if( res != SUCCESSFULLY_ENQUEUED ) { - // wrapping in task_arena::execute() is not needed since the method is called from - // inside task::execute() - spawn_in_graph_arena(graph_reference(), *res); - } - return true; - } - - using base_type::graph_reference; - -protected: - - graph_task* try_put_task(const output_type &i) { - return my_successors.try_put_task(i); - } - - template friend struct emit_element; - -}; // multifunction_output - -//composite_node -template -void add_nodes_impl(CompositeType*, bool) {} - -template< typename CompositeType, typename NodeType1, typename... NodeTypes > -void add_nodes_impl(CompositeType *c_node, bool visible, const NodeType1& n1, const NodeTypes&... n) { - void *addr = const_cast(&n1); - - fgt_alias_port(c_node, addr, visible); - add_nodes_impl(c_node, visible, n...); -} - -#endif // __TBB__flow_graph_node_impl_H diff --git a/include/oneapi/tbb/detail/_flow_graph_node_set_impl.h b/include/oneapi/tbb/detail/_flow_graph_node_set_impl.h deleted file mode 100644 index ce86712..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_node_set_impl.h +++ /dev/null @@ -1,265 +0,0 @@ -/* - Copyright (c) 2020-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_flow_graph_node_set_impl_H -#define __TBB_flow_graph_node_set_impl_H - -#ifndef __TBB_flow_graph_H -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -// Included in namespace tbb::detail::d1 (in flow_graph.h) - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET -// Visual Studio 2019 reports an error while calling predecessor_selector::get and successor_selector::get -// Seems like the well-formed expression in trailing decltype is treated as ill-formed -// TODO: investigate problems with decltype in trailing return types or find the cross-platform solution -#define __TBB_MSVC_DISABLE_TRAILING_DECLTYPE (_MSC_VER >= 1900) - -namespace order { -struct undefined {}; -struct following {}; -struct preceding {}; -} - -class get_graph_helper { -public: - // TODO: consider making graph_reference() public and consistent interface to get a reference to the graph - // and remove get_graph_helper - template - static graph& get(const T& object) { - return get_impl(object, std::is_base_of()); - } - -private: - // Get graph from the object of type derived from graph_node - template - static graph& get_impl(const T& object, std::true_type) { - return static_cast(&object)->my_graph; - } - - template - static graph& get_impl(const T& object, std::false_type) { - return object.graph_reference(); - } -}; - -template -struct node_set { - typedef Order order_type; - - std::tuple nodes; - node_set(Nodes&... ns) : nodes(ns...) {} - - template - node_set(const node_set& set) : nodes(set.nodes) {} - - graph& graph_reference() const { - return get_graph_helper::get(std::get<0>(nodes)); - } -}; - -namespace alias_helpers { -template using output_type = typename T::output_type; -template using output_ports_type = typename T::output_ports_type; -template using input_type = typename T::input_type; -template using input_ports_type = typename T::input_ports_type; -} // namespace alias_helpers - -template -using has_output_type = supports; - -template -using has_input_type = supports; - -template -using has_input_ports_type = supports; - -template -using has_output_ports_type = supports; - -template -struct is_sender : std::is_base_of, T> {}; - -template -struct is_receiver : std::is_base_of, T> {}; - -template -struct is_async_node : std::false_type {}; - -template -struct is_async_node> : std::true_type {}; - -template -node_set -follows(FirstPredecessor& first_predecessor, Predecessors&... predecessors) { - static_assert((conjunction, - has_output_type...>::value), - "Not all node's predecessors has output_type typedef"); - static_assert((conjunction, is_sender...>::value), - "Not all node's predecessors are senders"); - return node_set(first_predecessor, predecessors...); -} - -template -node_set -follows(node_set& predecessors_set) { - static_assert((conjunction...>::value), - "Not all nodes in the set has output_type typedef"); - static_assert((conjunction...>::value), - "Not all nodes in the set are senders"); - return node_set(predecessors_set); -} - -template -node_set -precedes(FirstSuccessor& first_successor, Successors&... successors) { - static_assert((conjunction, - has_input_type...>::value), - "Not all node's successors has input_type typedef"); - static_assert((conjunction, is_receiver...>::value), - "Not all node's successors are receivers"); - return node_set(first_successor, successors...); -} - -template -node_set -precedes(node_set& successors_set) { - static_assert((conjunction...>::value), - "Not all nodes in the set has input_type typedef"); - static_assert((conjunction...>::value), - "Not all nodes in the set are receivers"); - return node_set(successors_set); -} - -template -node_set -make_node_set(Node& first_node, Nodes&... nodes) { - return node_set(first_node, nodes...); -} - -template -class successor_selector { - template - static auto get_impl(NodeType& node, std::true_type) -> decltype(input_port(node)) { - return input_port(node); - } - - template - static NodeType& get_impl(NodeType& node, std::false_type) { return node; } - -public: - template -#if __TBB_MSVC_DISABLE_TRAILING_DECLTYPE - static auto& get(NodeType& node) -#else - static auto get(NodeType& node) -> decltype(get_impl(node, has_input_ports_type())) -#endif - { - return get_impl(node, has_input_ports_type()); - } -}; - -template -class predecessor_selector { - template - static auto internal_get(NodeType& node, std::true_type) -> decltype(output_port(node)) { - return output_port(node); - } - - template - static NodeType& internal_get(NodeType& node, std::false_type) { return node;} - - template -#if __TBB_MSVC_DISABLE_TRAILING_DECLTYPE - static auto& get_impl(NodeType& node, std::false_type) -#else - static auto get_impl(NodeType& node, std::false_type) -> decltype(internal_get(node, has_output_ports_type())) -#endif - { - return internal_get(node, has_output_ports_type()); - } - - template - static AsyncNode& get_impl(AsyncNode& node, std::true_type) { return node; } - -public: - template -#if __TBB_MSVC_DISABLE_TRAILING_DECLTYPE - static auto& get(NodeType& node) -#else - static auto get(NodeType& node) -> decltype(get_impl(node, is_async_node())) -#endif - { - return get_impl(node, is_async_node()); - } -}; - -template -class make_edges_helper { -public: - template - static void connect_predecessors(PredecessorsTuple& predecessors, NodeType& node) { - make_edge(std::get(predecessors), successor_selector::get(node)); - make_edges_helper::connect_predecessors(predecessors, node); - } - - template - static void connect_successors(NodeType& node, SuccessorsTuple& successors) { - make_edge(predecessor_selector::get(node), std::get(successors)); - make_edges_helper::connect_successors(node, successors); - } -}; - -template<> -struct make_edges_helper<0> { - template - static void connect_predecessors(PredecessorsTuple& predecessors, NodeType& node) { - make_edge(std::get<0>(predecessors), successor_selector<0>::get(node)); - } - - template - static void connect_successors(NodeType& node, SuccessorsTuple& successors) { - make_edge(predecessor_selector<0>::get(node), std::get<0>(successors)); - } -}; - -// TODO: consider adding an overload for making edges between node sets -template -void make_edges(const node_set& s, NodeType& node) { - const std::size_t SetSize = std::tuple_size::value; - make_edges_helper::connect_predecessors(s.nodes, node); -} - -template -void make_edges(NodeType& node, const node_set& s) { - const std::size_t SetSize = std::tuple_size::value; - make_edges_helper::connect_successors(node, s.nodes); -} - -template -void make_edges_in_order(const node_set& ns, NodeType& node) { - make_edges(ns, node); -} - -template -void make_edges_in_order(const node_set& ns, NodeType& node) { - make_edges(node, ns); -} - -#endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - -#endif // __TBB_flow_graph_node_set_impl_H diff --git a/include/oneapi/tbb/detail/_flow_graph_nodes_deduction.h b/include/oneapi/tbb/detail/_flow_graph_nodes_deduction.h deleted file mode 100644 index 8c20993..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_nodes_deduction.h +++ /dev/null @@ -1,277 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_flow_graph_nodes_deduction_H -#define __TBB_flow_graph_nodes_deduction_H - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -namespace tbb { -namespace detail { -namespace d1 { - -template -struct declare_body_types { - using input_type = Input; - using output_type = Output; -}; - -struct NoInputBody {}; - -template -struct declare_body_types { - using output_type = Output; -}; - -template struct body_types; - -template -struct body_types : declare_body_types {}; - -template -struct body_types : declare_body_types {}; - -template -struct body_types : declare_body_types {}; - -template -struct body_types : declare_body_types {}; - -template -struct body_types : declare_body_types {}; - -template -struct body_types : declare_body_types {}; - -template -struct body_types : declare_body_types {}; - -template -struct body_types : declare_body_types {}; - -template -struct body_types : declare_body_types {}; - -template -using input_t = typename body_types::input_type; - -template -using output_t = typename body_types::output_type; - -template -auto decide_on_operator_overload(Output (T::*name)(const Input&) const)->decltype(name); - -template -auto decide_on_operator_overload(Output (T::*name)(const Input&))->decltype(name); - -template -auto decide_on_operator_overload(Output (T::*name)(Input&) const)->decltype(name); - -template -auto decide_on_operator_overload(Output (T::*name)(Input&))->decltype(name); - -template -auto decide_on_operator_overload(Output (*name)(const Input&))->decltype(name); - -template -auto decide_on_operator_overload(Output (*name)(Input&))->decltype(name); - -template -decltype(decide_on_operator_overload(&Body::operator())) decide_on_callable_type(int); - -template -decltype(decide_on_operator_overload(std::declval())) decide_on_callable_type(...); - -// Deduction guides for Flow Graph nodes - -template -input_node(GraphOrSet&&, Body) -->input_node(0))>>; - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - -template -struct decide_on_set; - -template -struct decide_on_set> { - using type = typename Node::output_type; -}; - -template -struct decide_on_set> { - using type = typename Node::input_type; -}; - -template -using decide_on_set_t = typename decide_on_set>::type; - -template -broadcast_node(const NodeSet&) -->broadcast_node>; - -template -buffer_node(const NodeSet&) -->buffer_node>; - -template -queue_node(const NodeSet&) -->queue_node>; -#endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - -template -sequencer_node(GraphOrProxy&&, Sequencer) -->sequencer_node(0))>>; - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET -template -priority_queue_node(const NodeSet&, const Compare&) -->priority_queue_node, Compare>; - -template -priority_queue_node(const NodeSet&) -->priority_queue_node, std::less>>; -#endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - -template -struct join_key { - using type = Key; -}; - -template -struct join_key { - using type = T&; -}; - -template -using join_key_t = typename join_key::type; - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET -template -join_node(const node_set&, Policy) -->join_node, - Policy>; - -template -join_node(const node_set&, Policy) -->join_node; - -template -join_node(const node_set) -->join_node, - queueing>; - -template -join_node(const node_set) -->join_node; -#endif - -template -join_node(GraphOrProxy&&, Body, Bodies...) -->join_node(0))>, - input_t(0))>...>, - key_matching(0))>>>>; - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET -template -indexer_node(const node_set&) -->indexer_node; -#endif - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET -template -limiter_node(const NodeSet&, size_t) -->limiter_node>; - -template -split_node(const node_set&) -->split_node; - -template -split_node(const node_set&) -->split_node>; - -#endif - -template -function_node(GraphOrSet&&, - size_t, Body, - Policy, node_priority_t = no_priority) -->function_node(0))>, - output_t(0))>, - Policy>; - -template -function_node(GraphOrSet&&, size_t, - Body, node_priority_t = no_priority) -->function_node(0))>, - output_t(0))>, - queueing>; - -template -struct continue_output { - using type = Output; -}; - -template <> -struct continue_output { - using type = continue_msg; -}; - -template -using continue_output_t = typename continue_output::type; - -template -continue_node(GraphOrSet&&, Body, - Policy, node_priority_t = no_priority) -->continue_node>, - Policy>; - -template -continue_node(GraphOrSet&&, - int, Body, - Policy, node_priority_t = no_priority) -->continue_node>, - Policy>; - -template -continue_node(GraphOrSet&&, - Body, node_priority_t = no_priority) -->continue_node>, Policy>; - -template -continue_node(GraphOrSet&&, int, - Body, node_priority_t = no_priority) -->continue_node>, - Policy>; - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - -template -overwrite_node(const NodeSet&) -->overwrite_node>; - -template -write_once_node(const NodeSet&) -->write_once_node>; -#endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -#endif // __TBB_flow_graph_nodes_deduction_H diff --git a/include/oneapi/tbb/detail/_flow_graph_tagged_buffer_impl.h b/include/oneapi/tbb/detail/_flow_graph_tagged_buffer_impl.h deleted file mode 100644 index 0d9de17..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_tagged_buffer_impl.h +++ /dev/null @@ -1,257 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -// a hash table buffer that can expand, and can support as many deletions as -// additions, list-based, with elements of list held in array (for destruction -// management), multiplicative hashing (like ets). No synchronization built-in. -// - -#ifndef __TBB__flow_graph_hash_buffer_impl_H -#define __TBB__flow_graph_hash_buffer_impl_H - -#ifndef __TBB_flow_graph_H -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -// included in namespace tbb::flow::interfaceX::internal - -// elements in the table are a simple list; we need pointer to next element to -// traverse the chain -template -struct buffer_element_type { - // the second parameter below is void * because we can't forward-declare the type - // itself, so we just reinterpret_cast below. - typedef typename aligned_pair::type type; -}; - -template - < - typename Key, // type of key within ValueType - typename ValueType, - typename ValueToKey, // abstract method that returns "const Key" or "const Key&" given ValueType - typename HashCompare, // has hash and equal - typename Allocator=tbb::cache_aligned_allocator< typename aligned_pair::type > - > -class hash_buffer : public HashCompare { -public: - static const size_t INITIAL_SIZE = 8; // initial size of the hash pointer table - typedef ValueType value_type; - typedef typename buffer_element_type< value_type >::type element_type; - typedef value_type *pointer_type; - typedef element_type *list_array_type; // array we manage manually - typedef list_array_type *pointer_array_type; - typedef typename std::allocator_traits::template rebind_alloc pointer_array_allocator_type; - typedef typename std::allocator_traits::template rebind_alloc elements_array_allocator; - typedef typename std::decay::type Knoref; - -private: - ValueToKey *my_key; - size_t my_size; - size_t nelements; - pointer_array_type pointer_array; // pointer_array[my_size] - list_array_type elements_array; // elements_array[my_size / 2] - element_type* free_list; - - size_t mask() { return my_size - 1; } - - void set_up_free_list( element_type **p_free_list, list_array_type la, size_t sz) { - for(size_t i=0; i < sz - 1; ++i ) { // construct free list - la[i].second = &(la[i+1]); - } - la[sz-1].second = nullptr; - *p_free_list = (element_type *)&(la[0]); - } - - // cleanup for exceptions - struct DoCleanup { - pointer_array_type *my_pa; - list_array_type *my_elements; - size_t my_size; - - DoCleanup(pointer_array_type &pa, list_array_type &my_els, size_t sz) : - my_pa(&pa), my_elements(&my_els), my_size(sz) { } - ~DoCleanup() { - if(my_pa) { - size_t dont_care = 0; - internal_free_buffer(*my_pa, *my_elements, my_size, dont_care); - } - } - }; - - // exception-safety requires we do all the potentially-throwing operations first - void grow_array() { - size_t new_size = my_size*2; - size_t new_nelements = nelements; // internal_free_buffer zeroes this - list_array_type new_elements_array = nullptr; - pointer_array_type new_pointer_array = nullptr; - list_array_type new_free_list = nullptr; - { - DoCleanup my_cleanup(new_pointer_array, new_elements_array, new_size); - new_elements_array = elements_array_allocator().allocate(my_size); - new_pointer_array = pointer_array_allocator_type().allocate(new_size); - for(size_t i=0; i < new_size; ++i) new_pointer_array[i] = nullptr; - set_up_free_list(&new_free_list, new_elements_array, my_size ); - - for(size_t i=0; i < my_size; ++i) { - for( element_type* op = pointer_array[i]; op; op = (element_type *)(op->second)) { - value_type *ov = reinterpret_cast(&(op->first)); - // could have std::move semantics - internal_insert_with_key(new_pointer_array, new_size, new_free_list, *ov); - } - } - my_cleanup.my_pa = nullptr; - my_cleanup.my_elements = nullptr; - } - - internal_free_buffer(pointer_array, elements_array, my_size, nelements); - free_list = new_free_list; - pointer_array = new_pointer_array; - elements_array = new_elements_array; - my_size = new_size; - nelements = new_nelements; - } - - // v should have perfect forwarding if std::move implemented. - // we use this method to move elements in grow_array, so can't use class fields - void internal_insert_with_key( element_type **p_pointer_array, size_t p_sz, list_array_type &p_free_list, - const value_type &v) { - size_t l_mask = p_sz-1; - __TBB_ASSERT(my_key, "Error: value-to-key functor not provided"); - size_t h = this->hash(tbb::detail::invoke(*my_key, v)) & l_mask; - __TBB_ASSERT(p_free_list, "Error: free list not set up."); - element_type* my_elem = p_free_list; p_free_list = (element_type *)(p_free_list->second); - (void) new(&(my_elem->first)) value_type(v); - my_elem->second = p_pointer_array[h]; - p_pointer_array[h] = my_elem; - } - - void internal_initialize_buffer() { - pointer_array = pointer_array_allocator_type().allocate(my_size); - for(size_t i = 0; i < my_size; ++i) pointer_array[i] = nullptr; - elements_array = elements_array_allocator().allocate(my_size / 2); - set_up_free_list(&free_list, elements_array, my_size / 2); - } - - // made static so an enclosed class can use to properly dispose of the internals - static void internal_free_buffer( pointer_array_type &pa, list_array_type &el, size_t &sz, size_t &ne ) { - if(pa) { - for(size_t i = 0; i < sz; ++i ) { - element_type *p_next; - for( element_type *p = pa[i]; p; p = p_next) { - p_next = (element_type *)p->second; - // TODO revamp: make sure type casting is correct. - void* ptr = (void*)(p->first); -#if _MSC_VER && _MSC_VER <= 1900 && !__INTEL_COMPILER - suppress_unused_warning(ptr); -#endif - ((value_type*)ptr)->~value_type(); - } - } - pointer_array_allocator_type().deallocate(pa, sz); - pa = nullptr; - } - // Separate test (if allocation of pa throws, el may be allocated. - // but no elements will be constructed.) - if(el) { - elements_array_allocator().deallocate(el, sz / 2); - el = nullptr; - } - sz = INITIAL_SIZE; - ne = 0; - } - -public: - hash_buffer() : my_key(nullptr), my_size(INITIAL_SIZE), nelements(0) { - internal_initialize_buffer(); - } - - ~hash_buffer() { - internal_free_buffer(pointer_array, elements_array, my_size, nelements); - delete my_key; - my_key = nullptr; - } - hash_buffer(const hash_buffer&) = delete; - hash_buffer& operator=(const hash_buffer&) = delete; - - void reset() { - internal_free_buffer(pointer_array, elements_array, my_size, nelements); - internal_initialize_buffer(); - } - - // Take ownership of func object allocated with new. - // This method is only used internally, so can't be misused by user. - void set_key_func(ValueToKey *vtk) { my_key = vtk; } - // pointer is used to clone() - ValueToKey* get_key_func() { return my_key; } - - bool insert_with_key(const value_type &v) { - pointer_type p = nullptr; - __TBB_ASSERT(my_key, "Error: value-to-key functor not provided"); - if(find_ref_with_key(tbb::detail::invoke(*my_key, v), p)) { - p->~value_type(); - (void) new(p) value_type(v); // copy-construct into the space - return false; - } - ++nelements; - if(nelements*2 > my_size) grow_array(); - internal_insert_with_key(pointer_array, my_size, free_list, v); - return true; - } - - // returns true and sets v to array element if found, else returns false. - bool find_ref_with_key(const Knoref& k, pointer_type &v) { - size_t i = this->hash(k) & mask(); - for(element_type* p = pointer_array[i]; p; p = (element_type *)(p->second)) { - pointer_type pv = reinterpret_cast(&(p->first)); - __TBB_ASSERT(my_key, "Error: value-to-key functor not provided"); - if(this->equal(tbb::detail::invoke(*my_key, *pv), k)) { - v = pv; - return true; - } - } - return false; - } - - bool find_with_key( const Knoref& k, value_type &v) { - value_type *p; - if(find_ref_with_key(k, p)) { - v = *p; - return true; - } - else - return false; - } - - void delete_with_key(const Knoref& k) { - size_t h = this->hash(k) & mask(); - element_type* prev = nullptr; - for(element_type* p = pointer_array[h]; p; prev = p, p = (element_type *)(p->second)) { - value_type *vp = reinterpret_cast(&(p->first)); - __TBB_ASSERT(my_key, "Error: value-to-key functor not provided"); - if(this->equal(tbb::detail::invoke(*my_key, *vp), k)) { - vp->~value_type(); - if(prev) prev->second = p->second; - else pointer_array[h] = (element_type *)(p->second); - p->second = free_list; - free_list = p; - --nelements; - return; - } - } - __TBB_ASSERT(false, "key not found for delete"); - } -}; -#endif // __TBB__flow_graph_hash_buffer_impl_H diff --git a/include/oneapi/tbb/detail/_flow_graph_trace_impl.h b/include/oneapi/tbb/detail/_flow_graph_trace_impl.h deleted file mode 100644 index a161dd0..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_trace_impl.h +++ /dev/null @@ -1,364 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef _FGT_GRAPH_TRACE_IMPL_H -#define _FGT_GRAPH_TRACE_IMPL_H - -#include "../profiling.h" -#if (_MSC_VER >= 1900) - #include -#endif - -namespace tbb { -namespace detail { -namespace d1 { - -template< typename T > class sender; -template< typename T > class receiver; - -#if TBB_USE_PROFILING_TOOLS - #if __TBB_FLOW_TRACE_CODEPTR - #if (_MSC_VER >= 1900) - #define CODEPTR() (_ReturnAddress()) - #elif __TBB_GCC_VERSION >= 40800 - #define CODEPTR() ( __builtin_return_address(0)) - #else - #define CODEPTR() nullptr - #endif - #else - #define CODEPTR() nullptr - #endif /* __TBB_FLOW_TRACE_CODEPTR */ - -static inline void fgt_alias_port(void *node, void *p, bool visible) { - if(visible) - itt_relation_add( ITT_DOMAIN_FLOW, node, FLOW_NODE, __itt_relation_is_parent_of, p, FLOW_NODE ); - else - itt_relation_add( ITT_DOMAIN_FLOW, p, FLOW_NODE, __itt_relation_is_child_of, node, FLOW_NODE ); -} - -static inline void fgt_composite ( void* codeptr, void *node, void *graph ) { - itt_make_task_group( ITT_DOMAIN_FLOW, node, FLOW_NODE, graph, FLOW_GRAPH, FLOW_COMPOSITE_NODE ); - suppress_unused_warning( codeptr ); -#if __TBB_FLOW_TRACE_CODEPTR - if (codeptr != nullptr) { - register_node_addr(ITT_DOMAIN_FLOW, node, FLOW_NODE, CODE_ADDRESS, &codeptr); - } -#endif -} - -static inline void fgt_internal_alias_input_port( void *node, void *p, string_resource_index name_index ) { - itt_make_task_group( ITT_DOMAIN_FLOW, p, FLOW_INPUT_PORT, node, FLOW_NODE, name_index ); - itt_relation_add( ITT_DOMAIN_FLOW, node, FLOW_NODE, __itt_relation_is_parent_of, p, FLOW_INPUT_PORT ); -} - -static inline void fgt_internal_alias_output_port( void *node, void *p, string_resource_index name_index ) { - itt_make_task_group( ITT_DOMAIN_FLOW, p, FLOW_OUTPUT_PORT, node, FLOW_NODE, name_index ); - itt_relation_add( ITT_DOMAIN_FLOW, node, FLOW_NODE, __itt_relation_is_parent_of, p, FLOW_OUTPUT_PORT ); -} - -template -void alias_input_port(void *node, receiver* port, string_resource_index name_index) { - // TODO: Make fgt_internal_alias_input_port a function template? - fgt_internal_alias_input_port( node, port, name_index); -} - -template < typename PortsTuple, int N > -struct fgt_internal_input_alias_helper { - static void alias_port( void *node, PortsTuple &ports ) { - alias_input_port( node, &(std::get(ports)), static_cast(FLOW_INPUT_PORT_0 + N - 1) ); - fgt_internal_input_alias_helper::alias_port( node, ports ); - } -}; - -template < typename PortsTuple > -struct fgt_internal_input_alias_helper { - static void alias_port( void * /* node */, PortsTuple & /* ports */ ) { } -}; - -template -void alias_output_port(void *node, sender* port, string_resource_index name_index) { - // TODO: Make fgt_internal_alias_output_port a function template? - fgt_internal_alias_output_port( node, static_cast(port), name_index); -} - -template < typename PortsTuple, int N > -struct fgt_internal_output_alias_helper { - static void alias_port( void *node, PortsTuple &ports ) { - alias_output_port( node, &(std::get(ports)), static_cast(FLOW_OUTPUT_PORT_0 + N - 1) ); - fgt_internal_output_alias_helper::alias_port( node, ports ); - } -}; - -template < typename PortsTuple > -struct fgt_internal_output_alias_helper { - static void alias_port( void * /*node*/, PortsTuple &/*ports*/ ) { - } -}; - -static inline void fgt_internal_create_input_port( void *node, void *p, string_resource_index name_index ) { - itt_make_task_group( ITT_DOMAIN_FLOW, p, FLOW_INPUT_PORT, node, FLOW_NODE, name_index ); -} - -static inline void fgt_internal_create_output_port( void* codeptr, void *node, void *p, string_resource_index name_index ) { - itt_make_task_group(ITT_DOMAIN_FLOW, p, FLOW_OUTPUT_PORT, node, FLOW_NODE, name_index); - suppress_unused_warning( codeptr ); -#if __TBB_FLOW_TRACE_CODEPTR - if (codeptr != nullptr) { - register_node_addr(ITT_DOMAIN_FLOW, node, FLOW_NODE, CODE_ADDRESS, &codeptr); - } -#endif -} - -template -void register_input_port(void *node, receiver* port, string_resource_index name_index) { - // TODO: Make fgt_internal_create_input_port a function template? - fgt_internal_create_input_port(node, static_cast(port), name_index); -} - -template < typename PortsTuple, int N > -struct fgt_internal_input_helper { - static void register_port( void *node, PortsTuple &ports ) { - register_input_port( node, &(std::get(ports)), static_cast(FLOW_INPUT_PORT_0 + N - 1) ); - fgt_internal_input_helper::register_port( node, ports ); - } -}; - -template < typename PortsTuple > -struct fgt_internal_input_helper { - static void register_port( void *node, PortsTuple &ports ) { - register_input_port( node, &(std::get<0>(ports)), FLOW_INPUT_PORT_0 ); - } -}; - -template -void register_output_port(void* codeptr, void *node, sender* port, string_resource_index name_index) { - // TODO: Make fgt_internal_create_output_port a function template? - fgt_internal_create_output_port( codeptr, node, static_cast(port), name_index); -} - -template < typename PortsTuple, int N > -struct fgt_internal_output_helper { - static void register_port( void* codeptr, void *node, PortsTuple &ports ) { - register_output_port( codeptr, node, &(std::get(ports)), static_cast(FLOW_OUTPUT_PORT_0 + N - 1) ); - fgt_internal_output_helper::register_port( codeptr, node, ports ); - } -}; - -template < typename PortsTuple > -struct fgt_internal_output_helper { - static void register_port( void* codeptr, void *node, PortsTuple &ports ) { - register_output_port( codeptr, node, &(std::get<0>(ports)), FLOW_OUTPUT_PORT_0 ); - } -}; - -template< typename NodeType > -void fgt_multioutput_node_desc( const NodeType *node, const char *desc ) { - void *addr = (void *)( static_cast< receiver< typename NodeType::input_type > * >(const_cast< NodeType *>(node)) ); - itt_metadata_str_add( ITT_DOMAIN_FLOW, addr, FLOW_NODE, FLOW_OBJECT_NAME, desc ); -} - -template< typename NodeType > -void fgt_multiinput_multioutput_node_desc( const NodeType *node, const char *desc ) { - void *addr = const_cast(node); - itt_metadata_str_add( ITT_DOMAIN_FLOW, addr, FLOW_NODE, FLOW_OBJECT_NAME, desc ); -} - -template< typename NodeType > -static inline void fgt_node_desc( const NodeType *node, const char *desc ) { - void *addr = (void *)( static_cast< sender< typename NodeType::output_type > * >(const_cast< NodeType *>(node)) ); - itt_metadata_str_add( ITT_DOMAIN_FLOW, addr, FLOW_NODE, FLOW_OBJECT_NAME, desc ); -} - -static inline void fgt_graph_desc( const void *g, const char *desc ) { - void *addr = const_cast< void *>(g); - itt_metadata_str_add( ITT_DOMAIN_FLOW, addr, FLOW_GRAPH, FLOW_OBJECT_NAME, desc ); -} - -static inline void fgt_body( void *node, void *body ) { - itt_relation_add( ITT_DOMAIN_FLOW, body, FLOW_BODY, __itt_relation_is_child_of, node, FLOW_NODE ); -} - -template< int N, typename PortsTuple > -static inline void fgt_multioutput_node(void* codeptr, string_resource_index t, void *g, void *input_port, PortsTuple &ports ) { - itt_make_task_group( ITT_DOMAIN_FLOW, input_port, FLOW_NODE, g, FLOW_GRAPH, t ); - fgt_internal_create_input_port( input_port, input_port, FLOW_INPUT_PORT_0 ); - fgt_internal_output_helper::register_port(codeptr, input_port, ports ); -} - -template< int N, typename PortsTuple > -static inline void fgt_multioutput_node_with_body( void* codeptr, string_resource_index t, void *g, void *input_port, PortsTuple &ports, void *body ) { - itt_make_task_group( ITT_DOMAIN_FLOW, input_port, FLOW_NODE, g, FLOW_GRAPH, t ); - fgt_internal_create_input_port( input_port, input_port, FLOW_INPUT_PORT_0 ); - fgt_internal_output_helper::register_port( codeptr, input_port, ports ); - fgt_body( input_port, body ); -} - -template< int N, typename PortsTuple > -static inline void fgt_multiinput_node( void* codeptr, string_resource_index t, void *g, PortsTuple &ports, void *output_port) { - itt_make_task_group( ITT_DOMAIN_FLOW, output_port, FLOW_NODE, g, FLOW_GRAPH, t ); - fgt_internal_create_output_port( codeptr, output_port, output_port, FLOW_OUTPUT_PORT_0 ); - fgt_internal_input_helper::register_port( output_port, ports ); -} - -static inline void fgt_multiinput_multioutput_node( void* codeptr, string_resource_index t, void *n, void *g ) { - itt_make_task_group( ITT_DOMAIN_FLOW, n, FLOW_NODE, g, FLOW_GRAPH, t ); - suppress_unused_warning( codeptr ); -#if __TBB_FLOW_TRACE_CODEPTR - if (codeptr != nullptr) { - register_node_addr(ITT_DOMAIN_FLOW, n, FLOW_NODE, CODE_ADDRESS, &codeptr); - } -#endif -} - -static inline void fgt_node( void* codeptr, string_resource_index t, void *g, void *output_port ) { - itt_make_task_group( ITT_DOMAIN_FLOW, output_port, FLOW_NODE, g, FLOW_GRAPH, t ); - fgt_internal_create_output_port( codeptr, output_port, output_port, FLOW_OUTPUT_PORT_0 ); -} - -static void fgt_node_with_body( void* codeptr, string_resource_index t, void *g, void *output_port, void *body ) { - itt_make_task_group( ITT_DOMAIN_FLOW, output_port, FLOW_NODE, g, FLOW_GRAPH, t ); - fgt_internal_create_output_port(codeptr, output_port, output_port, FLOW_OUTPUT_PORT_0 ); - fgt_body( output_port, body ); -} - -static inline void fgt_node( void* codeptr, string_resource_index t, void *g, void *input_port, void *output_port ) { - fgt_node( codeptr, t, g, output_port ); - fgt_internal_create_input_port( output_port, input_port, FLOW_INPUT_PORT_0 ); -} - -static inline void fgt_node_with_body( void* codeptr, string_resource_index t, void *g, void *input_port, void *output_port, void *body ) { - fgt_node_with_body( codeptr, t, g, output_port, body ); - fgt_internal_create_input_port( output_port, input_port, FLOW_INPUT_PORT_0 ); -} - - -static inline void fgt_node( void* codeptr, string_resource_index t, void *g, void *input_port, void *decrement_port, void *output_port ) { - fgt_node( codeptr, t, g, input_port, output_port ); - fgt_internal_create_input_port( output_port, decrement_port, FLOW_INPUT_PORT_1 ); -} - -static inline void fgt_make_edge( void *output_port, void *input_port ) { - itt_relation_add( ITT_DOMAIN_FLOW, output_port, FLOW_OUTPUT_PORT, __itt_relation_is_predecessor_to, input_port, FLOW_INPUT_PORT); -} - -static inline void fgt_remove_edge( void *output_port, void *input_port ) { - itt_relation_add( ITT_DOMAIN_FLOW, output_port, FLOW_OUTPUT_PORT, __itt_relation_is_sibling_of, input_port, FLOW_INPUT_PORT); -} - -static inline void fgt_graph( void *g ) { - itt_make_task_group( ITT_DOMAIN_FLOW, g, FLOW_GRAPH, nullptr, FLOW_NULL, FLOW_GRAPH ); -} - -static inline void fgt_begin_body( void *body ) { - itt_task_begin( ITT_DOMAIN_FLOW, body, FLOW_BODY, nullptr, FLOW_NULL, FLOW_BODY ); -} - -static inline void fgt_end_body( void * ) { - itt_task_end( ITT_DOMAIN_FLOW ); -} - -static inline void fgt_async_try_put_begin( void *node, void *port ) { - itt_task_begin( ITT_DOMAIN_FLOW, port, FLOW_OUTPUT_PORT, node, FLOW_NODE, FLOW_OUTPUT_PORT ); -} - -static inline void fgt_async_try_put_end( void *, void * ) { - itt_task_end( ITT_DOMAIN_FLOW ); -} - -static inline void fgt_async_reserve( void *node, void *graph ) { - itt_region_begin( ITT_DOMAIN_FLOW, node, FLOW_NODE, graph, FLOW_GRAPH, FLOW_NULL ); -} - -static inline void fgt_async_commit( void *node, void * /*graph*/) { - itt_region_end( ITT_DOMAIN_FLOW, node, FLOW_NODE ); -} - -static inline void fgt_reserve_wait( void *graph ) { - itt_region_begin( ITT_DOMAIN_FLOW, graph, FLOW_GRAPH, nullptr, FLOW_NULL, FLOW_NULL ); -} - -static inline void fgt_release_wait( void *graph ) { - itt_region_end( ITT_DOMAIN_FLOW, graph, FLOW_GRAPH ); -} - -#else // TBB_USE_PROFILING_TOOLS - -#define CODEPTR() nullptr - -static inline void fgt_alias_port(void * /*node*/, void * /*p*/, bool /*visible*/ ) { } - -static inline void fgt_composite ( void* /*codeptr*/, void * /*node*/, void * /*graph*/ ) { } - -static inline void fgt_graph( void * /*g*/ ) { } - -template< typename NodeType > -static inline void fgt_multioutput_node_desc( const NodeType * /*node*/, const char * /*desc*/ ) { } - -template< typename NodeType > -static inline void fgt_node_desc( const NodeType * /*node*/, const char * /*desc*/ ) { } - -static inline void fgt_graph_desc( const void * /*g*/, const char * /*desc*/ ) { } - -template< int N, typename PortsTuple > -static inline void fgt_multioutput_node( void* /*codeptr*/, string_resource_index /*t*/, void * /*g*/, void * /*input_port*/, PortsTuple & /*ports*/ ) { } - -template< int N, typename PortsTuple > -static inline void fgt_multioutput_node_with_body( void* /*codeptr*/, string_resource_index /*t*/, void * /*g*/, void * /*input_port*/, PortsTuple & /*ports*/, void * /*body*/ ) { } - -template< int N, typename PortsTuple > -static inline void fgt_multiinput_node( void* /*codeptr*/, string_resource_index /*t*/, void * /*g*/, PortsTuple & /*ports*/, void * /*output_port*/ ) { } - -static inline void fgt_multiinput_multioutput_node( void* /*codeptr*/, string_resource_index /*t*/, void * /*node*/, void * /*graph*/ ) { } - -static inline void fgt_node( void* /*codeptr*/, string_resource_index /*t*/, void * /*g*/, void * /*input_port*/, void * /*output_port*/ ) { } -static inline void fgt_node( void* /*codeptr*/, string_resource_index /*t*/, void * /*g*/, void * /*input_port*/, void * /*decrement_port*/, void * /*output_port*/ ) { } - -static inline void fgt_node_with_body( void* /*codeptr*/, string_resource_index /*t*/, void * /*g*/, void * /*output_port*/, void * /*body*/ ) { } -static inline void fgt_node_with_body( void* /*codeptr*/, string_resource_index /*t*/, void * /*g*/, void * /*input_port*/, void * /*output_port*/, void * /*body*/ ) { } - -static inline void fgt_make_edge( void * /*output_port*/, void * /*input_port*/ ) { } -static inline void fgt_remove_edge( void * /*output_port*/, void * /*input_port*/ ) { } - -static inline void fgt_begin_body( void * /*body*/ ) { } -static inline void fgt_end_body( void * /*body*/) { } - -static inline void fgt_async_try_put_begin( void * /*node*/, void * /*port*/ ) { } -static inline void fgt_async_try_put_end( void * /*node*/ , void * /*port*/ ) { } -static inline void fgt_async_reserve( void * /*node*/, void * /*graph*/ ) { } -static inline void fgt_async_commit( void * /*node*/, void * /*graph*/ ) { } -static inline void fgt_reserve_wait( void * /*graph*/ ) { } -static inline void fgt_release_wait( void * /*graph*/ ) { } - -template< typename NodeType > -void fgt_multiinput_multioutput_node_desc( const NodeType * /*node*/, const char * /*desc*/ ) { } - -template < typename PortsTuple, int N > -struct fgt_internal_input_alias_helper { - static void alias_port( void * /*node*/, PortsTuple & /*ports*/ ) { } -}; - -template < typename PortsTuple, int N > -struct fgt_internal_output_alias_helper { - static void alias_port( void * /*node*/, PortsTuple & /*ports*/ ) { } -}; - -#endif // TBB_USE_PROFILING_TOOLS - -} // d1 -} // namespace detail -} // namespace tbb - -#endif // _FGT_GRAPH_TRACE_IMPL_H diff --git a/include/oneapi/tbb/detail/_flow_graph_types_impl.h b/include/oneapi/tbb/detail/_flow_graph_types_impl.h deleted file mode 100644 index 4827551..0000000 --- a/include/oneapi/tbb/detail/_flow_graph_types_impl.h +++ /dev/null @@ -1,407 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__flow_graph_types_impl_H -#define __TBB__flow_graph_types_impl_H - -#ifndef __TBB_flow_graph_H -#error Do not #include this internal file directly; use public TBB headers instead. -#endif - -// included in namespace tbb::detail::d1 - -// the change to key_matching (adding a K and KHash template parameter, making it a class) -// means we have to pass this data to the key_matching_port. All the ports have only one -// template parameter, so we have to wrap the following types in a trait: -// -// . K == key_type -// . KHash == hash and compare for Key -// . TtoK == function_body that given an object of T, returns its K -// . T == type accepted by port, and stored in the hash table -// -// The port will have an additional parameter on node construction, which is a function_body -// that accepts a const T& and returns a K which is the field in T which is its K. -template -struct KeyTrait { - typedef Kp K; - typedef Tp T; - typedef type_to_key_function_body TtoK; - typedef KHashp KHash; -}; - -// wrap each element of a tuple in a template, and make a tuple of the result. -template class PT, typename TypeTuple> -struct wrap_tuple_elements; - -// A wrapper that generates the traits needed for each port of a key-matching join, -// and the type of the tuple of input ports. -template class PT, typename KeyTraits, typename TypeTuple> -struct wrap_key_tuple_elements; - -template class PT, typename... Args> -struct wrap_tuple_elements >{ - typedef typename std::tuple... > type; -}; - -template class PT, typename KeyTraits, typename... Args> -struct wrap_key_tuple_elements > { - typedef typename KeyTraits::key_type K; - typedef typename KeyTraits::hash_compare_type KHash; - typedef typename std::tuple >... > type; -}; - -template< int... S > class sequence {}; - -template< int N, int... S > -struct make_sequence : make_sequence < N - 1, N - 1, S... > {}; - -template< int... S > -struct make_sequence < 0, S... > { - typedef sequence type; -}; - -//! type mimicking std::pair but with trailing fill to ensure each element of an array -//* will have the correct alignment -template -struct type_plus_align { - char first[sizeof(T1)]; - T2 second; - char fill1[REM]; -}; - -template -struct type_plus_align { - char first[sizeof(T1)]; - T2 second; -}; - -template struct alignment_of { - typedef struct { char t; U padded; } test_alignment; - static const size_t value = sizeof(test_alignment) - sizeof(U); -}; - -// T1, T2 are actual types stored. The space defined for T1 in the type returned -// is a char array of the correct size. Type T2 should be trivially-constructible, -// T1 must be explicitly managed. -template -struct aligned_pair { - static const size_t t1_align = alignment_of::value; - static const size_t t2_align = alignment_of::value; - typedef type_plus_align just_pair; - static const size_t max_align = t1_align < t2_align ? t2_align : t1_align; - static const size_t extra_bytes = sizeof(just_pair) % max_align; - static const size_t remainder = extra_bytes ? max_align - extra_bytes : 0; -public: - typedef type_plus_align type; -}; // aligned_pair - -// support for variant type -// type we use when we're not storing a value -struct default_constructed { }; - -// type which contains another type, tests for what type is contained, and references to it. -// Wrapper -// void CopyTo( void *newSpace) : builds a Wrapper copy of itself in newSpace - -// struct to allow us to copy and test the type of objects -struct WrapperBase { - virtual ~WrapperBase() {} - virtual void CopyTo(void* /*newSpace*/) const = 0; -}; - -// Wrapper contains a T, with the ability to test what T is. The Wrapper can be -// constructed from a T, can be copy-constructed from another Wrapper, and can be -// examined via value(), but not modified. -template -struct Wrapper: public WrapperBase { - typedef T value_type; - typedef T* pointer_type; -private: - T value_space; -public: - const value_type &value() const { return value_space; } - -private: - Wrapper(); - - // on exception will ensure the Wrapper will contain only a trivially-constructed object - struct _unwind_space { - pointer_type space; - _unwind_space(pointer_type p) : space(p) {} - ~_unwind_space() { - if(space) (void) new (space) Wrapper(default_constructed()); - } - }; -public: - explicit Wrapper( const T& other ) : value_space(other) { } - explicit Wrapper(const Wrapper& other) = delete; - - void CopyTo(void* newSpace) const override { - _unwind_space guard((pointer_type)newSpace); - (void) new(newSpace) Wrapper(value_space); - guard.space = nullptr; - } - ~Wrapper() { } -}; - -// specialization for array objects -template -struct Wrapper : public WrapperBase { - typedef T value_type; - typedef T* pointer_type; - // space must be untyped. - typedef T ArrayType[N]; -private: - // The space is not of type T[N] because when copy-constructing, it would be - // default-initialized and then copied to in some fashion, resulting in two - // constructions and one destruction per element. If the type is char[ ], we - // placement new into each element, resulting in one construction per element. - static const size_t space_size = sizeof(ArrayType); - char value_space[space_size]; - - - // on exception will ensure the already-built objects will be destructed - // (the value_space is a char array, so it is already trivially-destructible.) - struct _unwind_class { - pointer_type space; - int already_built; - _unwind_class(pointer_type p) : space(p), already_built(0) {} - ~_unwind_class() { - if(space) { - for(size_t i = already_built; i > 0 ; --i ) space[i-1].~value_type(); - (void) new(space) Wrapper(default_constructed()); - } - } - }; -public: - const ArrayType &value() const { - char *vp = const_cast(value_space); - return reinterpret_cast(*vp); - } - -private: - Wrapper(); -public: - // have to explicitly construct because other decays to a const value_type* - explicit Wrapper(const ArrayType& other) { - _unwind_class guard((pointer_type)value_space); - pointer_type vp = reinterpret_cast(&value_space); - for(size_t i = 0; i < N; ++i ) { - (void) new(vp++) value_type(other[i]); - ++(guard.already_built); - } - guard.space = nullptr; - } - explicit Wrapper(const Wrapper& other) : WrapperBase() { - // we have to do the heavy lifting to copy contents - _unwind_class guard((pointer_type)value_space); - pointer_type dp = reinterpret_cast(value_space); - pointer_type sp = reinterpret_cast(const_cast(other.value_space)); - for(size_t i = 0; i < N; ++i, ++dp, ++sp) { - (void) new(dp) value_type(*sp); - ++(guard.already_built); - } - guard.space = nullptr; - } - - void CopyTo(void* newSpace) const override { - (void) new(newSpace) Wrapper(*this); // exceptions handled in copy constructor - } - - ~Wrapper() { - // have to destroy explicitly in reverse order - pointer_type vp = reinterpret_cast(&value_space); - for(size_t i = N; i > 0 ; --i ) vp[i-1].~value_type(); - } -}; - -// given a tuple, return the type of the element that has the maximum alignment requirement. -// Given a tuple and that type, return the number of elements of the object with the max -// alignment requirement that is at least as big as the largest object in the tuple. - -template struct pick_one; -template struct pick_one { typedef T1 type; }; -template struct pick_one { typedef T2 type; }; - -template< template class Selector, typename T1, typename T2 > -struct pick_max { - typedef typename pick_one< (Selector::value > Selector::value), T1, T2 >::type type; -}; - -template struct size_of { static const int value = sizeof(T); }; - -template< size_t N, class Tuple, template class Selector > struct pick_tuple_max { - typedef typename pick_tuple_max::type LeftMaxType; - typedef typename std::tuple_element::type ThisType; - typedef typename pick_max::type type; -}; - -template< class Tuple, template class Selector > struct pick_tuple_max<0, Tuple, Selector> { - typedef typename std::tuple_element<0, Tuple>::type type; -}; - -// is the specified type included in a tuple? -template -struct is_element_of { - typedef typename std::tuple_element::type T_i; - static const bool value = std::is_same::value || is_element_of::value; -}; - -template -struct is_element_of { - typedef typename std::tuple_element<0, Tuple>::type T_i; - static const bool value = std::is_same::value; -}; - -// allow the construction of types that are listed tuple. If a disallowed type -// construction is written, a method involving this type is created. The -// type has no definition, so a syntax error is generated. -template struct ERROR_Type_Not_allowed_In_Tagged_Msg_Not_Member_Of_Tuple; - -template struct do_if; -template -struct do_if { - static void construct(void *mySpace, const T& x) { - (void) new(mySpace) Wrapper(x); - } -}; -template -struct do_if { - static void construct(void * /*mySpace*/, const T& x) { - // This method is instantiated when the type T does not match any of the - // element types in the Tuple in variant. - ERROR_Type_Not_allowed_In_Tagged_Msg_Not_Member_Of_Tuple::bad_type(x); - } -}; - -// Tuple tells us the allowed types that variant can hold. It determines the alignment of the space in -// Wrapper, and how big Wrapper is. -// -// the object can only be tested for type, and a read-only reference can be fetched by cast_to(). - -using tbb::detail::punned_cast; -struct tagged_null_type {}; -template -class tagged_msg { - typedef std::tuple= 6 - , T5 - #endif - #if __TBB_VARIADIC_MAX >= 7 - , T6 - #endif - #if __TBB_VARIADIC_MAX >= 8 - , T7 - #endif - #if __TBB_VARIADIC_MAX >= 9 - , T8 - #endif - #if __TBB_VARIADIC_MAX >= 10 - , T9 - #endif - > Tuple; - -private: - class variant { - static const size_t N = std::tuple_size::value; - typedef typename pick_tuple_max::type AlignType; - typedef typename pick_tuple_max::type MaxSizeType; - static const size_t MaxNBytes = (sizeof(Wrapper)+sizeof(AlignType)-1); - static const size_t MaxNElements = MaxNBytes/sizeof(AlignType); - typedef aligned_space SpaceType; - SpaceType my_space; - static const size_t MaxSize = sizeof(SpaceType); - - public: - variant() { (void) new(&my_space) Wrapper(default_constructed()); } - - template - variant( const T& x ) { - do_if::value>::construct(&my_space,x); - } - - variant(const variant& other) { - const WrapperBase * h = punned_cast(&(other.my_space)); - h->CopyTo(&my_space); - } - - // assignment must destroy and re-create the Wrapper type, as there is no way - // to create a Wrapper-to-Wrapper assign even if we find they agree in type. - void operator=( const variant& rhs ) { - if(&rhs != this) { - WrapperBase *h = punned_cast(&my_space); - h->~WrapperBase(); - const WrapperBase *ch = punned_cast(&(rhs.my_space)); - ch->CopyTo(&my_space); - } - } - - template - const U& variant_cast_to() const { - const Wrapper *h = dynamic_cast*>(punned_cast(&my_space)); - if(!h) { - throw_exception(exception_id::bad_tagged_msg_cast); - } - return h->value(); - } - template - bool variant_is_a() const { return dynamic_cast*>(punned_cast(&my_space)) != nullptr; } - - bool variant_is_default_constructed() const {return variant_is_a();} - - ~variant() { - WrapperBase *h = punned_cast(&my_space); - h->~WrapperBase(); - } - }; //class variant - - TagType my_tag; - variant my_msg; - -public: - tagged_msg(): my_tag(TagType(~0)), my_msg(){} - - template - tagged_msg(T const &index, R const &value) : my_tag(index), my_msg(value) {} - - template - tagged_msg(T const &index, R (&value)[N]) : my_tag(index), my_msg(value) {} - - void set_tag(TagType const &index) {my_tag = index;} - TagType tag() const {return my_tag;} - - template - const V& cast_to() const {return my_msg.template variant_cast_to();} - - template - bool is_a() const {return my_msg.template variant_is_a();} - - bool is_default_constructed() const {return my_msg.variant_is_default_constructed();} -}; //class tagged_msg - -// template to simplify cast and test for tagged_msg in template contexts -template -const V& cast_to(T const &t) { return t.template cast_to(); } - -template -bool is_a(T const &t) { return t.template is_a(); } - -enum op_stat { WAIT = 0, SUCCEEDED, FAILED }; - -#endif /* __TBB__flow_graph_types_impl_H */ diff --git a/include/oneapi/tbb/detail/_hash_compare.h b/include/oneapi/tbb/detail/_hash_compare.h deleted file mode 100644 index 5439264..0000000 --- a/include/oneapi/tbb/detail/_hash_compare.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__hash_compare_H -#define __TBB_detail__hash_compare_H - -#include - -#include "_containers_helpers.h" - -namespace tbb { -namespace detail { -namespace d1 { - -template -class hash_compare { - using is_transparent_hash = has_transparent_key_equal; -public: - using hasher = Hash; - using key_equal = typename is_transparent_hash::type; - - hash_compare() = default; - hash_compare( hasher hash, key_equal equal ) : my_hasher(hash), my_equal(equal) {} - - std::size_t operator()( const Key& key ) const { - return std::size_t(my_hasher(key)); - } - - bool operator()( const Key& key1, const Key& key2 ) const { - return my_equal(key1, key2); - } - - template ::type> - std::size_t operator()( const K& key ) const { - return std::size_t(my_hasher(key)); - } - - template ::type> - bool operator()( const K1& key1, const K2& key2 ) const { - return my_equal(key1, key2); - } - - hasher hash_function() const { - return my_hasher; - } - - key_equal key_eq() const { - return my_equal; - } - - -private: - hasher my_hasher; - key_equal my_equal; -}; // class hash_compare - -//! hash_compare that is default argument for concurrent_hash_map -template -class tbb_hash_compare { -public: - std::size_t hash( const Key& a ) const { return my_hash_func(a); } -#if defined(_MSC_VER) && _MSC_VER <= 1900 -#pragma warning (push) -// MSVC 2015 throws a strange warning: 'std::size_t': forcing value to bool 'true' or 'false' -#pragma warning (disable: 4800) -#endif - bool equal( const Key& a, const Key& b ) const { return my_key_equal(a, b); } -#if defined(_MSC_VER) && _MSC_VER <= 1900 -#pragma warning (pop) -#endif -private: - std::hash my_hash_func; - std::equal_to my_key_equal; -}; - -} // namespace d1 -#if __TBB_CPP20_CONCEPTS_PRESENT -inline namespace d0 { - -template -concept hash_compare = std::copy_constructible && - requires( const std::remove_reference_t& hc, const Key& key1, const Key& key2 ) { - { hc.hash(key1) } -> std::same_as; - { hc.equal(key1, key2) } -> std::convertible_to; - }; - -} // namespace d0 -#endif // __TBB_CPP20_CONCEPTS_PRESENT -} // namespace detail -} // namespace tbb - -#if TBB_DEFINE_STD_HASH_SPECIALIZATIONS - -namespace std { - -template -struct hash> { -public: - std::size_t operator()( const std::pair& p ) const { - return first_hash(p.first) ^ second_hash(p.second); - } - -private: - std::hash first_hash; - std::hash second_hash; -}; // struct hash - -// Apple clang and MSVC defines their own specializations for std::hash> -#if !(_LIBCPP_VERSION) && !(_CPPLIB_VER) - -template -struct hash> { -public: - std::size_t operator()( const std::basic_string& s ) const { - std::size_t h = 0; - for ( const CharT* c = s.c_str(); *c; ++c ) { - h = h * hash_multiplier ^ char_hash(*c); - } - return h; - } - -private: - static constexpr std::size_t hash_multiplier = tbb::detail::select_size_t_constant<2654435769U, 11400714819323198485ULL>::value; - - std::hash char_hash; -}; // struct hash - -#endif // !(_LIBCPP_VERSION || _CPPLIB_VER) - -} // namespace std - -#endif // TBB_DEFINE_STD_HASH_SPECIALIZATIONS - -#endif // __TBB_detail__hash_compare_H diff --git a/include/oneapi/tbb/detail/_intrusive_list_node.h b/include/oneapi/tbb/detail/_intrusive_list_node.h deleted file mode 100644 index 69286c8..0000000 --- a/include/oneapi/tbb/detail/_intrusive_list_node.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef _TBB_detail__intrusive_list_node_H -#define _TBB_detail__intrusive_list_node_H - -namespace tbb { -namespace detail { -namespace d1 { - -//! Data structure to be inherited by the types that can form intrusive lists. -/** Intrusive list is formed by means of the member_intrusive_list template class. - Note that type T must derive from intrusive_list_node either publicly or - declare instantiation member_intrusive_list as a friend. - This class implements a limited subset of std::list interface. **/ -struct intrusive_list_node { - intrusive_list_node* my_prev_node{}; - intrusive_list_node* my_next_node{}; -#if TBB_USE_ASSERT - intrusive_list_node() { my_prev_node = my_next_node = this; } -#endif /* TBB_USE_ASSERT */ -}; - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // _TBB_detail__intrusive_list_node_H diff --git a/include/oneapi/tbb/detail/_machine.h b/include/oneapi/tbb/detail/_machine.h deleted file mode 100644 index 7a4a1e3..0000000 --- a/include/oneapi/tbb/detail/_machine.h +++ /dev/null @@ -1,373 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__machine_H -#define __TBB_detail__machine_H - -#include "_config.h" -#include "_assert.h" - -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#ifdef __TBBMALLOC_BUILD -#define WIN32_LEAN_AND_MEAN -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include // SwitchToThread() -#endif -#ifdef _MSC_VER -#if __TBB_x86_64 || __TBB_x86_32 -#pragma intrinsic(__rdtsc) -#endif -#endif -#endif -#if __TBB_x86_64 || __TBB_x86_32 -#include // _mm_pause -#endif -#if (_WIN32) -#include // _control87 -#endif - -#if __TBB_GLIBCXX_THIS_THREAD_YIELD_BROKEN -#include // sched_yield -#else -#include // std::this_thread::yield() -#endif - -namespace tbb { -namespace detail { -inline namespace d0 { - -//-------------------------------------------------------------------------------------------------- -// Yield implementation -//-------------------------------------------------------------------------------------------------- - -#if __TBB_GLIBCXX_THIS_THREAD_YIELD_BROKEN -static inline void yield() { - int err = sched_yield(); - __TBB_ASSERT_EX(err == 0, "sched_yield has failed"); -} -#elif __TBBMALLOC_BUILD && _WIN32 -// Use Windows API for yield in tbbmalloc to avoid dependency on C++ runtime with some implementations. -static inline void yield() { - SwitchToThread(); -} -#else -using std::this_thread::yield; -#endif - -//-------------------------------------------------------------------------------------------------- -// atomic_fence_seq_cst implementation -//-------------------------------------------------------------------------------------------------- - -static inline void atomic_fence_seq_cst() { -#if (__TBB_x86_64 || __TBB_x86_32) && defined(__GNUC__) && __GNUC__ < 11 - unsigned char dummy = 0u; - __asm__ __volatile__ ("lock; notb %0" : "+m" (dummy) :: "memory"); -#else - std::atomic_thread_fence(std::memory_order_seq_cst); -#endif -} - -//-------------------------------------------------------------------------------------------------- -// Pause implementation -//-------------------------------------------------------------------------------------------------- - -static inline void machine_pause(int32_t delay) { -#if __TBB_x86_64 || __TBB_x86_32 - while (delay-- > 0) { _mm_pause(); } -#elif __ARM_ARCH_7A__ || __aarch64__ - while (delay-- > 0) { __asm__ __volatile__("yield" ::: "memory"); } -#else /* Generic */ - (void)delay; // suppress without including _template_helpers.h - yield(); -#endif -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// tbb::detail::log2() implementation -//////////////////////////////////////////////////////////////////////////////////////////////////// -// TODO: Use log2p1() function that will be available in C++20 standard - -#if defined(__GNUC__) || defined(__clang__) -namespace gnu_builtins { - inline uintptr_t clz(unsigned int x) { return static_cast(__builtin_clz(x)); } - inline uintptr_t clz(unsigned long int x) { return static_cast(__builtin_clzl(x)); } - inline uintptr_t clz(unsigned long long int x) { return static_cast(__builtin_clzll(x)); } -} -#elif defined(_MSC_VER) -#pragma intrinsic(__TBB_W(_BitScanReverse)) -namespace msvc_intrinsics { - static inline uintptr_t bit_scan_reverse(uintptr_t i) { - unsigned long j; - __TBB_W(_BitScanReverse)( &j, i ); - return j; - } -} -#endif - -template -constexpr std::uintptr_t number_of_bits() { - return sizeof(T) * CHAR_BIT; -} - -// logarithm is the index of the most significant non-zero bit -static inline uintptr_t machine_log2(uintptr_t x) { -#if defined(__GNUC__) || defined(__clang__) - // If P is a power of 2 and x() - 1) ^ gnu_builtins::clz(x); -#elif defined(_MSC_VER) - return msvc_intrinsics::bit_scan_reverse(x); -#elif __i386__ || __i386 /*for Sun OS*/ || __MINGW32__ - uintptr_t j, i = x; - __asm__("bsr %1,%0" : "=r"(j) : "r"(i)); - return j; -#elif __powerpc__ || __POWERPC__ - #if __TBB_WORDSIZE==8 - __asm__ __volatile__ ("cntlzd %0,%0" : "+r"(x)); - return 63 - static_cast(x); - #else - __asm__ __volatile__ ("cntlzw %0,%0" : "+r"(x)); - return 31 - static_cast(x); - #endif /*__TBB_WORDSIZE*/ -#elif __sparc - uint64_t count; - // one hot encode - x |= (x >> 1); - x |= (x >> 2); - x |= (x >> 4); - x |= (x >> 8); - x |= (x >> 16); - x |= (x >> 32); - // count 1's - __asm__ ("popc %1, %0" : "=r"(count) : "r"(x) ); - return count - 1; -#else - intptr_t result = 0; - - if( sizeof(x) > 4 && (uintptr_t tmp = x >> 32) ) { x = tmp; result += 32; } - if( uintptr_t tmp = x >> 16 ) { x = tmp; result += 16; } - if( uintptr_t tmp = x >> 8 ) { x = tmp; result += 8; } - if( uintptr_t tmp = x >> 4 ) { x = tmp; result += 4; } - if( uintptr_t tmp = x >> 2 ) { x = tmp; result += 2; } - - return (x & 2) ? result + 1 : result; -#endif -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// tbb::detail::reverse_bits() implementation -//////////////////////////////////////////////////////////////////////////////////////////////////// -#if TBB_USE_CLANG_BITREVERSE_BUILTINS -namespace llvm_builtins { - inline uint8_t builtin_bitreverse(uint8_t x) { return __builtin_bitreverse8 (x); } - inline uint16_t builtin_bitreverse(uint16_t x) { return __builtin_bitreverse16(x); } - inline uint32_t builtin_bitreverse(uint32_t x) { return __builtin_bitreverse32(x); } - inline uint64_t builtin_bitreverse(uint64_t x) { return __builtin_bitreverse64(x); } -} -#else // generic -template -struct reverse { - static const T byte_table[256]; -}; - -template -const T reverse::byte_table[256] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF -}; - -inline unsigned char reverse_byte(unsigned char src) { - return reverse::byte_table[src]; -} -#endif // TBB_USE_CLANG_BITREVERSE_BUILTINS - -template -T machine_reverse_bits(T src) { -#if TBB_USE_CLANG_BITREVERSE_BUILTINS - return builtin_bitreverse(fixed_width_cast(src)); -#else /* Generic */ - T dst; - unsigned char *original = reinterpret_cast(&src); - unsigned char *reversed = reinterpret_cast(&dst); - - for ( int i = sizeof(T) - 1; i >= 0; i-- ) { - reversed[i] = reverse_byte( original[sizeof(T) - i - 1] ); - } - - return dst; -#endif // TBB_USE_CLANG_BITREVERSE_BUILTINS -} - -} // inline namespace d0 - -namespace d1 { - -#if (_WIN32) -// API to retrieve/update FPU control setting -#define __TBB_CPU_CTL_ENV_PRESENT 1 -struct cpu_ctl_env { - unsigned int x87cw{}; -#if (__TBB_x86_64) - // Changing the infinity mode or the floating-point precision is not supported on x64. - // The attempt causes an assertion. See - // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/control87-controlfp-control87-2 - static constexpr unsigned int X87CW_CONTROL_MASK = _MCW_DN | _MCW_EM | _MCW_RC; -#else - static constexpr unsigned int X87CW_CONTROL_MASK = ~0U; -#endif -#if (__TBB_x86_32 || __TBB_x86_64) - unsigned int mxcsr{}; - static constexpr unsigned int MXCSR_CONTROL_MASK = ~0x3fu; /* all except last six status bits */ -#endif - - bool operator!=( const cpu_ctl_env& ctl ) const { - return -#if (__TBB_x86_32 || __TBB_x86_64) - mxcsr != ctl.mxcsr || -#endif - x87cw != ctl.x87cw; - } - void get_env() { - x87cw = _control87(0, 0); -#if (__TBB_x86_32 || __TBB_x86_64) - mxcsr = _mm_getcsr(); -#endif - } - void set_env() const { - _control87(x87cw, X87CW_CONTROL_MASK); -#if (__TBB_x86_32 || __TBB_x86_64) - _mm_setcsr(mxcsr & MXCSR_CONTROL_MASK); -#endif - } -}; -#elif (__TBB_x86_32 || __TBB_x86_64) -// API to retrieve/update FPU control setting -#define __TBB_CPU_CTL_ENV_PRESENT 1 -struct cpu_ctl_env { - int mxcsr{}; - short x87cw{}; - static const int MXCSR_CONTROL_MASK = ~0x3f; /* all except last six status bits */ - - bool operator!=(const cpu_ctl_env& ctl) const { - return mxcsr != ctl.mxcsr || x87cw != ctl.x87cw; - } - void get_env() { - __asm__ __volatile__( - "stmxcsr %0\n\t" - "fstcw %1" - : "=m"(mxcsr), "=m"(x87cw) - ); - mxcsr &= MXCSR_CONTROL_MASK; - } - void set_env() const { - __asm__ __volatile__( - "ldmxcsr %0\n\t" - "fldcw %1" - : : "m"(mxcsr), "m"(x87cw) - ); - } -}; -#endif - -} // namespace d1 - -} // namespace detail -} // namespace tbb - -#if !__TBB_CPU_CTL_ENV_PRESENT -#include - -#include - -namespace tbb { -namespace detail { - -namespace r1 { -void* __TBB_EXPORTED_FUNC cache_aligned_allocate(std::size_t size); -void __TBB_EXPORTED_FUNC cache_aligned_deallocate(void* p); -} // namespace r1 - -namespace d1 { - -class cpu_ctl_env { - fenv_t *my_fenv_ptr; -public: - cpu_ctl_env() : my_fenv_ptr(nullptr) {} - ~cpu_ctl_env() { - if ( my_fenv_ptr ) - r1::cache_aligned_deallocate( (void*)my_fenv_ptr ); - } - // It is possible not to copy memory but just to copy pointers but the following issues should be addressed: - // 1. The arena lifetime and the context lifetime are independent; - // 2. The user is allowed to recapture different FPU settings to context so 'current FPU settings' inside - // dispatch loop may become invalid. - // But do we really want to improve the fenv implementation? It seems to be better to replace the fenv implementation - // with a platform specific implementation. - cpu_ctl_env( const cpu_ctl_env &src ) : my_fenv_ptr(nullptr) { - *this = src; - } - cpu_ctl_env& operator=( const cpu_ctl_env &src ) { - __TBB_ASSERT( src.my_fenv_ptr, nullptr); - if ( !my_fenv_ptr ) - my_fenv_ptr = (fenv_t*)r1::cache_aligned_allocate(sizeof(fenv_t)); - *my_fenv_ptr = *src.my_fenv_ptr; - return *this; - } - bool operator!=( const cpu_ctl_env &ctl ) const { - __TBB_ASSERT( my_fenv_ptr, "cpu_ctl_env is not initialized." ); - __TBB_ASSERT( ctl.my_fenv_ptr, "cpu_ctl_env is not initialized." ); - return std::memcmp( (void*)my_fenv_ptr, (void*)ctl.my_fenv_ptr, sizeof(fenv_t) ); - } - void get_env () { - if ( !my_fenv_ptr ) - my_fenv_ptr = (fenv_t*)r1::cache_aligned_allocate(sizeof(fenv_t)); - fegetenv( my_fenv_ptr ); - } - const cpu_ctl_env& set_env () const { - __TBB_ASSERT( my_fenv_ptr, "cpu_ctl_env is not initialized." ); - fesetenv( my_fenv_ptr ); - return *this; - } -}; - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif /* !__TBB_CPU_CTL_ENV_PRESENT */ - -#endif // __TBB_detail__machine_H diff --git a/include/oneapi/tbb/detail/_mutex_common.h b/include/oneapi/tbb/detail/_mutex_common.h deleted file mode 100644 index 4650c19..0000000 --- a/include/oneapi/tbb/detail/_mutex_common.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright (c) 2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__mutex_common_H -#define __TBB_detail__mutex_common_H - -#include "_config.h" -#include "_utils.h" - -#if __TBB_CPP20_CONCEPTS_PRESENT -#include - -namespace tbb { -namespace detail { -inline namespace d0 { - -template -concept mutex_scoped_lock = std::default_initializable && - std::constructible_from && - requires( Lock& lock, Mutex& mutex ) { - lock.acquire(mutex); - { lock.try_acquire(mutex) } -> adaptive_same_as; - lock.release(); - }; - -template -concept rw_mutex_scoped_lock = mutex_scoped_lock && - std::constructible_from && - requires( Lock& lock, Mutex& mutex ) { - lock.acquire(mutex, false); - { lock.try_acquire(mutex, false) } -> adaptive_same_as; - { lock.upgrade_to_writer() } -> adaptive_same_as; - { lock.downgrade_to_reader() } -> adaptive_same_as; - }; - -template -concept scoped_lockable = mutex_scoped_lock; - -template -concept rw_scoped_lockable = scoped_lockable && - rw_mutex_scoped_lock; - -} // namespace d0 -} // namespace detail -} // namespace tbb - -#endif // __TBB_CPP20_CONCEPTS_PRESENT -#endif // __TBB_detail__mutex_common_H diff --git a/include/oneapi/tbb/detail/_namespace_injection.h b/include/oneapi/tbb/detail/_namespace_injection.h deleted file mode 100644 index 2e1df30..0000000 --- a/include/oneapi/tbb/detail/_namespace_injection.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright (c) 2020-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -// All public entities of the OneAPI Spec are available under oneapi namespace - -// Define tbb namespace first as it might not be known yet -namespace tbb {} - -namespace oneapi { -namespace tbb = ::tbb; -} diff --git a/include/oneapi/tbb/detail/_node_handle.h b/include/oneapi/tbb/detail/_node_handle.h deleted file mode 100644 index 265be07..0000000 --- a/include/oneapi/tbb/detail/_node_handle.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - Copyright (c) 2019-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__node_handle_H -#define __TBB_detail__node_handle_H - -#include "_allocator_traits.h" -#include "_assert.h" - -namespace tbb { -namespace detail { -namespace d1 { - -// A structure to access private node handle methods in internal TBB classes -// Regular friend declaration is not convenient because classes which use node handle -// can be placed in the different versioning namespaces. -struct node_handle_accessor { - template - static typename NodeHandleType::node* get_node_ptr( NodeHandleType& nh ) { - return nh.get_node_ptr(); - } - - template - static NodeHandleType construct( typename NodeHandleType::node* node_ptr ) { - return NodeHandleType{node_ptr}; - } - - template - static void deactivate( NodeHandleType& nh ) { - nh.deactivate(); - } -}; // struct node_handle_accessor - -template -class node_handle_base { -public: - using allocator_type = Allocator; -protected: - using node = Node; - using allocator_traits_type = tbb::detail::allocator_traits; -public: - - node_handle_base() : my_node(nullptr), my_allocator() {} - node_handle_base(node_handle_base&& nh) : my_node(nh.my_node), - my_allocator(std::move(nh.my_allocator)) { - nh.my_node = nullptr; - } - - __TBB_nodiscard bool empty() const { return my_node == nullptr; } - explicit operator bool() const { return my_node != nullptr; } - - ~node_handle_base() { internal_destroy(); } - - node_handle_base& operator=( node_handle_base&& nh ) { - internal_destroy(); - my_node = nh.my_node; - move_assign_allocators(my_allocator, nh.my_allocator); - nh.deactivate(); - return *this; - } - - void swap( node_handle_base& nh ) { - using std::swap; - swap(my_node, nh.my_node); - swap_allocators(my_allocator, nh.my_allocator); - } - - allocator_type get_allocator() const { - return my_allocator; - } - -protected: - node_handle_base( node* n ) : my_node(n) {} - - void internal_destroy() { - if(my_node != nullptr) { - allocator_traits_type::destroy(my_allocator, my_node->storage()); - typename allocator_traits_type::template rebind_alloc node_allocator(my_allocator); - node_allocator.deallocate(my_node, 1); - } - } - - node* get_node_ptr() { return my_node; } - - void deactivate() { my_node = nullptr; } - - node* my_node; - allocator_type my_allocator; -}; - -// node handle for maps -template -class node_handle : public node_handle_base { - using base_type = node_handle_base; -public: - using key_type = Key; - using mapped_type = typename Value::second_type; - using allocator_type = typename base_type::allocator_type; - - node_handle() = default; - - key_type& key() const { - __TBB_ASSERT(!this->empty(), "Cannot get key from the empty node_type object"); - return *const_cast(&(this->my_node->value().first)); - } - - mapped_type& mapped() const { - __TBB_ASSERT(!this->empty(), "Cannot get mapped value from the empty node_type object"); - return this->my_node->value().second; - } - -private: - friend struct node_handle_accessor; - - node_handle( typename base_type::node* n ) : base_type(n) {} -}; // class node_handle - -// node handle for sets -template -class node_handle : public node_handle_base { - using base_type = node_handle_base; -public: - using value_type = Key; - using allocator_type = typename base_type::allocator_type; - - node_handle() = default; - - value_type& value() const { - __TBB_ASSERT(!this->empty(), "Cannot get value from the empty node_type object"); - return *const_cast(&(this->my_node->value())); - } - -private: - friend struct node_handle_accessor; - - node_handle( typename base_type::node* n ) : base_type(n) {} -}; // class node_handle - -template -void swap( node_handle& lhs, - node_handle& rhs ) { - return lhs.swap(rhs); -} - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__node_handle_H diff --git a/include/oneapi/tbb/detail/_pipeline_filters.h b/include/oneapi/tbb/detail/_pipeline_filters.h deleted file mode 100644 index 46e7b95..0000000 --- a/include/oneapi/tbb/detail/_pipeline_filters.h +++ /dev/null @@ -1,455 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_parallel_filters_H -#define __TBB_parallel_filters_H - -#include "_config.h" -#include "_task.h" -#include "_pipeline_filters_deduction.h" -#include "../tbb_allocator.h" - -#include -#include - -namespace tbb { -namespace detail { - -namespace d1 { -class base_filter; -} - -namespace r1 { -TBB_EXPORT void __TBB_EXPORTED_FUNC set_end_of_input(d1::base_filter&); -class pipeline; -class stage_task; -class input_buffer; -} - -namespace d1 { -class filter_node; - -//! A stage in a pipeline. -/** @ingroup algorithms */ -class base_filter{ -private: - //! Value used to mark "not in pipeline" - static base_filter* not_in_pipeline() { return reinterpret_cast(std::intptr_t(-1)); } -public: - //! The lowest bit 0 is for parallel vs serial - static constexpr unsigned int filter_is_serial = 0x1; - - //! 2nd bit distinguishes ordered vs unordered filters. - static constexpr unsigned int filter_is_out_of_order = 0x1<<1; - - //! 3rd bit marks input filters emitting small objects - static constexpr unsigned int filter_may_emit_null = 0x1<<2; - - base_filter(const base_filter&) = delete; - base_filter& operator=(const base_filter&) = delete; - -protected: - explicit base_filter( unsigned int m ) : - next_filter_in_pipeline(not_in_pipeline()), - my_input_buffer(nullptr), - my_filter_mode(m), - my_pipeline(nullptr) - {} - - // signal end-of-input for concrete_filters - void set_end_of_input() { - r1::set_end_of_input(*this); - } - -public: - //! True if filter is serial. - bool is_serial() const { - return bool( my_filter_mode & filter_is_serial ); - } - - //! True if filter must receive stream in order. - bool is_ordered() const { - return (my_filter_mode & filter_is_serial) && !(my_filter_mode & filter_is_out_of_order); - } - - //! true if an input filter can emit null - bool object_may_be_null() { - return ( my_filter_mode & filter_may_emit_null ) == filter_may_emit_null; - } - - //! Operate on an item from the input stream, and return item for output stream. - /** Returns nullptr if filter is a sink. */ - virtual void* operator()( void* item ) = 0; - - //! Destroy filter. - virtual ~base_filter() {}; - - //! Destroys item if pipeline was cancelled. - /** Required to prevent memory leaks. - Note it can be called concurrently even for serial filters.*/ - virtual void finalize( void* /*item*/ ) {} - -private: - //! Pointer to next filter in the pipeline. - base_filter* next_filter_in_pipeline; - - //! Buffer for incoming tokens, or nullptr if not required. - /** The buffer is required if the filter is serial. */ - r1::input_buffer* my_input_buffer; - - friend class r1::stage_task; - friend class r1::pipeline; - friend void r1::set_end_of_input(d1::base_filter&); - - //! Storage for filter mode and dynamically checked implementation version. - const unsigned int my_filter_mode; - - //! Pointer to the pipeline. - r1::pipeline* my_pipeline; -}; - -template -class concrete_filter; - -//! input_filter control to signal end-of-input for parallel_pipeline -class flow_control { - bool is_pipeline_stopped = false; - flow_control() = default; - template friend class concrete_filter; - template - __TBB_requires(std::copyable) - friend class input_node; -public: - void stop() { is_pipeline_stopped = true; } -}; - -// Emulate std::is_trivially_copyable (false positives not allowed, false negatives suboptimal but safe). -#if __TBB_CPP11_TYPE_PROPERTIES_PRESENT -template using tbb_trivially_copyable = std::is_trivially_copyable; -#else -template struct tbb_trivially_copyable { enum { value = false }; }; -template struct tbb_trivially_copyable < T* > { enum { value = true }; }; -template<> struct tbb_trivially_copyable < bool > { enum { value = true }; }; -template<> struct tbb_trivially_copyable < char > { enum { value = true }; }; -template<> struct tbb_trivially_copyable < signed char > { enum { value = true }; }; -template<> struct tbb_trivially_copyable { enum { value = true }; }; -template<> struct tbb_trivially_copyable < short > { enum { value = true }; }; -template<> struct tbb_trivially_copyable { enum { value = true }; }; -template<> struct tbb_trivially_copyable < int > { enum { value = true }; }; -template<> struct tbb_trivially_copyable { enum { value = true }; }; -template<> struct tbb_trivially_copyable < long > { enum { value = true }; }; -template<> struct tbb_trivially_copyable { enum { value = true }; }; -template<> struct tbb_trivially_copyable < long long> { enum { value = true }; }; -template<> struct tbb_trivially_copyable { enum { value = true }; }; -template<> struct tbb_trivially_copyable < float > { enum { value = true }; }; -template<> struct tbb_trivially_copyable < double > { enum { value = true }; }; -template<> struct tbb_trivially_copyable < long double > { enum { value = true }; }; -#endif // __TBB_CPP11_TYPE_PROPERTIES_PRESENT - -template -struct use_allocator { - static constexpr bool value = sizeof(T) > sizeof(void *) || !tbb_trivially_copyable::value; -}; - -// A helper class to customize how a type is passed between filters. -// Usage: token_helper::value> -template struct token_helper; - -// using tbb_allocator -template -struct token_helper { - using pointer = T*; - using value_type = T; - static pointer create_token(value_type && source) { - return new (r1::allocate_memory(sizeof(T))) T(std::move(source)); - } - static value_type & token(pointer & t) { return *t; } - static void * cast_to_void_ptr(pointer ref) { return reinterpret_cast(ref); } - static pointer cast_from_void_ptr(void * ref) { return reinterpret_cast(ref); } - static void destroy_token(pointer token) { - token->~value_type(); - r1::deallocate_memory(token); - } -}; - -// pointer specialization -template -struct token_helper { - using pointer = T*; - using value_type = T*; - static pointer create_token(const value_type & source) { return source; } - static value_type & token(pointer & t) { return t; } - static void * cast_to_void_ptr(pointer ref) { return reinterpret_cast(ref); } - static pointer cast_from_void_ptr(void * ref) { return reinterpret_cast(ref); } - static void destroy_token( pointer /*token*/) {} -}; - -// converting type to and from void*, passing objects directly -template -struct token_helper { - typedef union { - T actual_value; - void * void_overlay; - } type_to_void_ptr_map; - using pointer = T; // not really a pointer in this case. - using value_type = T; - static pointer create_token(const value_type & source) { return source; } - static value_type & token(pointer & t) { return t; } - static void * cast_to_void_ptr(pointer ref) { - type_to_void_ptr_map mymap; - mymap.void_overlay = nullptr; - mymap.actual_value = ref; - return mymap.void_overlay; - } - static pointer cast_from_void_ptr(void * ref) { - type_to_void_ptr_map mymap; - mymap.void_overlay = ref; - return mymap.actual_value; - } - static void destroy_token( pointer /*token*/) {} -}; - -// intermediate -template -class concrete_filter: public base_filter { - const Body& my_body; - using input_helper = token_helper::value>; - using input_pointer = typename input_helper::pointer; - using output_helper = token_helper::value>; - using output_pointer = typename output_helper::pointer; - - void* operator()(void* input) override { - input_pointer temp_input = input_helper::cast_from_void_ptr(input); - output_pointer temp_output = output_helper::create_token(tbb::detail::invoke(my_body, std::move(input_helper::token(temp_input)))); - input_helper::destroy_token(temp_input); - return output_helper::cast_to_void_ptr(temp_output); - } - - void finalize(void * input) override { - input_pointer temp_input = input_helper::cast_from_void_ptr(input); - input_helper::destroy_token(temp_input); - } - -public: - concrete_filter(unsigned int m, const Body& body) : base_filter(m), my_body(body) {} -}; - -// input -template -class concrete_filter: public base_filter { - const Body& my_body; - using output_helper = token_helper::value>; - using output_pointer = typename output_helper::pointer; - - void* operator()(void*) override { - flow_control control; - output_pointer temp_output = output_helper::create_token(my_body(control)); - if(control.is_pipeline_stopped) { - output_helper::destroy_token(temp_output); - set_end_of_input(); - return nullptr; - } - return output_helper::cast_to_void_ptr(temp_output); - } - -public: - concrete_filter(unsigned int m, const Body& body) : - base_filter(m | filter_may_emit_null), - my_body(body) - {} -}; - -// output -template -class concrete_filter: public base_filter { - const Body& my_body; - using input_helper = token_helper::value>; - using input_pointer = typename input_helper::pointer; - - void* operator()(void* input) override { - input_pointer temp_input = input_helper::cast_from_void_ptr(input); - tbb::detail::invoke(my_body, std::move(input_helper::token(temp_input))); - input_helper::destroy_token(temp_input); - return nullptr; - } - void finalize(void* input) override { - input_pointer temp_input = input_helper::cast_from_void_ptr(input); - input_helper::destroy_token(temp_input); - } - -public: - concrete_filter(unsigned int m, const Body& body) : base_filter(m), my_body(body) {} -}; - -template -class concrete_filter: public base_filter { - const Body& my_body; - - void* operator()(void*) override { - flow_control control; - my_body(control); - void* output = control.is_pipeline_stopped ? nullptr : (void*)(std::intptr_t)-1; - return output; - } -public: - concrete_filter(unsigned int m, const Body& body) : base_filter(m), my_body(body) {} -}; - -class filter_node_ptr { - filter_node * my_node; - -public: - filter_node_ptr() : my_node(nullptr) {} - filter_node_ptr(filter_node *); - ~filter_node_ptr(); - filter_node_ptr(const filter_node_ptr &); - filter_node_ptr(filter_node_ptr &&); - void operator=(filter_node *); - void operator=(const filter_node_ptr &); - void operator=(filter_node_ptr &&); - filter_node& operator*() const; - operator bool() const; -}; - -//! Abstract base class that represents a node in a parse tree underlying a filter class. -/** These nodes are always heap-allocated and can be shared by filter objects. */ -class filter_node { - /** Count must be atomic because it is hidden state for user, but might be shared by threads. */ - std::atomic ref_count; -public: - filter_node_ptr left; - filter_node_ptr right; -protected: - filter_node() : ref_count(0), left(nullptr), right(nullptr) { -#ifdef __TBB_TEST_FILTER_NODE_COUNT - ++(__TBB_TEST_FILTER_NODE_COUNT); -#endif - } -public: - filter_node(const filter_node_ptr& x, const filter_node_ptr& y) : filter_node(){ - left = x; - right = y; - } - filter_node(const filter_node&) = delete; - filter_node& operator=(const filter_node&) = delete; - - //! Add concrete_filter to pipeline - virtual base_filter* create_filter() const { - __TBB_ASSERT(false, "method of non-leaf was called"); - return nullptr; - } - - //! Increment reference count - void add_ref() { ref_count.fetch_add(1, std::memory_order_relaxed); } - - //! Decrement reference count and delete if it becomes zero. - void remove_ref() { - __TBB_ASSERT(ref_count>0,"ref_count underflow"); - if( ref_count.fetch_sub(1, std::memory_order_relaxed) == 1 ) { - this->~filter_node(); - r1::deallocate_memory(this); - } - } - - virtual ~filter_node() { -#ifdef __TBB_TEST_FILTER_NODE_COUNT - --(__TBB_TEST_FILTER_NODE_COUNT); -#endif - } -}; - -inline filter_node_ptr::filter_node_ptr(filter_node * nd) : my_node(nd) { - if (my_node) { - my_node->add_ref(); - } -} - -inline filter_node_ptr::~filter_node_ptr() { - if (my_node) { - my_node->remove_ref(); - } -} - -inline filter_node_ptr::filter_node_ptr(const filter_node_ptr & rhs) : my_node(rhs.my_node) { - if (my_node) { - my_node->add_ref(); - } -} - -inline filter_node_ptr::filter_node_ptr(filter_node_ptr && rhs) : my_node(rhs.my_node) { - rhs.my_node = nullptr; -} - -inline void filter_node_ptr::operator=(filter_node * rhs) { - // Order of operations below carefully chosen so that reference counts remain correct - // in unlikely event that remove_ref throws exception. - filter_node* old = my_node; - my_node = rhs; - if (my_node) { - my_node->add_ref(); - } - if (old) { - old->remove_ref(); - } -} - -inline void filter_node_ptr::operator=(const filter_node_ptr & rhs) { - *this = rhs.my_node; -} - -inline void filter_node_ptr::operator=(filter_node_ptr && rhs) { - filter_node* old = my_node; - my_node = rhs.my_node; - rhs.my_node = nullptr; - if (old) { - old->remove_ref(); - } -} - -inline filter_node& filter_node_ptr::operator*() const{ - __TBB_ASSERT(my_node,"nullptr node is used"); - return *my_node; -} - -inline filter_node_ptr::operator bool() const { - return my_node != nullptr; -} - -//! Node in parse tree representing result of make_filter. -template -class filter_node_leaf: public filter_node { - const unsigned int my_mode; - const Body my_body; - base_filter* create_filter() const override { - return new(r1::allocate_memory(sizeof(concrete_filter))) concrete_filter(my_mode,my_body); - } -public: - filter_node_leaf( unsigned int m, const Body& b ) : my_mode(m), my_body(b) {} -}; - - -template ::input_type> -using filter_input = typename std::conditional::value, void, Input>::type; - -template -using filter_output = typename filter_body_types::output_type; - -} // namespace d1 -} // namespace detail -} // namespace tbb - - -#endif /* __TBB_parallel_filters_H */ diff --git a/include/oneapi/tbb/detail/_pipeline_filters_deduction.h b/include/oneapi/tbb/detail/_pipeline_filters_deduction.h deleted file mode 100644 index c1a6e8a..0000000 --- a/include/oneapi/tbb/detail/_pipeline_filters_deduction.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__pipeline_filters_deduction_H -#define __TBB__pipeline_filters_deduction_H - -#include "_config.h" -#include -#include - -namespace tbb { -namespace detail { -namespace d1 { - -template -struct declare_filter_types { - using input_type = typename std::remove_const::type>::type; - using output_type = typename std::remove_const::type>::type; -}; - -template struct filter_body_types; - -template -struct filter_body_types : declare_filter_types {}; - -template -struct filter_body_types : declare_filter_types {}; - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB__pipeline_filters_deduction_H diff --git a/include/oneapi/tbb/detail/_range_common.h b/include/oneapi/tbb/detail/_range_common.h deleted file mode 100644 index 1011f02..0000000 --- a/include/oneapi/tbb/detail/_range_common.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__range_common_H -#define __TBB_detail__range_common_H - -#include "_config.h" -#include "_utils.h" -#if __TBB_CPP20_CONCEPTS_PRESENT -#include -#endif -#include - -namespace tbb { -namespace detail { -inline namespace d0 { - -//! Dummy type that distinguishes splitting constructor from copy constructor. -/** - * See description of parallel_for and parallel_reduce for example usages. - * @ingroup algorithms - */ -class split {}; - -//! Type enables transmission of splitting proportion from partitioners to range objects -/** - * In order to make use of such facility Range objects must implement - * splitting constructor with this type passed. - */ -class proportional_split : no_assign { -public: - proportional_split(size_t _left = 1, size_t _right = 1) : my_left(_left), my_right(_right) { } - - size_t left() const { return my_left; } - size_t right() const { return my_right; } - - // used when range does not support proportional split - explicit operator split() const { return split(); } - -private: - size_t my_left, my_right; -}; - -template -struct range_split_object_provider { - template - static split get( PartitionerSplitType& ) { return split(); } -}; - -template -struct range_split_object_provider::value>::type> { - template - static PartitionerSplitType& get( PartitionerSplitType& split_obj ) { return split_obj; } -}; - -template -auto get_range_split_object( PartitionerSplitType& split_obj ) --> decltype(range_split_object_provider::get(split_obj)) { - return range_split_object_provider::get(split_obj); -} - -template -using range_iterator_type = decltype(std::begin(std::declval())); - -#if __TBB_CPP20_CONCEPTS_PRESENT -template -using iterator_reference_type = typename std::iterator_traits::reference; - -template -using range_reference_type = iterator_reference_type>; - -template -concept blocked_range_value = std::copyable && - requires( const std::remove_reference_t& lhs, const std::remove_reference_t& rhs ) { - { lhs < rhs } -> relaxed_convertible_to; - { lhs - rhs } -> std::convertible_to; - { lhs + (rhs - lhs) } -> std::convertible_to; - }; - -template -concept splittable = std::constructible_from; - -template -concept tbb_range = std::copy_constructible && - splittable && - requires( const std::remove_reference_t& range ) { - { range.empty() } -> relaxed_convertible_to; - { range.is_divisible() } -> relaxed_convertible_to; - }; - -template -constexpr bool iterator_concept_helper( std::input_iterator_tag ) { - return std::input_iterator; -} - -template -constexpr bool iterator_concept_helper( std::random_access_iterator_tag ) { - return std::random_access_iterator; -} - -template -concept iterator_satisfies = requires (IteratorTag tag) { - requires iterator_concept_helper(tag); -}; - -template -concept container_based_sequence = requires( Sequence& seq ) { - { std::begin(seq) } -> iterator_satisfies; - { std::end(seq) } -> iterator_satisfies; -}; -#endif // __TBB_CPP20_CONCEPTS_PRESENT -} // namespace d0 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__range_common_H diff --git a/include/oneapi/tbb/detail/_rtm_mutex.h b/include/oneapi/tbb/detail/_rtm_mutex.h deleted file mode 100644 index 11583ba..0000000 --- a/include/oneapi/tbb/detail/_rtm_mutex.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__rtm_mutex_impl_H -#define __TBB__rtm_mutex_impl_H - -#include "_assert.h" -#include "_utils.h" -#include "../spin_mutex.h" - -#include "../profiling.h" - -namespace tbb { -namespace detail { -namespace r1 { -struct rtm_mutex_impl; -} -namespace d1 { - -#if _MSC_VER && !defined(__INTEL_COMPILER) - // Suppress warning: structure was padded due to alignment specifier - #pragma warning (push) - #pragma warning (disable: 4324) -#endif - -/** A rtm_mutex is an speculation-enabled spin mutex. - It should be used for locking short critical sections where the lock is - contended but the data it protects are not. If zero-initialized, the - mutex is considered unheld. - @ingroup synchronization */ -class alignas(max_nfs_size) rtm_mutex : private spin_mutex { -private: - enum class rtm_state { - rtm_none, - rtm_transacting, - rtm_real - }; -public: - //! Constructors - rtm_mutex() noexcept { - create_itt_sync(this, "tbb::speculative_spin_mutex", ""); - } - - //! Destructor - ~rtm_mutex() = default; - - //! Represents acquisition of a mutex. - class scoped_lock { - public: - friend class rtm_mutex; - //! Construct lock that has not acquired a mutex. - /** Equivalent to zero-initialization of *this. */ - constexpr scoped_lock() : m_mutex(nullptr), m_transaction_state(rtm_state::rtm_none) {} - - //! Acquire lock on given mutex. - scoped_lock(rtm_mutex& m) : m_mutex(nullptr), m_transaction_state(rtm_state::rtm_none) { - acquire(m); - } - - //! Release lock (if lock is held). - ~scoped_lock() { - if(m_transaction_state != rtm_state::rtm_none) { - release(); - } - } - - //! No Copy - scoped_lock(const scoped_lock&) = delete; - scoped_lock& operator=(const scoped_lock&) = delete; - - //! Acquire lock on given mutex. - void acquire(rtm_mutex& m); - - //! Try acquire lock on given mutex. - bool try_acquire(rtm_mutex& m); - - //! Release lock - void release(); - - private: - rtm_mutex* m_mutex; - rtm_state m_transaction_state; - friend r1::rtm_mutex_impl; - }; - - //! Mutex traits - static constexpr bool is_rw_mutex = false; - static constexpr bool is_recursive_mutex = false; - static constexpr bool is_fair_mutex = false; -private: - friend r1::rtm_mutex_impl; -}; // end of rtm_mutex -} // namespace d1 - -namespace r1 { - //! Internal acquire lock. - // only_speculate == true if we're doing a try_lock, else false. - TBB_EXPORT void __TBB_EXPORTED_FUNC acquire(d1::rtm_mutex&, d1::rtm_mutex::scoped_lock&, bool only_speculate = false); - //! Internal try_acquire lock. - TBB_EXPORT bool __TBB_EXPORTED_FUNC try_acquire(d1::rtm_mutex&, d1::rtm_mutex::scoped_lock&); - //! Internal release lock. - TBB_EXPORT void __TBB_EXPORTED_FUNC release(d1::rtm_mutex::scoped_lock&); -} // namespace r1 - -namespace d1 { -//! Acquire lock on given mutex. -inline void rtm_mutex::scoped_lock::acquire(rtm_mutex& m) { - __TBB_ASSERT(!m_mutex, "lock is already acquired"); - r1::acquire(m, *this); -} - -//! Try acquire lock on given mutex. -inline bool rtm_mutex::scoped_lock::try_acquire(rtm_mutex& m) { - __TBB_ASSERT(!m_mutex, "lock is already acquired"); - return r1::try_acquire(m, *this); -} - -//! Release lock -inline void rtm_mutex::scoped_lock::release() { - __TBB_ASSERT(m_mutex, "lock is not acquired"); - __TBB_ASSERT(m_transaction_state != rtm_state::rtm_none, "lock is not acquired"); - return r1::release(*this); -} - -#if _MSC_VER && !defined(__INTEL_COMPILER) - #pragma warning (pop) // 4324 warning -#endif - -#if TBB_USE_PROFILING_TOOLS -inline void set_name(rtm_mutex& obj, const char* name) { - itt_set_sync_name(&obj, name); -} -#if (_WIN32||_WIN64) -inline void set_name(rtm_mutex& obj, const wchar_t* name) { - itt_set_sync_name(&obj, name); -} -#endif // WIN -#else -inline void set_name(rtm_mutex&, const char*) {} -#if (_WIN32||_WIN64) -inline void set_name(rtm_mutex&, const wchar_t*) {} -#endif // WIN -#endif - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif /* __TBB__rtm_mutex_impl_H */ diff --git a/include/oneapi/tbb/detail/_rtm_rw_mutex.h b/include/oneapi/tbb/detail/_rtm_rw_mutex.h deleted file mode 100644 index 25a8fc1..0000000 --- a/include/oneapi/tbb/detail/_rtm_rw_mutex.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__rtm_rw_mutex_H -#define __TBB_detail__rtm_rw_mutex_H - -#include "_assert.h" -#include "_utils.h" -#include "../spin_rw_mutex.h" - -#include - -namespace tbb { -namespace detail { - -namespace r1 { -struct rtm_rw_mutex_impl; -} - -namespace d1 { - -constexpr std::size_t speculation_granularity = 64; -#if _MSC_VER && !defined(__INTEL_COMPILER) - // Suppress warning: structure was padded due to alignment specifier - #pragma warning (push) - #pragma warning (disable: 4324) -#endif - -//! Fast, unfair, spinning speculation-enabled reader-writer lock with backoff and writer-preference -/** @ingroup synchronization */ -class alignas(max_nfs_size) rtm_rw_mutex : private spin_rw_mutex { - friend struct r1::rtm_rw_mutex_impl; -private: - enum class rtm_type { - rtm_not_in_mutex, - rtm_transacting_reader, - rtm_transacting_writer, - rtm_real_reader, - rtm_real_writer - }; -public: - //! Constructors - rtm_rw_mutex() noexcept : write_flag(false) { - create_itt_sync(this, "tbb::speculative_spin_rw_mutex", ""); - } - - //! Destructor - ~rtm_rw_mutex() = default; - - //! Represents acquisition of a mutex. - class scoped_lock { - friend struct r1::rtm_rw_mutex_impl; - public: - //! Construct lock that has not acquired a mutex. - /** Equivalent to zero-initialization of *this. */ - constexpr scoped_lock() : m_mutex(nullptr), m_transaction_state(rtm_type::rtm_not_in_mutex) {} - - //! Acquire lock on given mutex. - scoped_lock(rtm_rw_mutex& m, bool write = true) : m_mutex(nullptr), m_transaction_state(rtm_type::rtm_not_in_mutex) { - acquire(m, write); - } - - //! Release lock (if lock is held). - ~scoped_lock() { - if(m_transaction_state != rtm_type::rtm_not_in_mutex) { - release(); - } - } - - //! No Copy - scoped_lock(const scoped_lock&) = delete; - scoped_lock& operator=(const scoped_lock&) = delete; - - //! Acquire lock on given mutex. - inline void acquire(rtm_rw_mutex& m, bool write = true); - - //! Try acquire lock on given mutex. - inline bool try_acquire(rtm_rw_mutex& m, bool write = true); - - //! Release lock - inline void release(); - - //! Upgrade reader to become a writer. - /** Returns whether the upgrade happened without releasing and re-acquiring the lock */ - inline bool upgrade_to_writer(); - - //! Downgrade writer to become a reader. - inline bool downgrade_to_reader(); - - inline bool is_writer() const; - private: - rtm_rw_mutex* m_mutex; - rtm_type m_transaction_state; - }; - - //! Mutex traits - static constexpr bool is_rw_mutex = true; - static constexpr bool is_recursive_mutex = false; - static constexpr bool is_fair_mutex = false; - -private: - alignas(speculation_granularity) std::atomic write_flag; -}; - -#if _MSC_VER && !defined(__INTEL_COMPILER) - #pragma warning (pop) // 4324 warning -#endif - -} // namespace d1 - -namespace r1 { - //! Internal acquire write lock. - // only_speculate == true if we're doing a try_lock, else false. - TBB_EXPORT void __TBB_EXPORTED_FUNC acquire_writer(d1::rtm_rw_mutex&, d1::rtm_rw_mutex::scoped_lock&, bool only_speculate = false); - //! Internal acquire read lock. - // only_speculate == true if we're doing a try_lock, else false. - TBB_EXPORT void __TBB_EXPORTED_FUNC acquire_reader(d1::rtm_rw_mutex&, d1::rtm_rw_mutex::scoped_lock&, bool only_speculate = false); - //! Internal upgrade reader to become a writer. - TBB_EXPORT bool __TBB_EXPORTED_FUNC upgrade(d1::rtm_rw_mutex::scoped_lock&); - //! Internal downgrade writer to become a reader. - TBB_EXPORT bool __TBB_EXPORTED_FUNC downgrade(d1::rtm_rw_mutex::scoped_lock&); - //! Internal try_acquire write lock. - TBB_EXPORT bool __TBB_EXPORTED_FUNC try_acquire_writer(d1::rtm_rw_mutex&, d1::rtm_rw_mutex::scoped_lock&); - //! Internal try_acquire read lock. - TBB_EXPORT bool __TBB_EXPORTED_FUNC try_acquire_reader(d1::rtm_rw_mutex&, d1::rtm_rw_mutex::scoped_lock&); - //! Internal release lock. - TBB_EXPORT void __TBB_EXPORTED_FUNC release(d1::rtm_rw_mutex::scoped_lock&); -} - -namespace d1 { -//! Acquire lock on given mutex. -void rtm_rw_mutex::scoped_lock::acquire(rtm_rw_mutex& m, bool write) { - __TBB_ASSERT(!m_mutex, "lock is already acquired"); - if (write) { - r1::acquire_writer(m, *this); - } else { - r1::acquire_reader(m, *this); - } -} - -//! Try acquire lock on given mutex. -bool rtm_rw_mutex::scoped_lock::try_acquire(rtm_rw_mutex& m, bool write) { - __TBB_ASSERT(!m_mutex, "lock is already acquired"); - if (write) { - return r1::try_acquire_writer(m, *this); - } else { - return r1::try_acquire_reader(m, *this); - } -} - -//! Release lock -void rtm_rw_mutex::scoped_lock::release() { - __TBB_ASSERT(m_mutex, "lock is not acquired"); - __TBB_ASSERT(m_transaction_state != rtm_type::rtm_not_in_mutex, "lock is not acquired"); - return r1::release(*this); -} - -//! Upgrade reader to become a writer. -/** Returns whether the upgrade happened without releasing and re-acquiring the lock */ -bool rtm_rw_mutex::scoped_lock::upgrade_to_writer() { - __TBB_ASSERT(m_mutex, "lock is not acquired"); - if (m_transaction_state == rtm_type::rtm_transacting_writer || m_transaction_state == rtm_type::rtm_real_writer) { - return true; // Already a writer - } - return r1::upgrade(*this); -} - -//! Downgrade writer to become a reader. -bool rtm_rw_mutex::scoped_lock::downgrade_to_reader() { - __TBB_ASSERT(m_mutex, "lock is not acquired"); - if (m_transaction_state == rtm_type::rtm_transacting_reader || m_transaction_state == rtm_type::rtm_real_reader) { - return true; // Already a reader - } - return r1::downgrade(*this); -} - -bool rtm_rw_mutex::scoped_lock::is_writer() const { - __TBB_ASSERT(m_mutex, "lock is not acquired"); - return m_transaction_state == rtm_type::rtm_transacting_writer || m_transaction_state == rtm_type::rtm_real_writer; -} - -#if TBB_USE_PROFILING_TOOLS -inline void set_name(rtm_rw_mutex& obj, const char* name) { - itt_set_sync_name(&obj, name); -} -#if (_WIN32||_WIN64) -inline void set_name(rtm_rw_mutex& obj, const wchar_t* name) { - itt_set_sync_name(&obj, name); -} -#endif // WIN -#else -inline void set_name(rtm_rw_mutex&, const char*) {} -#if (_WIN32||_WIN64) -inline void set_name(rtm_rw_mutex&, const wchar_t*) {} -#endif // WIN -#endif - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__rtm_rw_mutex_H diff --git a/include/oneapi/tbb/detail/_scoped_lock.h b/include/oneapi/tbb/detail/_scoped_lock.h deleted file mode 100644 index a49dcdf..0000000 --- a/include/oneapi/tbb/detail/_scoped_lock.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail_scoped_lock_H -#define __TBB_detail_scoped_lock_H - -namespace tbb { -namespace detail { -namespace d1 { - -// unique_scoped_lock supposes that Mutex operations never throw -template -class unique_scoped_lock { - //! Points to currently held Mutex, or nullptr if no lock is held. - Mutex* m_mutex{}; - -public: - //! Construct without acquiring a Mutex. - constexpr unique_scoped_lock() noexcept : m_mutex(nullptr) {} - - //! Construct and acquire lock on a Mutex. - unique_scoped_lock(Mutex& m) { - acquire(m); - } - - //! No Copy - unique_scoped_lock(const unique_scoped_lock&) = delete; - unique_scoped_lock& operator=(const unique_scoped_lock&) = delete; - - //! Acquire lock. - void acquire(Mutex& m) { - __TBB_ASSERT(m_mutex == nullptr, "The mutex is already acquired"); - m_mutex = &m; - m.lock(); - } - - //! Try acquiring lock (non-blocking) - /** Return true if lock acquired; false otherwise. */ - bool try_acquire(Mutex& m) { - __TBB_ASSERT(m_mutex == nullptr, "The mutex is already acquired"); - bool succeed = m.try_lock(); - if (succeed) { - m_mutex = &m; - } - return succeed; - } - - //! Release lock - void release() { - __TBB_ASSERT(m_mutex, "release on Mutex::unique_scoped_lock that is not holding a lock"); - m_mutex->unlock(); - m_mutex = nullptr; - } - - //! Destroy lock. If holding a lock, releases the lock first. - ~unique_scoped_lock() { - if (m_mutex) { - release(); - } - } -}; - -// rw_scoped_lock supposes that Mutex operations never throw -template -class rw_scoped_lock { -public: - //! Construct lock that has not acquired a mutex. - /** Equivalent to zero-initialization of *this. */ - constexpr rw_scoped_lock() noexcept {} - - //! Acquire lock on given mutex. - rw_scoped_lock(Mutex& m, bool write = true) { - acquire(m, write); - } - - //! Release lock (if lock is held). - ~rw_scoped_lock() { - if (m_mutex) { - release(); - } - } - - //! No Copy - rw_scoped_lock(const rw_scoped_lock&) = delete; - rw_scoped_lock& operator=(const rw_scoped_lock&) = delete; - - //! Acquire lock on given mutex. - void acquire(Mutex& m, bool write = true) { - __TBB_ASSERT(m_mutex == nullptr, "The mutex is already acquired"); - m_is_writer = write; - m_mutex = &m; - if (write) { - m_mutex->lock(); - } else { - m_mutex->lock_shared(); - } - } - - //! Try acquire lock on given mutex. - bool try_acquire(Mutex& m, bool write = true) { - bool succeed = write ? m.try_lock() : m.try_lock_shared(); - if (succeed) { - m_mutex = &m; - m_is_writer = write; - } - return succeed; - } - - //! Release lock. - void release() { - __TBB_ASSERT(m_mutex != nullptr, "The mutex is not acquired"); - Mutex* m = m_mutex; - m_mutex = nullptr; - - if (m_is_writer) { - m->unlock(); - } else { - m->unlock_shared(); - } - } - - //! Upgrade reader to become a writer. - /** Returns whether the upgrade happened without releasing and re-acquiring the lock */ - bool upgrade_to_writer() { - __TBB_ASSERT(m_mutex != nullptr, "The mutex is not acquired"); - if (m_is_writer) { - return true; // Already a writer - } - m_is_writer = true; - return m_mutex->upgrade(); - } - - //! Downgrade writer to become a reader. - bool downgrade_to_reader() { - __TBB_ASSERT(m_mutex != nullptr, "The mutex is not acquired"); - if (m_is_writer) { - m_mutex->downgrade(); - m_is_writer = false; - } - return true; - } - - bool is_writer() const { - __TBB_ASSERT(m_mutex != nullptr, "The mutex is not acquired"); - return m_is_writer; - } - -protected: - //! The pointer to the current mutex that is held, or nullptr if no mutex is held. - Mutex* m_mutex {nullptr}; - - //! If mutex != nullptr, then is_writer is true if holding a writer lock, false if holding a reader lock. - /** Not defined if not holding a lock. */ - bool m_is_writer {false}; -}; - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail_scoped_lock_H diff --git a/include/oneapi/tbb/detail/_segment_table.h b/include/oneapi/tbb/detail/_segment_table.h deleted file mode 100644 index 1a31d8a..0000000 --- a/include/oneapi/tbb/detail/_segment_table.h +++ /dev/null @@ -1,566 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__segment_table_H -#define __TBB_detail__segment_table_H - -#include "_config.h" -#include "_allocator_traits.h" -#include "_template_helpers.h" -#include "_utils.h" -#include "_assert.h" -#include "_exception.h" -#include -#include -#include -#include - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -#pragma warning(push) -#pragma warning(disable: 4127) // warning C4127: conditional expression is constant -#endif - -namespace tbb { -namespace detail { -namespace d1 { - -template -class segment_table { -public: - using value_type = T; - using segment_type = T*; - using atomic_segment = std::atomic; - using segment_table_type = atomic_segment*; - - using size_type = std::size_t; - using segment_index_type = std::size_t; - - using allocator_type = Allocator; - - using allocator_traits_type = tbb::detail::allocator_traits; - using segment_table_allocator_type = typename allocator_traits_type::template rebind_alloc; -protected: - using segment_table_allocator_traits = tbb::detail::allocator_traits; - using derived_type = DerivedType; - - static constexpr size_type pointers_per_embedded_table = PointersPerEmbeddedTable; - static constexpr size_type pointers_per_long_table = sizeof(size_type) * 8; -public: - segment_table( const allocator_type& alloc = allocator_type() ) - : my_segment_table_allocator(alloc), my_segment_table(nullptr) - , my_first_block{}, my_size{}, my_segment_table_allocation_failed{} - { - my_segment_table.store(my_embedded_table, std::memory_order_relaxed); - zero_table(my_embedded_table, pointers_per_embedded_table); - } - - segment_table( const segment_table& other ) - : my_segment_table_allocator(segment_table_allocator_traits:: - select_on_container_copy_construction(other.my_segment_table_allocator)) - , my_segment_table(nullptr), my_first_block{}, my_size{}, my_segment_table_allocation_failed{} - { - my_segment_table.store(my_embedded_table, std::memory_order_relaxed); - zero_table(my_embedded_table, pointers_per_embedded_table); - try_call( [&] { - internal_transfer(other, copy_segment_body_type{*this}); - } ).on_exception( [&] { - clear(); - }); - } - - segment_table( const segment_table& other, const allocator_type& alloc ) - : my_segment_table_allocator(alloc), my_segment_table(nullptr) - , my_first_block{}, my_size{}, my_segment_table_allocation_failed{} - { - my_segment_table.store(my_embedded_table, std::memory_order_relaxed); - zero_table(my_embedded_table, pointers_per_embedded_table); - try_call( [&] { - internal_transfer(other, copy_segment_body_type{*this}); - } ).on_exception( [&] { - clear(); - }); - } - - segment_table( segment_table&& other ) - : my_segment_table_allocator(std::move(other.my_segment_table_allocator)), my_segment_table(nullptr) - , my_first_block{}, my_size{}, my_segment_table_allocation_failed{} - { - my_segment_table.store(my_embedded_table, std::memory_order_relaxed); - zero_table(my_embedded_table, pointers_per_embedded_table); - internal_move(std::move(other)); - } - - segment_table( segment_table&& other, const allocator_type& alloc ) - : my_segment_table_allocator(alloc), my_segment_table(nullptr), my_first_block{} - , my_size{}, my_segment_table_allocation_failed{} - { - my_segment_table.store(my_embedded_table, std::memory_order_relaxed); - zero_table(my_embedded_table, pointers_per_embedded_table); - using is_equal_type = typename segment_table_allocator_traits::is_always_equal; - internal_move_construct_with_allocator(std::move(other), alloc, is_equal_type()); - } - - ~segment_table() { - clear(); - } - - segment_table& operator=( const segment_table& other ) { - if (this != &other) { - copy_assign_allocators(my_segment_table_allocator, other.my_segment_table_allocator); - internal_transfer(other, copy_segment_body_type{*this}); - } - return *this; - } - - segment_table& operator=( segment_table&& other ) - noexcept(derived_type::is_noexcept_assignment) - { - using pocma_type = typename segment_table_allocator_traits::propagate_on_container_move_assignment; - using is_equal_type = typename segment_table_allocator_traits::is_always_equal; - - if (this != &other) { - move_assign_allocators(my_segment_table_allocator, other.my_segment_table_allocator); - internal_move_assign(std::move(other), tbb::detail::disjunction()); - } - return *this; - } - - void swap( segment_table& other ) - noexcept(derived_type::is_noexcept_swap) - { - using is_equal_type = typename segment_table_allocator_traits::is_always_equal; - using pocs_type = typename segment_table_allocator_traits::propagate_on_container_swap; - - if (this != &other) { - swap_allocators(my_segment_table_allocator, other.my_segment_table_allocator); - internal_swap(other, tbb::detail::disjunction()); - } - } - - segment_type get_segment( segment_index_type index ) const { - return get_table()[index] + segment_base(index); - } - - value_type& operator[]( size_type index ) { - return internal_subscript(index); - } - - const value_type& operator[]( size_type index ) const { - return const_cast(this)->internal_subscript(index); - } - - const segment_table_allocator_type& get_allocator() const { - return my_segment_table_allocator; - } - - segment_table_allocator_type& get_allocator() { - return my_segment_table_allocator; - } - - void enable_segment( segment_type& segment, segment_table_type table, segment_index_type seg_index, size_type index ) { - // Allocate new segment - segment_type new_segment = self()->create_segment(table, seg_index, index); - if (new_segment != nullptr) { - // Store (new_segment - segment_base) into the segment table to allow access to the table by index via - // my_segment_table[segment_index_of(index)][index] - segment_type disabled_segment = nullptr; - if (!table[seg_index].compare_exchange_strong(disabled_segment, new_segment - segment_base(seg_index))) { - // compare_exchange failed => some other thread has already enabled this segment - // Deallocate the memory - self()->deallocate_segment(new_segment, seg_index); - } - } - - segment = table[seg_index].load(std::memory_order_acquire); - __TBB_ASSERT(segment != nullptr, "If create_segment returned nullptr, the element should be stored in the table"); - } - - void delete_segment( segment_index_type seg_index ) { - segment_type segment_to_delete = self()->nullify_segment(get_table(), seg_index); - if (segment_to_delete == segment_allocation_failure_tag) { - return; - } - - segment_to_delete += segment_base(seg_index); - - // Deallocate the segment - self()->destroy_segment(segment_to_delete, seg_index); - } - - size_type number_of_segments( segment_table_type table ) const { - // Check for an active table, if it is embedded table - return the number of embedded segments - // Otherwise - return the maximum number of segments - return table == my_embedded_table ? pointers_per_embedded_table : pointers_per_long_table; - } - - size_type capacity() const noexcept { - segment_table_type table = get_table(); - size_type num_segments = number_of_segments(table); - for (size_type seg_index = 0; seg_index < num_segments; ++seg_index) { - // Check if the pointer is valid (allocated) - if (table[seg_index].load(std::memory_order_relaxed) <= segment_allocation_failure_tag) { - return segment_base(seg_index); - } - } - return segment_base(num_segments); - } - - size_type find_last_allocated_segment( segment_table_type table ) const noexcept { - size_type end = 0; - size_type num_segments = number_of_segments(table); - for (size_type seg_index = 0; seg_index < num_segments; ++seg_index) { - // Check if the pointer is valid (allocated) - if (table[seg_index].load(std::memory_order_relaxed) > segment_allocation_failure_tag) { - end = seg_index + 1; - } - } - return end; - } - - void reserve( size_type n ) { - if (n > allocator_traits_type::max_size(my_segment_table_allocator)) { - throw_exception(exception_id::reservation_length_error); - } - - size_type size = my_size.load(std::memory_order_relaxed); - segment_index_type start_seg_idx = size == 0 ? 0 : segment_index_of(size - 1) + 1; - for (segment_index_type seg_idx = start_seg_idx; segment_base(seg_idx) < n; ++seg_idx) { - size_type first_index = segment_base(seg_idx); - internal_subscript(first_index); - } - } - - void clear() { - clear_segments(); - clear_table(); - my_size.store(0, std::memory_order_relaxed); - my_first_block.store(0, std::memory_order_relaxed); - } - - void clear_segments() { - segment_table_type current_segment_table = get_table(); - for (size_type i = number_of_segments(current_segment_table); i != 0; --i) { - if (current_segment_table[i - 1].load(std::memory_order_relaxed) != nullptr) { - // If the segment was enabled - disable and deallocate it - delete_segment(i - 1); - } - } - } - - void clear_table() { - segment_table_type current_segment_table = get_table(); - if (current_segment_table != my_embedded_table) { - // If the active table is not the embedded one - deallocate the active table - for (size_type i = 0; i != pointers_per_long_table; ++i) { - segment_table_allocator_traits::destroy(my_segment_table_allocator, ¤t_segment_table[i]); - } - - segment_table_allocator_traits::deallocate(my_segment_table_allocator, current_segment_table, pointers_per_long_table); - my_segment_table.store(my_embedded_table, std::memory_order_relaxed); - zero_table(my_embedded_table, pointers_per_embedded_table); - } - } - - void extend_table_if_necessary(segment_table_type& table, size_type start_index, size_type end_index) { - // extend_segment_table if an active table is an embedded table - // and the requested index is not in the embedded table - if (table == my_embedded_table && end_index > embedded_table_size) { - if (start_index <= embedded_table_size) { - try_call([&] { - table = self()->allocate_long_table(my_embedded_table, start_index); - // It is possible that the table was extended by the thread that allocated first_block. - // In this case it is necessary to re-read the current table. - - if (table) { - my_segment_table.store(table, std::memory_order_release); - } else { - table = my_segment_table.load(std::memory_order_acquire); - } - }).on_exception([&] { - my_segment_table_allocation_failed.store(true, std::memory_order_relaxed); - }); - } else { - atomic_backoff backoff; - do { - if (my_segment_table_allocation_failed.load(std::memory_order_relaxed)) { - throw_exception(exception_id::bad_alloc); - } - backoff.pause(); - table = my_segment_table.load(std::memory_order_acquire); - } while (table == my_embedded_table); - } - } - } - - // Return the segment where index is stored - static constexpr segment_index_type segment_index_of( size_type index ) { - return size_type(tbb::detail::log2(uintptr_t(index|1))); - } - - // Needed to calculate the offset in segment - static constexpr size_type segment_base( size_type index ) { - return size_type(1) << index & ~size_type(1); - } - - // Return size of the segment - static constexpr size_type segment_size( size_type index ) { - return index == 0 ? 2 : size_type(1) << index; - } - -private: - - derived_type* self() { - return static_cast(this); - } - - struct copy_segment_body_type { - void operator()( segment_index_type index, segment_type from, segment_type to ) const { - my_instance.self()->copy_segment(index, from, to); - } - segment_table& my_instance; - }; - - struct move_segment_body_type { - void operator()( segment_index_type index, segment_type from, segment_type to ) const { - my_instance.self()->move_segment(index, from, to); - } - segment_table& my_instance; - }; - - // Transgers all segments from the other table - template - void internal_transfer( const segment_table& other, TransferBody transfer_segment ) { - static_cast(this)->destroy_elements(); - - assign_first_block_if_necessary(other.my_first_block.load(std::memory_order_relaxed)); - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - - segment_table_type other_table = other.get_table(); - size_type end_segment_size = segment_size(other.find_last_allocated_segment(other_table)); - - // If an exception occurred in other, then the size may be greater than the size of the end segment. - size_type other_size = end_segment_size < other.my_size.load(std::memory_order_relaxed) ? - other.my_size.load(std::memory_order_relaxed) : end_segment_size; - other_size = my_segment_table_allocation_failed ? embedded_table_size : other_size; - - for (segment_index_type i = 0; segment_base(i) < other_size; ++i) { - // If the segment in other table is enabled - transfer it - if (other_table[i].load(std::memory_order_relaxed) == segment_allocation_failure_tag) - { - my_size = segment_base(i); - break; - } else if (other_table[i].load(std::memory_order_relaxed) != nullptr) { - internal_subscript(segment_base(i)); - transfer_segment(i, other.get_table()[i].load(std::memory_order_relaxed) + segment_base(i), - get_table()[i].load(std::memory_order_relaxed) + segment_base(i)); - } - } - } - - // Moves the other segment table - // Only equal allocators are allowed - void internal_move( segment_table&& other ) { - // NOTE: allocators should be equal - clear(); - my_first_block.store(other.my_first_block.load(std::memory_order_relaxed), std::memory_order_relaxed); - my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - // If an active table in other is embedded - restore all of the embedded segments - if (other.get_table() == other.my_embedded_table) { - for ( size_type i = 0; i != pointers_per_embedded_table; ++i ) { - segment_type other_segment = other.my_embedded_table[i].load(std::memory_order_relaxed); - my_embedded_table[i].store(other_segment, std::memory_order_relaxed); - other.my_embedded_table[i].store(nullptr, std::memory_order_relaxed); - } - my_segment_table.store(my_embedded_table, std::memory_order_relaxed); - } else { - my_segment_table.store(other.my_segment_table, std::memory_order_relaxed); - other.my_segment_table.store(other.my_embedded_table, std::memory_order_relaxed); - zero_table(other.my_embedded_table, pointers_per_embedded_table); - } - other.my_size.store(0, std::memory_order_relaxed); - } - - // Move construct the segment table with the allocator object - // if any instances of allocator_type are always equal - void internal_move_construct_with_allocator( segment_table&& other, const allocator_type&, - /*is_always_equal = */ std::true_type ) { - internal_move(std::move(other)); - } - - // Move construct the segment table with the allocator object - // if any instances of allocator_type are always equal - void internal_move_construct_with_allocator( segment_table&& other, const allocator_type& alloc, - /*is_always_equal = */ std::false_type ) { - if (other.my_segment_table_allocator == alloc) { - // If allocators are equal - restore pointers - internal_move(std::move(other)); - } else { - // If allocators are not equal - perform per element move with reallocation - try_call( [&] { - internal_transfer(other, move_segment_body_type{*this}); - } ).on_exception( [&] { - clear(); - }); - } - } - - // Move assigns the segment table to other is any instances of allocator_type are always equal - // or propagate_on_container_move_assignment is true - void internal_move_assign( segment_table&& other, /*is_always_equal || POCMA = */ std::true_type ) { - internal_move(std::move(other)); - } - - // Move assigns the segment table to other is any instances of allocator_type are not always equal - // and propagate_on_container_move_assignment is false - void internal_move_assign( segment_table&& other, /*is_always_equal || POCMA = */ std::false_type ) { - if (my_segment_table_allocator == other.my_segment_table_allocator) { - // If allocators are equal - restore pointers - internal_move(std::move(other)); - } else { - // If allocators are not equal - perform per element move with reallocation - internal_transfer(other, move_segment_body_type{*this}); - } - } - - // Swaps two segment tables if any instances of allocator_type are always equal - // or propagate_on_container_swap is true - void internal_swap( segment_table& other, /*is_always_equal || POCS = */ std::true_type ) { - internal_swap_fields(other); - } - - // Swaps two segment tables if any instances of allocator_type are not always equal - // and propagate_on_container_swap is false - // According to the C++ standard, swapping of two containers with unequal allocators - // is an undefined behavior scenario - void internal_swap( segment_table& other, /*is_always_equal || POCS = */ std::false_type ) { - __TBB_ASSERT(my_segment_table_allocator == other.my_segment_table_allocator, - "Swapping with unequal allocators is not allowed"); - internal_swap_fields(other); - } - - void internal_swap_fields( segment_table& other ) { - // If an active table in either *this segment table or other is an embedded one - swaps the embedded tables - if (get_table() == my_embedded_table || - other.get_table() == other.my_embedded_table) { - - for (size_type i = 0; i != pointers_per_embedded_table; ++i) { - segment_type current_segment = my_embedded_table[i].load(std::memory_order_relaxed); - segment_type other_segment = other.my_embedded_table[i].load(std::memory_order_relaxed); - - my_embedded_table[i].store(other_segment, std::memory_order_relaxed); - other.my_embedded_table[i].store(current_segment, std::memory_order_relaxed); - } - } - - segment_table_type current_segment_table = get_table(); - segment_table_type other_segment_table = other.get_table(); - - // If an active table is an embedded one - - // store an active table in other to the embedded one from other - if (current_segment_table == my_embedded_table) { - other.my_segment_table.store(other.my_embedded_table, std::memory_order_relaxed); - } else { - // Otherwise - store it to the active segment table - other.my_segment_table.store(current_segment_table, std::memory_order_relaxed); - } - - // If an active table in other segment table is an embedded one - - // store an active table in other to the embedded one from *this - if (other_segment_table == other.my_embedded_table) { - my_segment_table.store(my_embedded_table, std::memory_order_relaxed); - } else { - // Otherwise - store it to the active segment table in other - my_segment_table.store(other_segment_table, std::memory_order_relaxed); - } - auto first_block = other.my_first_block.load(std::memory_order_relaxed); - other.my_first_block.store(my_first_block.load(std::memory_order_relaxed), std::memory_order_relaxed); - my_first_block.store(first_block, std::memory_order_relaxed); - - auto size = other.my_size.load(std::memory_order_relaxed); - other.my_size.store(my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); - my_size.store(size, std::memory_order_relaxed); - } - -protected: - // A flag indicates that an exception was throws during segment allocations - const segment_type segment_allocation_failure_tag = reinterpret_cast(1); - static constexpr size_type embedded_table_size = segment_size(pointers_per_embedded_table); - - template - value_type& internal_subscript( size_type index ) { - segment_index_type seg_index = segment_index_of(index); - segment_table_type table = my_segment_table.load(std::memory_order_acquire); - segment_type segment = nullptr; - - if (allow_out_of_range_access) { - if (derived_type::allow_table_extending) { - extend_table_if_necessary(table, index, index + 1); - } - - segment = table[seg_index].load(std::memory_order_acquire); - // If the required segment is disabled - enable it - if (segment == nullptr) { - enable_segment(segment, table, seg_index, index); - } - // Check if an exception was thrown during segment allocation - if (segment == segment_allocation_failure_tag) { - throw_exception(exception_id::bad_alloc); - } - } else { - segment = table[seg_index].load(std::memory_order_acquire); - } - __TBB_ASSERT(segment != nullptr, nullptr); - - return segment[index]; - } - - void assign_first_block_if_necessary(segment_index_type index) { - size_type zero = 0; - if (this->my_first_block.load(std::memory_order_relaxed) == zero) { - this->my_first_block.compare_exchange_strong(zero, index); - } - } - - void zero_table( segment_table_type table, size_type count ) { - for (size_type i = 0; i != count; ++i) { - table[i].store(nullptr, std::memory_order_relaxed); - } - } - - segment_table_type get_table() const { - return my_segment_table.load(std::memory_order_acquire); - } - - segment_table_allocator_type my_segment_table_allocator; - std::atomic my_segment_table; - atomic_segment my_embedded_table[pointers_per_embedded_table]; - // Number of segments in first block - std::atomic my_first_block; - // Number of elements in table - std::atomic my_size; - // Flag to indicate failed extend table - std::atomic my_segment_table_allocation_failed; -}; // class segment_table - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) -#pragma warning(pop) // warning 4127 is back -#endif - -#endif // __TBB_detail__segment_table_H diff --git a/include/oneapi/tbb/detail/_small_object_pool.h b/include/oneapi/tbb/detail/_small_object_pool.h deleted file mode 100644 index 7485b31..0000000 --- a/include/oneapi/tbb/detail/_small_object_pool.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - Copyright (c) 2020-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__small_object_pool_H -#define __TBB__small_object_pool_H - -#include "_config.h" -#include "_assert.h" - -#include "../profiling.h" -#include -#include -#include - -namespace tbb { -namespace detail { - -namespace d1 { -class small_object_pool { -protected: - small_object_pool() = default; -}; -struct execution_data; -} - -namespace r1 { -TBB_EXPORT void* __TBB_EXPORTED_FUNC allocate(d1::small_object_pool*& pool, std::size_t number_of_bytes, - const d1::execution_data& ed); -TBB_EXPORT void* __TBB_EXPORTED_FUNC allocate(d1::small_object_pool*& pool, std::size_t number_of_bytes); -TBB_EXPORT void __TBB_EXPORTED_FUNC deallocate(d1::small_object_pool& pool, void* ptr, std::size_t number_of_bytes, - const d1::execution_data& ed); -TBB_EXPORT void __TBB_EXPORTED_FUNC deallocate(d1::small_object_pool& pool, void* ptr, std::size_t number_of_bytes); -} - -namespace d1 { -class small_object_allocator { -public: - template - Type* new_object(execution_data& ed, Args&&... args) { - void* allocated_object = r1::allocate(m_pool, sizeof(Type), ed); - - auto constructed_object = new(allocated_object) Type(std::forward(args)...); - return constructed_object; - } - - template - Type* new_object(Args&&... args) { - void* allocated_object = r1::allocate(m_pool, sizeof(Type)); - - auto constructed_object = new(allocated_object) Type(std::forward(args)...); - return constructed_object; - } - - template - void delete_object(Type* object, const execution_data& ed) { - // Copy this since it can be a member of the passed object and - // unintentionally destroyed when Type destructor is called below - small_object_allocator alloc = *this; - object->~Type(); - alloc.deallocate(object, ed); - } - - template - void delete_object(Type* object) { - // Copy this since it can be a member of the passed object and - // unintentionally destroyed when Type destructor is called below - small_object_allocator alloc = *this; - object->~Type(); - alloc.deallocate(object); - } - - template - void deallocate(Type* ptr, const execution_data& ed) { - call_itt_task_notify(destroy, ptr); - - __TBB_ASSERT(m_pool != nullptr, "Pool must be valid for deallocate call"); - r1::deallocate(*m_pool, ptr, sizeof(Type), ed); - } - - template - void deallocate(Type* ptr) { - call_itt_task_notify(destroy, ptr); - - __TBB_ASSERT(m_pool != nullptr, "Pool must be valid for deallocate call"); - r1::deallocate(*m_pool, ptr, sizeof(Type)); - } -private: - small_object_pool* m_pool{}; -}; - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif /* __TBB__small_object_pool_H */ diff --git a/include/oneapi/tbb/detail/_string_resource.h b/include/oneapi/tbb/detail/_string_resource.h deleted file mode 100644 index c06d5b5..0000000 --- a/include/oneapi/tbb/detail/_string_resource.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -TBB_STRING_RESOURCE(ALGORITHM, "tbb_algorithm") -TBB_STRING_RESOURCE(PARALLEL_FOR, "tbb_parallel_for") -TBB_STRING_RESOURCE(PARALLEL_FOR_EACH, "tbb_parallel_for_each") -TBB_STRING_RESOURCE(PARALLEL_INVOKE, "tbb_parallel_invoke") -TBB_STRING_RESOURCE(PARALLEL_REDUCE, "tbb_parallel_reduce") -TBB_STRING_RESOURCE(PARALLEL_SCAN, "tbb_parallel_scan") -TBB_STRING_RESOURCE(PARALLEL_SORT, "tbb_parallel_sort") -TBB_STRING_RESOURCE(PARALLEL_PIPELINE, "tbb_parallel_pipeline") -TBB_STRING_RESOURCE(CUSTOM_CTX, "tbb_custom") - -TBB_STRING_RESOURCE(FLOW_NULL, "null") -TBB_STRING_RESOURCE(FLOW_BROADCAST_NODE, "broadcast_node") -TBB_STRING_RESOURCE(FLOW_BUFFER_NODE, "buffer_node") -TBB_STRING_RESOURCE(FLOW_CONTINUE_NODE, "continue_node") -TBB_STRING_RESOURCE(FLOW_FUNCTION_NODE, "function_node") -TBB_STRING_RESOURCE(FLOW_JOIN_NODE_QUEUEING, "join_node (queueing)") -TBB_STRING_RESOURCE(FLOW_JOIN_NODE_RESERVING, "join_node (reserving)") -TBB_STRING_RESOURCE(FLOW_JOIN_NODE_TAG_MATCHING, "join_node (tag_matching)") -TBB_STRING_RESOURCE(FLOW_LIMITER_NODE, "limiter_node") -TBB_STRING_RESOURCE(FLOW_MULTIFUNCTION_NODE, "multifunction_node") -TBB_STRING_RESOURCE(FLOW_OVERWRITE_NODE, "overwrite_node") -TBB_STRING_RESOURCE(FLOW_PRIORITY_QUEUE_NODE, "priority_queue_node") -TBB_STRING_RESOURCE(FLOW_QUEUE_NODE, "queue_node") -TBB_STRING_RESOURCE(FLOW_SEQUENCER_NODE, "sequencer_node") -TBB_STRING_RESOURCE(FLOW_INPUT_NODE, "input_node") -TBB_STRING_RESOURCE(FLOW_SPLIT_NODE, "split_node") -TBB_STRING_RESOURCE(FLOW_WRITE_ONCE_NODE, "write_once_node") -TBB_STRING_RESOURCE(FLOW_INDEXER_NODE, "indexer_node") -TBB_STRING_RESOURCE(FLOW_COMPOSITE_NODE, "composite_node") -TBB_STRING_RESOURCE(FLOW_ASYNC_NODE, "async_node") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT, "input_port") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_0, "input_port_0") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_1, "input_port_1") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_2, "input_port_2") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_3, "input_port_3") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_4, "input_port_4") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_5, "input_port_5") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_6, "input_port_6") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_7, "input_port_7") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_8, "input_port_8") -TBB_STRING_RESOURCE(FLOW_INPUT_PORT_9, "input_port_9") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT, "output_port") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_0, "output_port_0") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_1, "output_port_1") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_2, "output_port_2") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_3, "output_port_3") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_4, "output_port_4") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_5, "output_port_5") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_6, "output_port_6") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_7, "output_port_7") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_8, "output_port_8") -TBB_STRING_RESOURCE(FLOW_OUTPUT_PORT_9, "output_port_9") -TBB_STRING_RESOURCE(FLOW_OBJECT_NAME, "object_name") -TBB_STRING_RESOURCE(FLOW_BODY, "body") -TBB_STRING_RESOURCE(FLOW_GRAPH, "graph") -TBB_STRING_RESOURCE(FLOW_NODE, "node") -TBB_STRING_RESOURCE(FLOW_TASKS, "tbb_flow_graph") -TBB_STRING_RESOURCE(USER_EVENT, "user_event") - -#if __TBB_FLOW_TRACE_CODEPTR -TBB_STRING_RESOURCE(CODE_ADDRESS, "code_address") -#endif diff --git a/include/oneapi/tbb/detail/_task.h b/include/oneapi/tbb/detail/_task.h deleted file mode 100644 index 636aea9..0000000 --- a/include/oneapi/tbb/detail/_task.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - Copyright (c) 2020-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB__task_H -#define __TBB__task_H - -#include "_config.h" -#include "_assert.h" -#include "_template_helpers.h" -#include "_small_object_pool.h" - -#include "../profiling.h" - -#include -#include -#include -#include -#include -#include - -namespace tbb { -namespace detail { - -namespace d1 { -using slot_id = unsigned short; -constexpr slot_id no_slot = slot_id(~0); -constexpr slot_id any_slot = slot_id(~1); - -class task; -class wait_context; -class task_group_context; -struct execution_data; -} - -namespace r1 { -//! Task spawn/wait entry points -TBB_EXPORT void __TBB_EXPORTED_FUNC spawn(d1::task& t, d1::task_group_context& ctx); -TBB_EXPORT void __TBB_EXPORTED_FUNC spawn(d1::task& t, d1::task_group_context& ctx, d1::slot_id id); -TBB_EXPORT void __TBB_EXPORTED_FUNC execute_and_wait(d1::task& t, d1::task_group_context& t_ctx, d1::wait_context&, d1::task_group_context& w_ctx); -TBB_EXPORT void __TBB_EXPORTED_FUNC wait(d1::wait_context&, d1::task_group_context& ctx); -TBB_EXPORT d1::slot_id __TBB_EXPORTED_FUNC execution_slot(const d1::execution_data*); -TBB_EXPORT d1::task_group_context* __TBB_EXPORTED_FUNC current_context(); - -// Do not place under __TBB_RESUMABLE_TASKS. It is a stub for unsupported platforms. -struct suspend_point_type; -using suspend_callback_type = void(*)(void*, suspend_point_type*); -//! The resumable tasks entry points -TBB_EXPORT void __TBB_EXPORTED_FUNC suspend(suspend_callback_type suspend_callback, void* user_callback); -TBB_EXPORT void __TBB_EXPORTED_FUNC resume(suspend_point_type* tag); -TBB_EXPORT suspend_point_type* __TBB_EXPORTED_FUNC current_suspend_point(); -TBB_EXPORT void __TBB_EXPORTED_FUNC notify_waiters(std::uintptr_t wait_ctx_addr); - -class thread_data; -class task_dispatcher; -class external_waiter; -struct task_accessor; -struct task_arena_impl; -} // namespace r1 - -namespace d1 { - -class task_arena; -using suspend_point = r1::suspend_point_type*; - -#if __TBB_RESUMABLE_TASKS -template -static void suspend_callback(void* user_callback, suspend_point sp) { - // Copy user function to a new stack after the context switch to avoid a race when the previous - // suspend point is resumed while the user_callback is being called. - F user_callback_copy = *static_cast(user_callback); - user_callback_copy(sp); -} - -template -void suspend(F f) { - r1::suspend(&suspend_callback, &f); -} - -inline void resume(suspend_point tag) { - r1::resume(tag); -} -#endif /* __TBB_RESUMABLE_TASKS */ - -// TODO align wait_context on cache lane -class wait_context { - static constexpr std::uint64_t overflow_mask = ~((1LLU << 32) - 1); - - std::uint64_t m_version_and_traits{1}; - std::atomic m_ref_count{}; - - void add_reference(std::int64_t delta) { - call_itt_task_notify(releasing, this); - std::uint64_t r = m_ref_count.fetch_add(static_cast(delta)) + static_cast(delta); - - __TBB_ASSERT_EX((r & overflow_mask) == 0, "Overflow is detected"); - - if (!r) { - // Some external waiters or coroutine waiters sleep in wait list - // Should to notify them that work is done - std::uintptr_t wait_ctx_addr = std::uintptr_t(this); - r1::notify_waiters(wait_ctx_addr); - } - } - - bool continue_execution() const { - std::uint64_t r = m_ref_count.load(std::memory_order_acquire); - __TBB_ASSERT_EX((r & overflow_mask) == 0, "Overflow is detected"); - return r > 0; - } - - friend class r1::thread_data; - friend class r1::task_dispatcher; - friend class r1::external_waiter; - friend class task_group; - friend class task_group_base; - friend struct r1::task_arena_impl; - friend struct r1::suspend_point_type; -public: - // Despite the internal reference count is uin64_t we limit the user interface with uint32_t - // to preserve a part of the internal reference count for special needs. - wait_context(std::uint32_t ref_count) : m_ref_count{ref_count} { suppress_unused_warning(m_version_and_traits); } - wait_context(const wait_context&) = delete; - - ~wait_context() { - __TBB_ASSERT(!continue_execution(), nullptr); - } - - void reserve(std::uint32_t delta = 1) { - add_reference(delta); - } - - void release(std::uint32_t delta = 1) { - add_reference(-std::int64_t(delta)); - } -}; - -struct execution_data { - task_group_context* context{}; - slot_id original_slot{}; - slot_id affinity_slot{}; -}; - -inline task_group_context* context(const execution_data& ed) { - return ed.context; -} - -inline slot_id original_slot(const execution_data& ed) { - return ed.original_slot; -} - -inline slot_id affinity_slot(const execution_data& ed) { - return ed.affinity_slot; -} - -inline slot_id execution_slot(const execution_data& ed) { - return r1::execution_slot(&ed); -} - -inline bool is_same_affinity(const execution_data& ed) { - return affinity_slot(ed) == no_slot || affinity_slot(ed) == execution_slot(ed); -} - -inline bool is_stolen(const execution_data& ed) { - return original_slot(ed) != execution_slot(ed); -} - -inline void spawn(task& t, task_group_context& ctx) { - call_itt_task_notify(releasing, &t); - r1::spawn(t, ctx); -} - -inline void spawn(task& t, task_group_context& ctx, slot_id id) { - call_itt_task_notify(releasing, &t); - r1::spawn(t, ctx, id); -} - -inline void execute_and_wait(task& t, task_group_context& t_ctx, wait_context& wait_ctx, task_group_context& w_ctx) { - r1::execute_and_wait(t, t_ctx, wait_ctx, w_ctx); - call_itt_task_notify(acquired, &wait_ctx); - call_itt_task_notify(destroy, &wait_ctx); -} - -inline void wait(wait_context& wait_ctx, task_group_context& ctx) { - r1::wait(wait_ctx, ctx); - call_itt_task_notify(acquired, &wait_ctx); - call_itt_task_notify(destroy, &wait_ctx); -} - -using r1::current_context; - -class task_traits { - std::uint64_t m_version_and_traits{}; - friend struct r1::task_accessor; -}; - -//! Alignment for a task object -static constexpr std::size_t task_alignment = 64; - -//! Base class for user-defined tasks. -/** @ingroup task_scheduling */ -class alignas(task_alignment) task : public task_traits { -protected: - virtual ~task() = default; - -public: - virtual task* execute(execution_data&) = 0; - virtual task* cancel(execution_data&) = 0; - -private: - std::uint64_t m_reserved[6]{}; - friend struct r1::task_accessor; -}; -static_assert(sizeof(task) == task_alignment, "task size is broken"); - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif /* __TBB__task_H */ diff --git a/include/oneapi/tbb/detail/_task_handle.h b/include/oneapi/tbb/detail/_task_handle.h deleted file mode 100644 index e32154f..0000000 --- a/include/oneapi/tbb/detail/_task_handle.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - Copyright (c) 2020-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - - -#ifndef __TBB_task_handle_H -#define __TBB_task_handle_H - -#include "_config.h" -#include "_task.h" -#include "_small_object_pool.h" -#include "_utils.h" -#include - -namespace tbb { -namespace detail { - -namespace d1 { class task_group_context; class wait_context; struct execution_data; } -namespace d2 { - -class task_handle; - -class task_handle_task : public d1::task { - std::uint64_t m_version_and_traits{}; - d1::wait_context& m_wait_ctx; - d1::task_group_context& m_ctx; - d1::small_object_allocator m_allocator; -public: - void finalize(const d1::execution_data* ed = nullptr) { - if (ed) { - m_allocator.delete_object(this, *ed); - } else { - m_allocator.delete_object(this); - } - } - - task_handle_task(d1::wait_context& wo, d1::task_group_context& ctx, d1::small_object_allocator& alloc) - : m_wait_ctx(wo) - , m_ctx(ctx) - , m_allocator(alloc) { - suppress_unused_warning(m_version_and_traits); - } - - ~task_handle_task() override { - m_wait_ctx.release(); - } - - d1::task_group_context& ctx() const { return m_ctx; } -}; - - -class task_handle { - struct task_handle_task_finalizer_t{ - void operator()(task_handle_task* p){ p->finalize(); } - }; - using handle_impl_t = std::unique_ptr; - - handle_impl_t m_handle = {nullptr}; -public: - task_handle() = default; - task_handle(task_handle&&) = default; - task_handle& operator=(task_handle&&) = default; - - explicit operator bool() const noexcept { return static_cast(m_handle); } - - friend bool operator==(task_handle const& th, std::nullptr_t) noexcept; - friend bool operator==(std::nullptr_t, task_handle const& th) noexcept; - - friend bool operator!=(task_handle const& th, std::nullptr_t) noexcept; - friend bool operator!=(std::nullptr_t, task_handle const& th) noexcept; - -private: - friend struct task_handle_accessor; - - task_handle(task_handle_task* t) : m_handle {t}{}; - - d1::task* release() { - return m_handle.release(); - } -}; - -struct task_handle_accessor { -static task_handle construct(task_handle_task* t) { return {t}; } -static d1::task* release(task_handle& th) { return th.release(); } -static d1::task_group_context& ctx_of(task_handle& th) { - __TBB_ASSERT(th.m_handle, "ctx_of does not expect empty task_handle."); - return th.m_handle->ctx(); -} -}; - -inline bool operator==(task_handle const& th, std::nullptr_t) noexcept { - return th.m_handle == nullptr; -} -inline bool operator==(std::nullptr_t, task_handle const& th) noexcept { - return th.m_handle == nullptr; -} - -inline bool operator!=(task_handle const& th, std::nullptr_t) noexcept { - return th.m_handle != nullptr; -} - -inline bool operator!=(std::nullptr_t, task_handle const& th) noexcept { - return th.m_handle != nullptr; -} - -} // namespace d2 -} // namespace detail -} // namespace tbb - -#endif /* __TBB_task_handle_H */ diff --git a/include/oneapi/tbb/detail/_template_helpers.h b/include/oneapi/tbb/detail/_template_helpers.h deleted file mode 100644 index 3491371..0000000 --- a/include/oneapi/tbb/detail/_template_helpers.h +++ /dev/null @@ -1,404 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__template_helpers_H -#define __TBB_detail__template_helpers_H - -#include "_utils.h" -#include "_config.h" - -#include -#include -#include -#include -#include -#include - -namespace tbb { -namespace detail { -inline namespace d0 { - -// An internal implementation of void_t, which can be used in SFINAE contexts -template -struct void_impl { - using type = void; -}; // struct void_impl - -template -using void_t = typename void_impl::type; - -// Generic SFINAE helper for expression checks, based on the idea demonstrated in ISO C++ paper n4502 -template class... Checks> -struct supports_impl { - using type = std::false_type; -}; - -template class... Checks> -struct supports_impl...>, Checks...> { - using type = std::true_type; -}; - -template class... Checks> -using supports = typename supports_impl::type; - -//! A template to select either 32-bit or 64-bit constant as compile time, depending on machine word size. -template -struct select_size_t_constant { - // Explicit cast is needed to avoid compiler warnings about possible truncation. - // The value of the right size, which is selected by ?:, is anyway not truncated or promoted. - static const std::size_t value = static_cast((sizeof(std::size_t)==sizeof(u)) ? u : ull); -}; - -// TODO: do we really need it? -//! Cast between unrelated pointer types. -/** This method should be used sparingly as a last resort for dealing with - situations that inherently break strict ISO C++ aliasing rules. */ -// T is a pointer type because it will be explicitly provided by the programmer as a template argument; -// U is a referent type to enable the compiler to check that "ptr" is a pointer, deducing U in the process. -template -inline T punned_cast( U* ptr ) { - std::uintptr_t x = reinterpret_cast(ptr); - return reinterpret_cast(x); -} - -template -struct padded_base : T { - char pad[S - R]; -}; -template struct padded_base : T {}; - -//! Pads type T to fill out to a multiple of cache line size. -template -struct padded : padded_base {}; - -#if __TBB_CPP14_INTEGER_SEQUENCE_PRESENT - -using std::index_sequence; -using std::make_index_sequence; - -#else - -template class index_sequence {}; - -template -struct make_index_sequence_impl : make_index_sequence_impl < N - 1, N - 1, S... > {}; - -template -struct make_index_sequence_impl <0, S...> { - using type = index_sequence; -}; - -template -using make_index_sequence = typename make_index_sequence_impl::type; - -#endif /* __TBB_CPP14_INTEGER_SEQUENCE_PRESENT */ - -#if __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT -using std::conjunction; -using std::disjunction; -#else // __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT - -template -struct conjunction : std::true_type {}; - -template -struct conjunction - : std::conditional, First>::type {}; - -template -struct conjunction : T {}; - -template -struct disjunction : std::false_type {}; - -template -struct disjunction - : std::conditional>::type {}; - -template -struct disjunction : T {}; - -#endif // __TBB_CPP17_LOGICAL_OPERATIONS_PRESENT - -template -using iterator_value_t = typename std::iterator_traits::value_type; - -template -using iterator_key_t = typename std::remove_const::first_type>::type; - -template -using iterator_mapped_t = typename iterator_value_t::second_type; - -template -using iterator_alloc_pair_t = std::pair>::type, - iterator_mapped_t>; - -template using alloc_value_type = typename A::value_type; -template using alloc_ptr_t = typename std::allocator_traits::pointer; -template using has_allocate = decltype(std::declval&>() = std::declval().allocate(0)); -template using has_deallocate = decltype(std::declval().deallocate(std::declval>(), 0)); - -// alloc_value_type should be checked first, because it can be used in other checks -template -using is_allocator = supports; - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -template -inline constexpr bool is_allocator_v = is_allocator::value; -#endif - -// Template class in which the "type" determines the type of the element number N in pack Args -template -struct pack_element { - using type = void; -}; - -template -struct pack_element { - using type = typename pack_element::type; -}; - -template -struct pack_element<0, T, Args...> { - using type = T; -}; - -template -using pack_element_t = typename pack_element::type; - -template -class raii_guard { -public: - static_assert( - std::is_nothrow_copy_constructible::value && - std::is_nothrow_move_constructible::value, - "Throwing an exception during the Func copy or move construction cause an unexpected behavior." - ); - - raii_guard( Func f ) noexcept : my_func(f), is_active(true) {} - - raii_guard( raii_guard&& g ) noexcept : my_func(std::move(g.my_func)), is_active(g.is_active) { - g.is_active = false; - } - - ~raii_guard() { - if (is_active) { - my_func(); - } - } - - void dismiss() { - is_active = false; - } -private: - Func my_func; - bool is_active; -}; // class raii_guard - -template -raii_guard make_raii_guard( Func f ) { - return raii_guard(f); -} - -template -struct try_call_proxy { - try_call_proxy( Body b ) : body(b) {} - - template - void on_exception( OnExceptionBody on_exception_body ) { - auto guard = make_raii_guard(on_exception_body); - body(); - guard.dismiss(); - } - - template - void on_completion(OnCompletionBody on_completion_body) { - auto guard = make_raii_guard(on_completion_body); - body(); - } - - Body body; -}; // struct try_call_proxy - -// Template helper function for API -// try_call(lambda1).on_exception(lambda2) -// Executes lambda1 and if it throws an exception - executes lambda2 -template -try_call_proxy try_call( Body b ) { - return try_call_proxy(b); -} - -#if __TBB_CPP17_IS_SWAPPABLE_PRESENT -using std::is_nothrow_swappable; -using std::is_swappable; -#else // __TBB_CPP17_IS_SWAPPABLE_PRESENT -namespace is_swappable_detail { -using std::swap; - -template -using has_swap = decltype(swap(std::declval(), std::declval())); - -#if _MSC_VER && _MSC_VER <= 1900 && !__INTEL_COMPILER -// Workaround for VS2015: it fails to instantiate noexcept(...) inside std::integral_constant. -template -struct noexcept_wrapper { - static const bool value = noexcept(swap(std::declval(), std::declval())); -}; -template -struct is_nothrow_swappable_impl : std::integral_constant::value> {}; -#else -template -struct is_nothrow_swappable_impl : std::integral_constant(), std::declval()))> {}; -#endif -} - -template -struct is_swappable : supports {}; - -template -struct is_nothrow_swappable - : conjunction, is_swappable_detail::is_nothrow_swappable_impl> {}; -#endif // __TBB_CPP17_IS_SWAPPABLE_PRESENT - -//! Allows to store a function parameter pack as a variable and later pass it to another function -template< typename... Types > -struct stored_pack; - -template<> -struct stored_pack<> -{ - using pack_type = stored_pack<>; - stored_pack() {} - - // Friend front-end functions - template< typename F, typename Pack > friend void call(F&& f, Pack&& p); - template< typename Ret, typename F, typename Pack > friend Ret call_and_return(F&& f, Pack&& p); - -protected: - // Ideally, ref-qualified non-static methods would be used, - // but that would greatly reduce the set of compilers where it works. - template< typename Ret, typename F, typename... Preceding > - static Ret call(F&& f, const pack_type& /*pack*/, Preceding&&... params) { - return std::forward(f)(std::forward(params)...); - } - template< typename Ret, typename F, typename... Preceding > - static Ret call(F&& f, pack_type&& /*pack*/, Preceding&&... params) { - return std::forward(f)(std::forward(params)...); - } -}; - -template< typename T, typename... Types > -struct stored_pack : stored_pack -{ - using pack_type = stored_pack; - using pack_remainder = stored_pack; - - // Since lifetime of original values is out of control, copies should be made. - // Thus references should be stripped away from the deduced type. - typename std::decay::type leftmost_value; - - // Here rvalue references act in the same way as forwarding references, - // as long as class template parameters were deduced via forwarding references. - stored_pack(T&& t, Types&&... types) - : pack_remainder(std::forward(types)...), leftmost_value(std::forward(t)) {} - - // Friend front-end functions - template< typename F, typename Pack > friend void call(F&& f, Pack&& p); - template< typename Ret, typename F, typename Pack > friend Ret call_and_return(F&& f, Pack&& p); - -protected: - template< typename Ret, typename F, typename... Preceding > - static Ret call(F&& f, pack_type& pack, Preceding&&... params) { - return pack_remainder::template call( - std::forward(f), static_cast(pack), - std::forward(params)... , pack.leftmost_value - ); - } - - template< typename Ret, typename F, typename... Preceding > - static Ret call(F&& f, pack_type&& pack, Preceding&&... params) { - return pack_remainder::template call( - std::forward(f), static_cast(pack), - std::forward(params)... , std::move(pack.leftmost_value) - ); - } -}; - -//! Calls the given function with arguments taken from a stored_pack -template< typename F, typename Pack > -void call(F&& f, Pack&& p) { - std::decay::type::template call(std::forward(f), std::forward(p)); -} - -template< typename Ret, typename F, typename Pack > -Ret call_and_return(F&& f, Pack&& p) { - return std::decay::type::template call(std::forward(f), std::forward(p)); -} - -template< typename... Types > -stored_pack save_pack(Types&&... types) { - return stored_pack(std::forward(types)...); -} - -// A structure with the value which is equal to Trait::value -// but can be used in the immediate context due to parameter T -template -struct dependent_bool : std::integral_constant {}; - -template -struct body_arg_detector; - -template -struct body_arg_detector { - using arg_type = Arg; -}; - -template -struct body_arg_detector { - using arg_type = Arg; -}; - -template -struct argument_detector; - -template -struct argument_detector { - using type = typename body_arg_detector::arg_type; -}; - -template -struct argument_detector { - using type = Arg; -}; - -// Detects the argument type of callable, works for callable with one argument. -template -using argument_type_of = typename argument_detector::type>::type; - -template -struct type_identity { - using type = T; -}; - -template -using type_identity_t = typename type_identity::type; - -} // inline namespace d0 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__template_helpers_H - diff --git a/include/oneapi/tbb/detail/_utils.h b/include/oneapi/tbb/detail/_utils.h deleted file mode 100644 index 1ac2e3b..0000000 --- a/include/oneapi/tbb/detail/_utils.h +++ /dev/null @@ -1,387 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__utils_H -#define __TBB_detail__utils_H - -#include -#include -#include -#include - -#include "_config.h" -#include "_assert.h" -#include "_machine.h" - -namespace tbb { -namespace detail { -inline namespace d0 { - -//! Utility template function to prevent "unused" warnings by various compilers. -template void suppress_unused_warning(T&&...) {} - -//! Compile-time constant that is upper bound on cache line/sector size. -/** It should be used only in situations where having a compile-time upper - bound is more useful than a run-time exact answer. - @ingroup memory_allocation */ -constexpr size_t max_nfs_size = 128; -constexpr std::size_t max_nfs_size_exp = 7; -static_assert(1 << max_nfs_size_exp == max_nfs_size, "max_nfs_size_exp must be a log2(max_nfs_size)"); - -//! Class that implements exponential backoff. -class atomic_backoff { - //! Time delay, in units of "pause" instructions. - /** Should be equal to approximately the number of "pause" instructions - that take the same time as an context switch. Must be a power of two.*/ - static constexpr std::int32_t LOOPS_BEFORE_YIELD = 16; - std::int32_t count; - -public: - // In many cases, an object of this type is initialized eagerly on hot path, - // as in for(atomic_backoff b; ; b.pause()) { /*loop body*/ } - // For this reason, the construction cost must be very small! - atomic_backoff() : count(1) {} - // This constructor pauses immediately; do not use on hot paths! - atomic_backoff(bool) : count(1) { pause(); } - - //! No Copy - atomic_backoff(const atomic_backoff&) = delete; - atomic_backoff& operator=(const atomic_backoff&) = delete; - - //! Pause for a while. - void pause() { - if (count <= LOOPS_BEFORE_YIELD) { - machine_pause(count); - // Pause twice as long the next time. - count *= 2; - } else { - // Pause is so long that we might as well yield CPU to scheduler. - yield(); - } - } - - //! Pause for a few times and return false if saturated. - bool bounded_pause() { - machine_pause(count); - if (count < LOOPS_BEFORE_YIELD) { - // Pause twice as long the next time. - count *= 2; - return true; - } else { - return false; - } - } - - void reset() { - count = 1; - } -}; - -//! Spin WHILE the condition is true. -/** T and U should be comparable types. */ -template -T spin_wait_while(const std::atomic& location, C comp, std::memory_order order) { - atomic_backoff backoff; - T snapshot = location.load(order); - while (comp(snapshot)) { - backoff.pause(); - snapshot = location.load(order); - } - return snapshot; -} - -//! Spin WHILE the value of the variable is equal to a given value -/** T and U should be comparable types. */ -template -T spin_wait_while_eq(const std::atomic& location, const U value, std::memory_order order = std::memory_order_acquire) { - return spin_wait_while(location, [&value](T t) { return t == value; }, order); -} - -//! Spin UNTIL the value of the variable is equal to a given value -/** T and U should be comparable types. */ -template -T spin_wait_until_eq(const std::atomic& location, const U value, std::memory_order order = std::memory_order_acquire) { - return spin_wait_while(location, [&value](T t) { return t != value; }, order); -} - -//! Spin UNTIL the condition returns true or spinning time is up. -/** Returns what the passed functor returned last time it was invoked. */ -template -bool timed_spin_wait_until(Condition condition) { - // 32 pauses + 32 yields are meausered as balanced spin time before sleep. - bool finish = condition(); - for (int i = 1; !finish && i < 32; finish = condition(), i *= 2) { - machine_pause(i); - } - for (int i = 32; !finish && i < 64; finish = condition(), ++i) { - yield(); - } - return finish; -} - -template -std::uintptr_t log2(T in) { - __TBB_ASSERT(in > 0, "The logarithm of a non-positive value is undefined."); - return machine_log2(in); -} - -template -T reverse_bits(T src) { - return machine_reverse_bits(src); -} - -template -T reverse_n_bits(T src, std::size_t n) { - __TBB_ASSERT(n != 0, "Reverse for 0 bits is undefined behavior."); - return reverse_bits(src) >> (number_of_bits() - n); -} - -// A function to check if passed integer is a power of two -template -constexpr bool is_power_of_two( IntegerType arg ) { - static_assert(std::is_integral::value, - "An argument for is_power_of_two should be integral type"); - return arg && (0 == (arg & (arg - 1))); -} - -// A function to determine if passed integer is a power of two -// at least as big as another power of two, i.e. for strictly positive i and j, -// with j being a power of two, determines whether i==j< -constexpr bool is_power_of_two_at_least(ArgIntegerType arg, DivisorIntegerType divisor) { - // Divisor should be a power of two - static_assert(std::is_integral::value, - "An argument for is_power_of_two_at_least should be integral type"); - return 0 == (arg & (arg - divisor)); -} - -// A function to compute arg modulo divisor where divisor is a power of 2. -template -inline ArgIntegerType modulo_power_of_two(ArgIntegerType arg, DivisorIntegerType divisor) { - __TBB_ASSERT( is_power_of_two(divisor), "Divisor should be a power of two" ); - return arg & (divisor - 1); -} - -//! A function to check if passed in pointer is aligned on a specific border -template -constexpr bool is_aligned(T* pointer, std::uintptr_t alignment) { - return 0 == (reinterpret_cast(pointer) & (alignment - 1)); -} - -#if TBB_USE_ASSERT -static void* const poisoned_ptr = reinterpret_cast(-1); - -//! Set p to invalid pointer value. -template -inline void poison_pointer( T* &p ) { p = reinterpret_cast(poisoned_ptr); } - -template -inline void poison_pointer(std::atomic& p) { p.store(reinterpret_cast(poisoned_ptr), std::memory_order_relaxed); } - -/** Expected to be used in assertions only, thus no empty form is defined. **/ -template -inline bool is_poisoned( T* p ) { return p == reinterpret_cast(poisoned_ptr); } - -template -inline bool is_poisoned(const std::atomic& p) { return is_poisoned(p.load(std::memory_order_relaxed)); } -#else -template -inline void poison_pointer(T&) {/*do nothing*/} -#endif /* !TBB_USE_ASSERT */ - -template -bool assert_pointer_valid(T* p, const char* comment = nullptr) { - suppress_unused_warning(p, comment); - __TBB_ASSERT(p != nullptr, comment); - __TBB_ASSERT(!is_poisoned(p), comment); -#if !(_MSC_VER && _MSC_VER <= 1900 && !__INTEL_COMPILER) - __TBB_ASSERT(is_aligned(p, alignment == 0 ? alignof(T) : alignment), comment); -#endif - // Returns something to simplify assert_pointers_valid implementation. - return true; -} - -template -void assert_pointers_valid(Args*... p) { - // suppress_unused_warning is used as an evaluation context for the variadic pack. - suppress_unused_warning(assert_pointer_valid(p)...); -} - -//! Base class for types that should not be assigned. -class no_assign { -public: - void operator=(const no_assign&) = delete; - no_assign(const no_assign&) = default; - no_assign() = default; -}; - -//! Base class for types that should not be copied or assigned. -class no_copy: no_assign { -public: - no_copy(const no_copy&) = delete; - no_copy() = default; -}; - -template -void swap_atomics_relaxed(std::atomic& lhs, std::atomic& rhs){ - T tmp = lhs.load(std::memory_order_relaxed); - lhs.store(rhs.load(std::memory_order_relaxed), std::memory_order_relaxed); - rhs.store(tmp, std::memory_order_relaxed); -} - -//! One-time initialization states -enum class do_once_state { - uninitialized = 0, ///< No execution attempts have been undertaken yet - pending, ///< A thread is executing associated do-once routine - executed, ///< Do-once routine has been executed - initialized = executed ///< Convenience alias -}; - -//! One-time initialization function -/** /param initializer Pointer to function without arguments - The variant that returns bool is used for cases when initialization can fail - and it is OK to continue execution, but the state should be reset so that - the initialization attempt was repeated the next time. - /param state Shared state associated with initializer that specifies its - initialization state. Must be initially set to #uninitialized value - (e.g. by means of default static zero initialization). **/ -template -void atomic_do_once( const F& initializer, std::atomic& state ) { - // The loop in the implementation is necessary to avoid race when thread T2 - // that arrived in the middle of initialization attempt by another thread T1 - // has just made initialization possible. - // In such a case T2 has to rely on T1 to initialize, but T1 may already be past - // the point where it can recognize the changed conditions. - do_once_state expected_state; - while ( state.load( std::memory_order_acquire ) != do_once_state::executed ) { - if( state.load( std::memory_order_relaxed ) == do_once_state::uninitialized ) { - expected_state = do_once_state::uninitialized; -#if defined(__INTEL_COMPILER) && __INTEL_COMPILER <= 1910 - using enum_type = typename std::underlying_type::type; - if( ((std::atomic&)state).compare_exchange_strong( (enum_type&)expected_state, (enum_type)do_once_state::pending ) ) { -#else - if( state.compare_exchange_strong( expected_state, do_once_state::pending ) ) { -#endif - run_initializer( initializer, state ); - break; - } - } - spin_wait_while_eq( state, do_once_state::pending ); - } -} - -// Run the initializer which can not fail -template -void run_initializer(const Functor& f, std::atomic& state ) { - f(); - state.store(do_once_state::executed, std::memory_order_release); -} - -#if __TBB_CPP20_CONCEPTS_PRESENT -template -concept boolean_testable_impl = std::convertible_to; - -template -concept boolean_testable = boolean_testable_impl && requires( T&& t ) { - { !std::forward(t) } -> boolean_testable_impl; - }; - -#if __TBB_CPP20_COMPARISONS_PRESENT -struct synthesized_three_way_comparator { - template - auto operator()( const T1& lhs, const T2& rhs ) const - requires requires { - { lhs < rhs } -> boolean_testable; - { rhs < lhs } -> boolean_testable; - } - { - if constexpr (std::three_way_comparable_with) { - return lhs <=> rhs; - } else { - if (lhs < rhs) { - return std::weak_ordering::less; - } - if (rhs < lhs) { - return std::weak_ordering::greater; - } - return std::weak_ordering::equivalent; - } - } -}; // struct synthesized_three_way_comparator - -template -using synthesized_three_way_result = decltype(synthesized_three_way_comparator{}(std::declval(), - std::declval())); - -#endif // __TBB_CPP20_COMPARISONS_PRESENT - -// Check if the type T is implicitly OR explicitly convertible to U -template -concept relaxed_convertible_to = std::constructible_from; - -template -concept adaptive_same_as = -#if __TBB_STRICT_CONSTRAINTS - std::same_as; -#else - std::convertible_to; -#endif -#endif // __TBB_CPP20_CONCEPTS_PRESENT - -template -auto invoke(F&& f, Args&&... args) -#if __TBB_CPP17_INVOKE_PRESENT - noexcept(std::is_nothrow_invocable_v) - -> std::invoke_result_t -{ - return std::invoke(std::forward(f), std::forward(args)...); -} -#else // __TBB_CPP17_INVOKE_PRESENT - noexcept(noexcept(std::forward(f)(std::forward(args)...))) - -> decltype(std::forward(f)(std::forward(args)...)) -{ - return std::forward(f)(std::forward(args)...); -} -#endif // __TBB_CPP17_INVOKE_PRESENT - -} // namespace d0 - -namespace d1 { - -class delegate_base { -public: - virtual bool operator()() const = 0; - virtual ~delegate_base() {} -}; - -template -class delegated_function : public delegate_base { -public: - delegated_function(FuncType& f) : my_func(f) {} - - bool operator()() const override { - return my_func(); - } - -private: - FuncType &my_func; -}; -} // namespace d1 - -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__utils_H diff --git a/include/oneapi/tbb/detail/_waitable_atomic.h b/include/oneapi/tbb/detail/_waitable_atomic.h deleted file mode 100644 index fa7280a..0000000 --- a/include/oneapi/tbb/detail/_waitable_atomic.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - Copyright (c) 2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_detail__address_waiters_H -#define __TBB_detail__address_waiters_H - -#include "_utils.h" - -namespace tbb { -namespace detail { - -namespace r1 { -TBB_EXPORT void __TBB_EXPORTED_FUNC wait_on_address(void* address, d1::delegate_base& wakeup_condition, std::uintptr_t context); -TBB_EXPORT void __TBB_EXPORTED_FUNC notify_by_address(void* address, std::uintptr_t context); -TBB_EXPORT void __TBB_EXPORTED_FUNC notify_by_address_one(void* address); -TBB_EXPORT void __TBB_EXPORTED_FUNC notify_by_address_all(void* address); -} // namespace r1 - -namespace d1 { - -template -void adaptive_wait_on_address(void* address, Predicate wakeup_condition, std::uintptr_t context) { - if (!timed_spin_wait_until(wakeup_condition)) { - d1::delegated_function pred(wakeup_condition); - r1::wait_on_address(address, pred, context); - } -} - -template -class waitable_atomic { -public: - waitable_atomic() = default; - - explicit waitable_atomic(T value) : my_atomic(value) {} - - waitable_atomic(const waitable_atomic&) = delete; - waitable_atomic& operator=(const waitable_atomic&) = delete; - - T load(std::memory_order order) const noexcept { - return my_atomic.load(order); - } - - T exchange(T desired) noexcept { - return my_atomic.exchange(desired); - } - - void wait(T old, std::uintptr_t context, std::memory_order order) { - auto wakeup_condition = [&] { return my_atomic.load(order) != old; }; - if (!timed_spin_wait_until(wakeup_condition)) { - // We need to use while here, because notify_all() will wake up all threads - // But predicate for them might be false - d1::delegated_function pred(wakeup_condition); - do { - r1::wait_on_address(this, pred, context); - } while (!wakeup_condition()); - } - } - - void wait_until(T expected, std::uintptr_t context, std::memory_order order) { - auto wakeup_condition = [&] { return my_atomic.load(order) == expected; }; - if (!timed_spin_wait_until(wakeup_condition)) { - // We need to use while here, because notify_all() will wake up all threads - // But predicate for them might be false - d1::delegated_function pred(wakeup_condition); - do { - r1::wait_on_address(this, pred, context); - } while (!wakeup_condition()); - } - } - - void notify_relaxed(std::uintptr_t context) { - r1::notify_by_address(this, context); - } - - void notify_one_relaxed() { - r1::notify_by_address_one(this); - } - - // TODO: consider adding following interfaces: - // store(desired, memory_order) - // notify_all_relaxed() - -private: - std::atomic my_atomic{}; -}; - -} // namespace d1 -} // namespace detail -} // namespace tbb - -#endif // __TBB_detail__address_waiters_H diff --git a/include/oneapi/tbb/enumerable_thread_specific.h b/include/oneapi/tbb/enumerable_thread_specific.h deleted file mode 100644 index 34bcab6..0000000 --- a/include/oneapi/tbb/enumerable_thread_specific.h +++ /dev/null @@ -1,1113 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_enumerable_thread_specific_H -#define __TBB_enumerable_thread_specific_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_assert.h" -#include "detail/_template_helpers.h" -#include "detail/_aligned_space.h" - -#include "concurrent_vector.h" -#include "tbb_allocator.h" -#include "cache_aligned_allocator.h" -#include "profiling.h" - -#include -#include -#include // memcpy -#include // std::ptrdiff_t - -#include "task.h" // for task::suspend_point - -#if _WIN32 || _WIN64 -#include -#else -#include -#endif - -namespace tbb { -namespace detail { -namespace d1 { - -//! enum for selecting between single key and key-per-instance versions -enum ets_key_usage_type { - ets_key_per_instance - , ets_no_key -#if __TBB_RESUMABLE_TASKS - , ets_suspend_aware -#endif -}; - -// Forward declaration to use in internal classes -template -class enumerable_thread_specific; - -template -struct internal_ets_key_selector { - using key_type = std::thread::id; - static key_type current_key() { - return std::this_thread::get_id(); - } -}; - -// Intel Compiler on OSX cannot create atomics objects that instantiated from non-fundamental types -#if __INTEL_COMPILER && __APPLE__ -template<> -struct internal_ets_key_selector { - using key_type = std::size_t; - static key_type current_key() { - auto id = std::this_thread::get_id(); - return reinterpret_cast(id); - } -}; -#endif - -template -struct ets_key_selector : internal_ets_key_selector {}; - -#if __TBB_RESUMABLE_TASKS -template <> -struct ets_key_selector { - using key_type = suspend_point; - static key_type current_key() { - return r1::current_suspend_point(); - } -}; -#endif - -template -class ets_base : detail::no_copy { -protected: - using key_type = typename ets_key_selector::key_type; - -public: - struct slot; - struct array { - array* next; - std::size_t lg_size; - slot& at( std::size_t k ) { - return (reinterpret_cast(reinterpret_cast(this+1)))[k]; - } - std::size_t size() const { return std::size_t(1) << lg_size; } - std::size_t mask() const { return size() - 1; } - std::size_t start( std::size_t h ) const { - return h >> (8 * sizeof(std::size_t) - lg_size); - } - }; - struct slot { - std::atomic key; - void* ptr; - bool empty() const { return key.load(std::memory_order_relaxed) == key_type(); } - bool match( key_type k ) const { return key.load(std::memory_order_relaxed) == k; } - bool claim( key_type k ) { - // TODO: maybe claim ptr, because key_type is not guaranteed to fit into word size - key_type expected = key_type(); - return key.compare_exchange_strong(expected, k); - } - }; - -protected: - //! Root of linked list of arrays of decreasing size. - /** nullptr if and only if my_count==0. - Each array in the list is half the size of its predecessor. */ - std::atomic my_root; - std::atomic my_count; - - virtual void* create_local() = 0; - virtual void* create_array(std::size_t _size) = 0; // _size in bytes - virtual void free_array(void* ptr, std::size_t _size) = 0; // _size in bytes - - array* allocate( std::size_t lg_size ) { - std::size_t n = std::size_t(1) << lg_size; - array* a = static_cast(create_array(sizeof(array) + n * sizeof(slot))); - a->lg_size = lg_size; - std::memset( a + 1, 0, n * sizeof(slot) ); - return a; - } - void deallocate(array* a) { - std::size_t n = std::size_t(1) << (a->lg_size); - free_array( static_cast(a), std::size_t(sizeof(array) + n * sizeof(slot)) ); - } - - ets_base() : my_root{nullptr}, my_count{0} {} - virtual ~ets_base(); // g++ complains if this is not virtual - - void* table_lookup( bool& exists ); - void table_clear(); - // The following functions are not used in concurrent context, - // so we don't need synchronization and ITT annotations there. - template - void table_elementwise_copy( const ets_base& other, - void*(*add_element)(ets_base&, void*) ) { - __TBB_ASSERT(!my_root.load(std::memory_order_relaxed), nullptr); - __TBB_ASSERT(!my_count.load(std::memory_order_relaxed), nullptr); - if( !other.my_root.load(std::memory_order_relaxed) ) return; - array* root = allocate(other.my_root.load(std::memory_order_relaxed)->lg_size); - my_root.store(root, std::memory_order_relaxed); - root->next = nullptr; - my_count.store(other.my_count.load(std::memory_order_relaxed), std::memory_order_relaxed); - std::size_t mask = root->mask(); - for( array* r = other.my_root.load(std::memory_order_relaxed); r; r = r->next ) { - for( std::size_t i = 0; i < r->size(); ++i ) { - slot& s1 = r->at(i); - if( !s1.empty() ) { - for( std::size_t j = root->start(std::hash{}(s1.key.load(std::memory_order_relaxed))); ; j = (j+1)&mask ) { - slot& s2 = root->at(j); - if( s2.empty() ) { - s2.ptr = add_element(static_cast&>(*this), s1.ptr); - s2.key.store(s1.key.load(std::memory_order_relaxed), std::memory_order_relaxed); - break; - } - else if( s2.match(s1.key.load(std::memory_order_relaxed)) ) - break; - } - } - } - } - } - void table_swap( ets_base& other ) { - __TBB_ASSERT(this!=&other, "Don't swap an instance with itself"); - swap_atomics_relaxed(my_root, other.my_root); - swap_atomics_relaxed(my_count, other.my_count); - } -}; - -template -ets_base::~ets_base() { - __TBB_ASSERT(!my_root.load(std::memory_order_relaxed), nullptr); -} - -template -void ets_base::table_clear() { - while ( array* r = my_root.load(std::memory_order_relaxed) ) { - my_root.store(r->next, std::memory_order_relaxed); - deallocate(r); - } - my_count.store(0, std::memory_order_relaxed); -} - -template -void* ets_base::table_lookup( bool& exists ) { - const key_type k = ets_key_selector::current_key(); - - __TBB_ASSERT(k != key_type(), nullptr); - void* found; - std::size_t h = std::hash{}(k); - for( array* r = my_root.load(std::memory_order_acquire); r; r = r->next ) { - call_itt_notify(acquired,r); - std::size_t mask=r->mask(); - for(std::size_t i = r->start(h); ;i=(i+1)&mask) { - slot& s = r->at(i); - if( s.empty() ) break; - if( s.match(k) ) { - if( r == my_root.load(std::memory_order_acquire) ) { - // Success at top level - exists = true; - return s.ptr; - } else { - // Success at some other level. Need to insert at top level. - exists = true; - found = s.ptr; - goto insert; - } - } - } - } - // Key does not yet exist. The density of slots in the table does not exceed 0.5, - // for if this will occur a new table is allocated with double the current table - // size, which is swapped in as the new root table. So an empty slot is guaranteed. - exists = false; - found = create_local(); - { - std::size_t c = ++my_count; - array* r = my_root.load(std::memory_order_acquire); - call_itt_notify(acquired,r); - if( !r || c > r->size()/2 ) { - std::size_t s = r ? r->lg_size : 2; - while( c > std::size_t(1)<<(s-1) ) ++s; - array* a = allocate(s); - for(;;) { - a->next = r; - call_itt_notify(releasing,a); - array* new_r = r; - if( my_root.compare_exchange_strong(new_r, a) ) break; - call_itt_notify(acquired, new_r); - __TBB_ASSERT(new_r != nullptr, nullptr); - if( new_r->lg_size >= s ) { - // Another thread inserted an equal or bigger array, so our array is superfluous. - deallocate(a); - break; - } - r = new_r; - } - } - } - insert: - // Whether a slot has been found in an older table, or if it has been inserted at this level, - // it has already been accounted for in the total. Guaranteed to be room for it, and it is - // not present, so search for empty slot and use it. - array* ir = my_root.load(std::memory_order_acquire); - call_itt_notify(acquired, ir); - std::size_t mask = ir->mask(); - for(std::size_t i = ir->start(h);; i = (i+1)&mask) { - slot& s = ir->at(i); - if( s.empty() ) { - if( s.claim(k) ) { - s.ptr = found; - return found; - } - } - } -} - -//! Specialization that exploits native TLS -template <> -class ets_base: public ets_base { - using super = ets_base; -#if _WIN32||_WIN64 -#if __TBB_WIN8UI_SUPPORT - using tls_key_t = DWORD; - void create_key() { my_key = FlsAlloc(nullptr); } - void destroy_key() { FlsFree(my_key); } - void set_tls(void * value) { FlsSetValue(my_key, (LPVOID)value); } - void* get_tls() { return (void *)FlsGetValue(my_key); } -#else - using tls_key_t = DWORD; - void create_key() { my_key = TlsAlloc(); } - void destroy_key() { TlsFree(my_key); } - void set_tls(void * value) { TlsSetValue(my_key, (LPVOID)value); } - void* get_tls() { return (void *)TlsGetValue(my_key); } -#endif -#else - using tls_key_t = pthread_key_t; - void create_key() { pthread_key_create(&my_key, nullptr); } - void destroy_key() { pthread_key_delete(my_key); } - void set_tls( void * value ) const { pthread_setspecific(my_key, value); } - void* get_tls() const { return pthread_getspecific(my_key); } -#endif - tls_key_t my_key; - virtual void* create_local() override = 0; - virtual void* create_array(std::size_t _size) override = 0; // _size in bytes - virtual void free_array(void* ptr, std::size_t _size) override = 0; // size in bytes -protected: - ets_base() {create_key();} - ~ets_base() {destroy_key();} - void* table_lookup( bool& exists ) { - void* found = get_tls(); - if( found ) { - exists=true; - } else { - found = super::table_lookup(exists); - set_tls(found); - } - return found; - } - void table_clear() { - destroy_key(); - create_key(); - super::table_clear(); - } - void table_swap( ets_base& other ) { - using std::swap; - __TBB_ASSERT(this!=&other, "Don't swap an instance with itself"); - swap(my_key, other.my_key); - super::table_swap(other); - } -}; - -//! Random access iterator for traversing the thread local copies. -template< typename Container, typename Value > -class enumerable_thread_specific_iterator -{ - //! current position in the concurrent_vector - - Container *my_container; - typename Container::size_type my_index; - mutable Value *my_value; - - template - friend bool operator==( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ); - - template - friend bool operator<( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ); - - template - friend std::ptrdiff_t operator-( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ); - - template - friend class enumerable_thread_specific_iterator; - -public: - //! STL support - using difference_type = std::ptrdiff_t; - using value_type = Value; - using pointer = Value*; - using reference = Value&; - using iterator_category = std::random_access_iterator_tag; - - enumerable_thread_specific_iterator( const Container &container, typename Container::size_type index ) : - my_container(&const_cast(container)), my_index(index), my_value(nullptr) {} - - //! Default constructor - enumerable_thread_specific_iterator() : my_container(nullptr), my_index(0), my_value(nullptr) {} - - template - enumerable_thread_specific_iterator( const enumerable_thread_specific_iterator& other ) : - my_container( other.my_container ), my_index( other.my_index), my_value( const_cast(other.my_value) ) {} - - enumerable_thread_specific_iterator operator+( std::ptrdiff_t offset ) const { - return enumerable_thread_specific_iterator(*my_container, my_index + offset); - } - - friend enumerable_thread_specific_iterator operator+( std::ptrdiff_t offset, enumerable_thread_specific_iterator v ) { - return enumerable_thread_specific_iterator(*v.my_container, v.my_index + offset); - } - - enumerable_thread_specific_iterator &operator+=( std::ptrdiff_t offset ) { - my_index += offset; - my_value = nullptr; - return *this; - } - - enumerable_thread_specific_iterator operator-( std::ptrdiff_t offset ) const { - return enumerable_thread_specific_iterator( *my_container, my_index-offset ); - } - - enumerable_thread_specific_iterator &operator-=( std::ptrdiff_t offset ) { - my_index -= offset; - my_value = nullptr; - return *this; - } - - Value& operator*() const { - Value* value = my_value; - if( !value ) { - value = my_value = (*my_container)[my_index].value(); - } - __TBB_ASSERT( value==(*my_container)[my_index].value(), "corrupt cache" ); - return *value; - } - - Value& operator[]( std::ptrdiff_t k ) const { - return *(*my_container)[my_index + k].value(); - } - - Value* operator->() const {return &operator*();} - - enumerable_thread_specific_iterator& operator++() { - ++my_index; - my_value = nullptr; - return *this; - } - - enumerable_thread_specific_iterator& operator--() { - --my_index; - my_value = nullptr; - return *this; - } - - //! Post increment - enumerable_thread_specific_iterator operator++(int) { - enumerable_thread_specific_iterator result = *this; - ++my_index; - my_value = nullptr; - return result; - } - - //! Post decrement - enumerable_thread_specific_iterator operator--(int) { - enumerable_thread_specific_iterator result = *this; - --my_index; - my_value = nullptr; - return result; - } -}; - -template -bool operator==( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ) { - return i.my_index == j.my_index && i.my_container == j.my_container; -} - -template -bool operator!=( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ) { - return !(i==j); -} - -template -bool operator<( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ) { - return i.my_index -bool operator>( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ) { - return j -bool operator>=( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ) { - return !(i -bool operator<=( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ) { - return !(j -std::ptrdiff_t operator-( const enumerable_thread_specific_iterator& i, - const enumerable_thread_specific_iterator& j ) { - return i.my_index-j.my_index; -} - -template -class segmented_iterator -{ - template - friend bool operator==(const segmented_iterator& i, const segmented_iterator& j); - - template - friend bool operator!=(const segmented_iterator& i, const segmented_iterator& j); - - template - friend class segmented_iterator; - -public: - segmented_iterator() {my_segcont = nullptr;} - - segmented_iterator( const SegmentedContainer& _segmented_container ) : - my_segcont(const_cast(&_segmented_container)), - outer_iter(my_segcont->end()) { } - - ~segmented_iterator() {} - - using InnerContainer = typename SegmentedContainer::value_type; - using inner_iterator = typename InnerContainer::iterator; - using outer_iterator = typename SegmentedContainer::iterator; - - // STL support - // TODO: inherit all types from segmented container? - using difference_type = std::ptrdiff_t; - using value_type = Value; - using size_type = typename SegmentedContainer::size_type; - using pointer = Value*; - using reference = Value&; - using iterator_category = std::input_iterator_tag; - - // Copy Constructor - template - segmented_iterator(const segmented_iterator& other) : - my_segcont(other.my_segcont), - outer_iter(other.outer_iter), - // can we assign a default-constructed iterator to inner if we're at the end? - inner_iter(other.inner_iter) - {} - - // assignment - template - segmented_iterator& operator=( const segmented_iterator& other) { - my_segcont = other.my_segcont; - outer_iter = other.outer_iter; - if(outer_iter != my_segcont->end()) inner_iter = other.inner_iter; - return *this; - } - - // allow assignment of outer iterator to segmented iterator. Once it is - // assigned, move forward until a non-empty inner container is found or - // the end of the outer container is reached. - segmented_iterator& operator=(const outer_iterator& new_outer_iter) { - __TBB_ASSERT(my_segcont != nullptr, nullptr); - // check that this iterator points to something inside the segmented container - for(outer_iter = new_outer_iter ;outer_iter!=my_segcont->end(); ++outer_iter) { - if( !outer_iter->empty() ) { - inner_iter = outer_iter->begin(); - break; - } - } - return *this; - } - - // pre-increment - segmented_iterator& operator++() { - advance_me(); - return *this; - } - - // post-increment - segmented_iterator operator++(int) { - segmented_iterator tmp = *this; - operator++(); - return tmp; - } - - bool operator==(const outer_iterator& other_outer) const { - __TBB_ASSERT(my_segcont != nullptr, nullptr); - return (outer_iter == other_outer && - (outer_iter == my_segcont->end() || inner_iter == outer_iter->begin())); - } - - bool operator!=(const outer_iterator& other_outer) const { - return !operator==(other_outer); - - } - - // (i)* RHS - reference operator*() const { - __TBB_ASSERT(my_segcont != nullptr, nullptr); - __TBB_ASSERT(outer_iter != my_segcont->end(), "Dereferencing a pointer at end of container"); - __TBB_ASSERT(inner_iter != outer_iter->end(), nullptr); // should never happen - return *inner_iter; - } - - // i-> - pointer operator->() const { return &operator*();} - -private: - SegmentedContainer* my_segcont; - outer_iterator outer_iter; - inner_iterator inner_iter; - - void advance_me() { - __TBB_ASSERT(my_segcont != nullptr, nullptr); - __TBB_ASSERT(outer_iter != my_segcont->end(), nullptr); // not true if there are no inner containers - __TBB_ASSERT(inner_iter != outer_iter->end(), nullptr); // not true if the inner containers are all empty. - ++inner_iter; - while(inner_iter == outer_iter->end() && ++outer_iter != my_segcont->end()) { - inner_iter = outer_iter->begin(); - } - } -}; // segmented_iterator - -template -bool operator==( const segmented_iterator& i, - const segmented_iterator& j ) { - if(i.my_segcont != j.my_segcont) return false; - if(i.my_segcont == nullptr) return true; - if(i.outer_iter != j.outer_iter) return false; - if(i.outer_iter == i.my_segcont->end()) return true; - return i.inner_iter == j.inner_iter; -} - -// != -template -bool operator!=( const segmented_iterator& i, - const segmented_iterator& j ) { - return !(i==j); -} - -template -struct construct_by_default: no_assign { - void construct(void*where) {new(where) T();} // C++ note: the () in T() ensure zero initialization. - construct_by_default( int ) {} -}; - -template -struct construct_by_exemplar: no_assign { - const T exemplar; - void construct(void*where) {new(where) T(exemplar);} - construct_by_exemplar( const T& t ) : exemplar(t) {} - construct_by_exemplar( T&& t ) : exemplar(std::move(t)) {} -}; - -template -struct construct_by_finit: no_assign { - Finit f; - void construct(void* where) {new(where) T(f());} - construct_by_finit( Finit&& f_ ) : f(std::move(f_)) {} -}; - -template -struct construct_by_args: no_assign { - stored_pack pack; - void construct(void* where) { - call( [where](const typename std::decay

::type&... args ){ - new(where) T(args...); - }, pack ); - } - construct_by_args( P&& ... args ) : pack(std::forward

(args)...) {} -}; - -// storage for initialization function pointer -// TODO: consider removing the template parameter T here and in callback_leaf -class callback_base { -public: - // Clone *this - virtual callback_base* clone() const = 0; - // Destruct and free *this - virtual void destroy() = 0; - // Need virtual destructor to satisfy GCC compiler warning - virtual ~callback_base() { } - // Construct T at where - virtual void construct(void* where) = 0; -}; - -template -class callback_leaf: public callback_base, Constructor { - template callback_leaf( P&& ... params ) : Constructor(std::forward

(params)...) {} - // TODO: make the construction/destruction consistent (use allocator.construct/destroy) - using my_allocator_type = typename tbb::tbb_allocator; - - callback_base* clone() const override { - return make(*this); - } - - void destroy() override { - my_allocator_type alloc; - tbb::detail::allocator_traits::destroy(alloc, this); - tbb::detail::allocator_traits::deallocate(alloc, this, 1); - } - - void construct(void* where) override { - Constructor::construct(where); - } - -public: - template - static callback_base* make( P&& ... params ) { - void* where = my_allocator_type().allocate(1); - return new(where) callback_leaf( std::forward

(params)... ); - } -}; - -//! Template for recording construction of objects in table -/** All maintenance of the space will be done explicitly on push_back, - and all thread local copies must be destroyed before the concurrent - vector is deleted. - - The flag is_built is initialized to false. When the local is - successfully-constructed, set the flag to true or call value_committed(). - If the constructor throws, the flag will be false. -*/ -template -struct ets_element { - detail::aligned_space my_space; - bool is_built; - ets_element() { is_built = false; } // not currently-built - U* value() { return my_space.begin(); } - U* value_committed() { is_built = true; return my_space.begin(); } - ~ets_element() { - if(is_built) { - my_space.begin()->~U(); - is_built = false; - } - } -}; - -// A predicate that can be used for a compile-time compatibility check of ETS instances -// Ideally, it should have been declared inside the ETS class, but unfortunately -// in that case VS2013 does not enable the variadic constructor. -template struct is_compatible_ets : std::false_type {}; -template -struct is_compatible_ets< T, enumerable_thread_specific > : std::is_same {}; - -// A predicate that checks whether, for a variable 'foo' of type T, foo() is a valid expression -template using has_empty_braces_operator = decltype(std::declval()()); -template using is_callable_no_args = supports; - -//! 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 clear()) so the address of a copy is invariant. - - the contained objects need not have operator=() defined if combine 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 accessed via TLS storage for speed. - - outside of parallel contexts, the contents of all thread-local copies are accessible by iterator or using combine or combine_each methods - -@par Segmented iterator - When the thread-local objects are containers with input_iterators defined, 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 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 , - ets_key_usage_type ETS_key_type=ets_no_key > -class enumerable_thread_specific: ets_base { - - template friend class enumerable_thread_specific; - - using padded_element = padded>; - - //! A generic range, used to create range objects from the iterators - template - class generic_range_type: public blocked_range { - public: - using value_type = T; - using reference = T&; - using const_reference = const T&; - using iterator = I; - using difference_type = std::ptrdiff_t; - - generic_range_type( I begin_, I end_, std::size_t grainsize_ = 1) : blocked_range(begin_,end_,grainsize_) {} - template - generic_range_type( const generic_range_type& r) : blocked_range(r.begin(),r.end(),r.grainsize()) {} - generic_range_type( generic_range_type& r, split ) : blocked_range(r,split()) {} - }; - - using allocator_traits_type = tbb::detail::allocator_traits; - - using padded_allocator_type = typename allocator_traits_type::template rebind_alloc; - using internal_collection_type = tbb::concurrent_vector< padded_element, padded_allocator_type >; - - callback_base *my_construct_callback; - - internal_collection_type my_locals; - - // TODO: consider unifying the callback mechanism for all create_local* methods below - // (likely non-compatible and requires interface version increase) - void* create_local() override { - padded_element& lref = *my_locals.grow_by(1); - my_construct_callback->construct(lref.value()); - return lref.value_committed(); - } - - static void* create_local_by_copy( ets_base& base, void* p ) { - enumerable_thread_specific& ets = static_cast(base); - padded_element& lref = *ets.my_locals.grow_by(1); - new(lref.value()) T(*static_cast(p)); - return lref.value_committed(); - } - - static void* create_local_by_move( ets_base& base, void* p ) { - enumerable_thread_specific& ets = static_cast(base); - padded_element& lref = *ets.my_locals.grow_by(1); - new(lref.value()) T(std::move(*static_cast(p))); - return lref.value_committed(); - } - - using array_allocator_type = typename allocator_traits_type::template rebind_alloc; - - // _size is in bytes - void* create_array(std::size_t _size) override { - std::size_t nelements = (_size + sizeof(uintptr_t) -1) / sizeof(uintptr_t); - return array_allocator_type().allocate(nelements); - } - - void free_array( void* _ptr, std::size_t _size) override { - std::size_t nelements = (_size + sizeof(uintptr_t) -1) / sizeof(uintptr_t); - array_allocator_type().deallocate( reinterpret_cast(_ptr),nelements); - } - -public: - - //! Basic types - using value_type = T; - using allocator_type = Allocator; - using size_type = typename internal_collection_type::size_type; - using difference_type = typename internal_collection_type::difference_type; - using reference = value_type&; - using const_reference = const value_type&; - - using pointer = typename allocator_traits_type::pointer; - using const_pointer = typename allocator_traits_type::const_pointer; - - // Iterator types - using iterator = enumerable_thread_specific_iterator; - using const_iterator = enumerable_thread_specific_iterator; - - // Parallel range types - using range_type = generic_range_type; - using const_range_type = generic_range_type; - - //! Default constructor. Each local instance of T is default constructed. - enumerable_thread_specific() : my_construct_callback( - callback_leaf >::make(/*dummy argument*/0) - ){} - - //! Constructor with initializer functor. Each local instance of T is constructed by T(finit()). - template ::type>::value>::type> - explicit enumerable_thread_specific( Finit finit ) : my_construct_callback( - callback_leaf >::make( std::move(finit) ) - ){} - - //! Constructor with exemplar. Each local instance of T is copy-constructed from the exemplar. - explicit enumerable_thread_specific( const T& exemplar ) : my_construct_callback( - callback_leaf >::make( exemplar ) - ){} - - explicit enumerable_thread_specific( T&& exemplar ) : my_construct_callback( - callback_leaf >::make( std::move(exemplar) ) - ){} - - //! Variadic constructor with initializer arguments. Each local instance of T is constructed by T(args...) - template ::type>::value - && !is_compatible_ets::type>::value - && !std::is_same::type>::value - >::type> - enumerable_thread_specific( P1&& arg1, P&& ... args ) : my_construct_callback( - callback_leaf >::make( std::forward(arg1), std::forward

(args)... ) - ){} - - //! Destructor - ~enumerable_thread_specific() { - if(my_construct_callback) my_construct_callback->destroy(); - // Deallocate the hash table before overridden free_array() becomes inaccessible - this->ets_base::table_clear(); - } - - //! returns reference to local, discarding exists - reference local() { - bool exists; - return local(exists); - } - - //! Returns reference to calling thread's local copy, creating one if necessary - reference local(bool& exists) { - void* ptr = this->table_lookup(exists); - return *(T*)ptr; - } - - //! Get the number of local copies - size_type size() const { return my_locals.size(); } - - //! true if there have been no local copies created - bool empty() const { return my_locals.empty(); } - - //! begin iterator - iterator begin() { return iterator( my_locals, 0 ); } - //! end iterator - iterator end() { return iterator(my_locals, my_locals.size() ); } - - //! begin const iterator - const_iterator begin() const { return const_iterator(my_locals, 0); } - - //! end const iterator - const_iterator end() const { return const_iterator(my_locals, my_locals.size()); } - - //! Get range for parallel algorithms - range_type range( std::size_t grainsize=1 ) { return range_type( begin(), end(), grainsize ); } - - //! Get const range for parallel algorithms - const_range_type range( std::size_t grainsize=1 ) const { return const_range_type( begin(), end(), grainsize ); } - - //! Destroys local copies - void clear() { - my_locals.clear(); - this->table_clear(); - // callback is not destroyed - } - -private: - template - void internal_copy(const enumerable_thread_specific& other) { - // this tests is_compatible_ets - static_assert( (is_compatible_ets::type>::value), "is_compatible_ets fails" ); - // Initialize my_construct_callback first, so that it is valid even if rest of this routine throws an exception. - my_construct_callback = other.my_construct_callback->clone(); - __TBB_ASSERT(my_locals.size()==0, nullptr); - my_locals.reserve(other.size()); - this->table_elementwise_copy( other, create_local_by_copy ); - } - - void internal_swap(enumerable_thread_specific& other) { - using std::swap; - __TBB_ASSERT( this!=&other, nullptr); - swap(my_construct_callback, other.my_construct_callback); - // concurrent_vector::swap() preserves storage space, - // so addresses to the vector kept in ETS hash table remain valid. - swap(my_locals, other.my_locals); - this->ets_base::table_swap(other); - } - - template - void internal_move(enumerable_thread_specific&& other) { - static_assert( (is_compatible_ets::type>::value), "is_compatible_ets fails" ); - my_construct_callback = other.my_construct_callback; - other.my_construct_callback = nullptr; - __TBB_ASSERT(my_locals.size()==0, nullptr); - my_locals.reserve(other.size()); - this->table_elementwise_copy( other, create_local_by_move ); - } - -public: - enumerable_thread_specific( const enumerable_thread_specific& other ) - : ets_base() /* prevents GCC warnings with -Wextra */ - { - internal_copy(other); - } - - template - enumerable_thread_specific( const enumerable_thread_specific& other ) - { - internal_copy(other); - } - - enumerable_thread_specific( enumerable_thread_specific&& other ) : my_construct_callback() - { - // TODO: use internal_move correctly here - internal_swap(other); - } - - template - enumerable_thread_specific( enumerable_thread_specific&& other ) : my_construct_callback() - { - internal_move(std::move(other)); - } - - enumerable_thread_specific& operator=( const enumerable_thread_specific& other ) - { - if( this != &other ) { - this->clear(); - my_construct_callback->destroy(); - internal_copy( other ); - } - return *this; - } - - template - enumerable_thread_specific& operator=( const enumerable_thread_specific& other ) - { - __TBB_ASSERT( static_cast(this)!=static_cast(&other), nullptr); // Objects of different types - this->clear(); - my_construct_callback->destroy(); - internal_copy(other); - return *this; - } - - enumerable_thread_specific& operator=( enumerable_thread_specific&& other ) - { - if( this != &other ) { - // TODO: use internal_move correctly here - internal_swap(other); - } - return *this; - } - - template - enumerable_thread_specific& operator=( enumerable_thread_specific&& other ) - { - __TBB_ASSERT( static_cast(this)!=static_cast(&other), nullptr); // Objects of different types - this->clear(); - my_construct_callback->destroy(); - internal_move(std::move(other)); - return *this; - } - - // CombineFunc has signature T(T,T) or T(const T&, const T&) - template - T combine(CombineFunc f_combine) { - if(begin() == end()) { - ets_element location; - my_construct_callback->construct(location.value()); - return *location.value_committed(); - } - const_iterator ci = begin(); - T my_result = *ci; - while(++ci != end()) - my_result = f_combine( my_result, *ci ); - return my_result; - } - - // combine_func_t takes T by value or by [const] reference, and returns nothing - template - void combine_each(CombineFunc f_combine) { - for(iterator ci = begin(); ci != end(); ++ci) { - f_combine( *ci ); - } - } - -}; // enumerable_thread_specific - -template< typename Container > -class flattened2d { - // This intermediate typedef is to address issues with VC7.1 compilers - using conval_type = typename Container::value_type; - -public: - //! Basic types - using size_type = typename conval_type::size_type; - using difference_type = typename conval_type::difference_type; - using allocator_type = typename conval_type::allocator_type; - using value_type = typename conval_type::value_type; - using reference = typename conval_type::reference; - using const_reference = typename conval_type::const_reference; - using pointer = typename conval_type::pointer; - using const_pointer = typename conval_type::const_pointer; - - using iterator = segmented_iterator; - using const_iterator = segmented_iterator; - - flattened2d( const Container &c, typename Container::const_iterator b, typename Container::const_iterator e ) : - my_container(const_cast(&c)), my_begin(b), my_end(e) { } - - explicit flattened2d( const Container &c ) : - my_container(const_cast(&c)), my_begin(c.begin()), my_end(c.end()) { } - - iterator begin() { return iterator(*my_container) = my_begin; } - iterator end() { return iterator(*my_container) = my_end; } - const_iterator begin() const { return const_iterator(*my_container) = my_begin; } - const_iterator end() const { return const_iterator(*my_container) = my_end; } - - size_type size() const { - size_type tot_size = 0; - for(typename Container::const_iterator i = my_begin; i != my_end; ++i) { - tot_size += i->size(); - } - return tot_size; - } - -private: - Container *my_container; - typename Container::const_iterator my_begin; - typename Container::const_iterator my_end; -}; - -template -flattened2d flatten2d(const Container &c, const typename Container::const_iterator b, const typename Container::const_iterator e) { - return flattened2d(c, b, e); -} - -template -flattened2d flatten2d(const Container &c) { - return flattened2d(c); -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::enumerable_thread_specific; -using detail::d1::flattened2d; -using detail::d1::flatten2d; -// ets enum keys -using detail::d1::ets_key_usage_type; -using detail::d1::ets_key_per_instance; -using detail::d1::ets_no_key; -#if __TBB_RESUMABLE_TASKS -using detail::d1::ets_suspend_aware; -#endif -} // inline namespace v1 - -} // namespace tbb - -#endif // __TBB_enumerable_thread_specific_H - diff --git a/include/oneapi/tbb/flow_graph.h b/include/oneapi/tbb/flow_graph.h deleted file mode 100644 index 2df4b14..0000000 --- a/include/oneapi/tbb/flow_graph.h +++ /dev/null @@ -1,3376 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_flow_graph_H -#define __TBB_flow_graph_H - -#include -#include -#include - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "spin_mutex.h" -#include "null_mutex.h" -#include "spin_rw_mutex.h" -#include "null_rw_mutex.h" -#include "detail/_pipeline_filters.h" -#include "detail/_task.h" -#include "detail/_small_object_pool.h" -#include "cache_aligned_allocator.h" -#include "detail/_exception.h" -#include "detail/_template_helpers.h" -#include "detail/_aggregator.h" -#include "detail/_allocator_traits.h" -#include "detail/_utils.h" -#include "profiling.h" -#include "task_arena.h" - -#if TBB_USE_PROFILING_TOOLS && ( __unix__ || __APPLE__ ) - #if __INTEL_COMPILER - // Disabled warning "routine is both inline and noinline" - #pragma warning (push) - #pragma warning( disable: 2196 ) - #endif - #define __TBB_NOINLINE_SYM __attribute__((noinline)) -#else - #define __TBB_NOINLINE_SYM -#endif - -#include -#include -#include -#if __TBB_CPP20_CONCEPTS_PRESENT -#include -#endif - -/** @file - \brief The graph related classes and functions - - There are some applications that best express dependencies as messages - passed between nodes in a graph. These messages may contain data or - simply act as signals that a predecessors has completed. The graph - class and its associated node classes can be used to express such - applications. -*/ - -namespace tbb { -namespace detail { - -namespace d1 { - -//! An enumeration the provides the two most common concurrency levels: unlimited and serial -enum concurrency { unlimited = 0, serial = 1 }; - -//! A generic null type -struct null_type {}; - -//! An empty class used for messages that mean "I'm done" -class continue_msg {}; - -} // namespace d1 - -#if __TBB_CPP20_CONCEPTS_PRESENT -namespace d0 { - -template -concept node_body_return_type = std::same_as || - std::convertible_to; - -// TODO: consider using std::invocable here -template -concept continue_node_body = std::copy_constructible && - requires( Body& body, const tbb::detail::d1::continue_msg& v ) { - { body(v) } -> node_body_return_type; - }; - -template -concept function_node_body = std::copy_constructible && - std::invocable && - node_body_return_type, Output>; - -template -concept join_node_function_object = std::copy_constructible && - std::invocable && - std::convertible_to, Key>; - -template -concept input_node_body = std::copy_constructible && - requires( Body& body, tbb::detail::d1::flow_control& fc ) { - { body(fc) } -> adaptive_same_as; - }; - -template -concept multifunction_node_body = std::copy_constructible && - std::invocable; - -template -concept sequencer = std::copy_constructible && - std::invocable && - std::convertible_to, std::size_t>; - -template -concept async_node_body = std::copy_constructible && - std::invocable; - -} // namespace d0 -#endif // __TBB_CPP20_CONCEPTS_PRESENT - -namespace d1 { - -//! Forward declaration section -template< typename T > class sender; -template< typename T > class receiver; -class continue_receiver; - -template< typename T, typename U > class limiter_node; // needed for resetting decrementer - -template class successor_cache; -template class broadcast_cache; -template class round_robin_cache; -template class predecessor_cache; -template class reservable_predecessor_cache; - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET -namespace order { -struct following; -struct preceding; -} -template struct node_set; -#endif - - -} // namespace d1 -} // namespace detail -} // namespace tbb - -//! The graph class -#include "detail/_flow_graph_impl.h" - -namespace tbb { -namespace detail { -namespace d1 { - -static inline std::pair order_tasks(graph_task* first, graph_task* second) { - if (second->priority > first->priority) - return std::make_pair(second, first); - return std::make_pair(first, second); -} - -// submit task if necessary. Returns the non-enqueued task if there is one. -static inline graph_task* combine_tasks(graph& g, graph_task* left, graph_task* right) { - // if no RHS task, don't change left. - if (right == nullptr) return left; - // right != nullptr - if (left == nullptr) return right; - if (left == SUCCESSFULLY_ENQUEUED) return right; - // left contains a task - if (right != SUCCESSFULLY_ENQUEUED) { - // both are valid tasks - auto tasks_pair = order_tasks(left, right); - spawn_in_graph_arena(g, *tasks_pair.first); - return tasks_pair.second; - } - return left; -} - -//! Pure virtual template class that defines a sender of messages of type T -template< typename T > -class sender { -public: - virtual ~sender() {} - - //! Request an item from the sender - virtual bool try_get( T & ) { return false; } - - //! Reserves an item in the sender - virtual bool try_reserve( T & ) { return false; } - - //! Releases the reserved item - virtual bool try_release( ) { return false; } - - //! Consumes the reserved item - virtual bool try_consume( ) { return false; } - -protected: - //! The output type of this sender - typedef T output_type; - - //! The successor type for this node - typedef receiver successor_type; - - //! Add a new successor to this node - virtual bool register_successor( successor_type &r ) = 0; - - //! Removes a successor from this node - virtual bool remove_successor( successor_type &r ) = 0; - - template - friend bool register_successor(sender& s, receiver& r); - - template - friend bool remove_successor (sender& s, receiver& r); -}; // class sender - -template -bool register_successor(sender& s, receiver& r) { - return s.register_successor(r); -} - -template -bool remove_successor(sender& s, receiver& r) { - return s.remove_successor(r); -} - -//! Pure virtual template class that defines a receiver of messages of type T -template< typename T > -class receiver { -public: - //! Destructor - virtual ~receiver() {} - - //! Put an item to the receiver - bool try_put( const T& t ) { - graph_task *res = try_put_task(t); - if (!res) return false; - if (res != SUCCESSFULLY_ENQUEUED) spawn_in_graph_arena(graph_reference(), *res); - return true; - } - - //! put item to successor; return task to run the successor if possible. -protected: - //! The input type of this receiver - typedef T input_type; - - //! The predecessor type for this node - typedef sender predecessor_type; - - template< typename R, typename B > friend class run_and_put_task; - template< typename X, typename Y > friend class broadcast_cache; - template< typename X, typename Y > friend class round_robin_cache; - virtual graph_task *try_put_task(const T& t) = 0; - virtual graph& graph_reference() const = 0; - - template friend class successor_cache; - virtual bool is_continue_receiver() { return false; } - - // TODO revamp: reconsider the inheritance and move node priority out of receiver - virtual node_priority_t priority() const { return no_priority; } - - //! Add a predecessor to the node - virtual bool register_predecessor( predecessor_type & ) { return false; } - - //! Remove a predecessor from the node - virtual bool remove_predecessor( predecessor_type & ) { return false; } - - template - friend bool register_predecessor(receiver& r, sender& s); - template - friend bool remove_predecessor (receiver& r, sender& s); -}; // class receiver - -template -bool register_predecessor(receiver& r, sender& s) { - return r.register_predecessor(s); -} - -template -bool remove_predecessor(receiver& r, sender& s) { - return r.remove_predecessor(s); -} - -//! Base class for receivers of completion messages -/** These receivers automatically reset, but cannot be explicitly waited on */ -class continue_receiver : public receiver< continue_msg > { -protected: - - //! Constructor - explicit continue_receiver( int number_of_predecessors, node_priority_t a_priority ) { - my_predecessor_count = my_initial_predecessor_count = number_of_predecessors; - my_current_count = 0; - my_priority = a_priority; - } - - //! Copy constructor - continue_receiver( const continue_receiver& src ) : receiver() { - my_predecessor_count = my_initial_predecessor_count = src.my_initial_predecessor_count; - my_current_count = 0; - my_priority = src.my_priority; - } - - //! Increments the trigger threshold - bool register_predecessor( predecessor_type & ) override { - spin_mutex::scoped_lock l(my_mutex); - ++my_predecessor_count; - return true; - } - - //! Decrements the trigger threshold - /** Does not check to see if the removal of the predecessor now makes the current count - exceed the new threshold. So removing a predecessor while the graph is active can cause - unexpected results. */ - bool remove_predecessor( predecessor_type & ) override { - spin_mutex::scoped_lock l(my_mutex); - --my_predecessor_count; - return true; - } - - //! The input type - typedef continue_msg input_type; - - //! The predecessor type for this node - typedef receiver::predecessor_type predecessor_type; - - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - // execute body is supposed to be too small to create a task for. - graph_task* try_put_task( const input_type & ) override { - { - spin_mutex::scoped_lock l(my_mutex); - if ( ++my_current_count < my_predecessor_count ) - return SUCCESSFULLY_ENQUEUED; - else - my_current_count = 0; - } - graph_task* res = execute(); - return res? res : SUCCESSFULLY_ENQUEUED; - } - - spin_mutex my_mutex; - int my_predecessor_count; - int my_current_count; - int my_initial_predecessor_count; - node_priority_t my_priority; - // the friend declaration in the base class did not eliminate the "protected class" - // error in gcc 4.1.2 - template friend class limiter_node; - - virtual void reset_receiver( reset_flags f ) { - my_current_count = 0; - if (f & rf_clear_edges) { - my_predecessor_count = my_initial_predecessor_count; - } - } - - //! Does whatever should happen when the threshold is reached - /** This should be very fast or else spawn a task. This is - called while the sender is blocked in the try_put(). */ - virtual graph_task* execute() = 0; - template friend class successor_cache; - bool is_continue_receiver() override { return true; } - - node_priority_t priority() const override { return my_priority; } -}; // class continue_receiver - -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - template - K key_from_message( const T &t ) { - return t.key(); - } -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - -} // d1 -} // detail -} // tbb - -#include "detail/_flow_graph_trace_impl.h" -#include "detail/_hash_compare.h" - -namespace tbb { -namespace detail { -namespace d1 { - -#include "detail/_flow_graph_body_impl.h" -#include "detail/_flow_graph_cache_impl.h" -#include "detail/_flow_graph_types_impl.h" - -using namespace graph_policy_namespace; - -template -graph_iterator::graph_iterator(C *g, bool begin) : my_graph(g), current_node(nullptr) -{ - if (begin) current_node = my_graph->my_nodes; - //else it is an end iterator by default -} - -template -typename graph_iterator::reference graph_iterator::operator*() const { - __TBB_ASSERT(current_node, "graph_iterator at end"); - return *operator->(); -} - -template -typename graph_iterator::pointer graph_iterator::operator->() const { - return current_node; -} - -template -void graph_iterator::internal_forward() { - if (current_node) current_node = current_node->next; -} - -//! Constructs a graph with isolated task_group_context -inline graph::graph() : my_wait_context(0), my_nodes(nullptr), my_nodes_last(nullptr), my_task_arena(nullptr) { - prepare_task_arena(); - own_context = true; - cancelled = false; - caught_exception = false; - my_context = new (r1::cache_aligned_allocate(sizeof(task_group_context))) task_group_context(FLOW_TASKS); - fgt_graph(this); - my_is_active = true; -} - -inline graph::graph(task_group_context& use_this_context) : - my_wait_context(0), my_context(&use_this_context), my_nodes(nullptr), my_nodes_last(nullptr), my_task_arena(nullptr) { - prepare_task_arena(); - own_context = false; - cancelled = false; - caught_exception = false; - fgt_graph(this); - my_is_active = true; -} - -inline graph::~graph() { - wait_for_all(); - if (own_context) { - my_context->~task_group_context(); - r1::cache_aligned_deallocate(my_context); - } - delete my_task_arena; -} - -inline void graph::reserve_wait() { - my_wait_context.reserve(); - fgt_reserve_wait(this); -} - -inline void graph::release_wait() { - fgt_release_wait(this); - my_wait_context.release(); -} - -inline void graph::register_node(graph_node *n) { - n->next = nullptr; - { - spin_mutex::scoped_lock lock(nodelist_mutex); - n->prev = my_nodes_last; - if (my_nodes_last) my_nodes_last->next = n; - my_nodes_last = n; - if (!my_nodes) my_nodes = n; - } -} - -inline void graph::remove_node(graph_node *n) { - { - spin_mutex::scoped_lock lock(nodelist_mutex); - __TBB_ASSERT(my_nodes && my_nodes_last, "graph::remove_node: Error: no registered nodes"); - if (n->prev) n->prev->next = n->next; - if (n->next) n->next->prev = n->prev; - if (my_nodes_last == n) my_nodes_last = n->prev; - if (my_nodes == n) my_nodes = n->next; - } - n->prev = n->next = nullptr; -} - -inline void graph::reset( reset_flags f ) { - // reset context - deactivate_graph(*this); - - my_context->reset(); - cancelled = false; - caught_exception = false; - // reset all the nodes comprising the graph - for(iterator ii = begin(); ii != end(); ++ii) { - graph_node *my_p = &(*ii); - my_p->reset_node(f); - } - // Reattach the arena. Might be useful to run the graph in a particular task_arena - // while not limiting graph lifetime to a single task_arena::execute() call. - prepare_task_arena( /*reinit=*/true ); - activate_graph(*this); -} - -inline void graph::cancel() { - my_context->cancel_group_execution(); -} - -inline graph::iterator graph::begin() { return iterator(this, true); } - -inline graph::iterator graph::end() { return iterator(this, false); } - -inline graph::const_iterator graph::begin() const { return const_iterator(this, true); } - -inline graph::const_iterator graph::end() const { return const_iterator(this, false); } - -inline graph::const_iterator graph::cbegin() const { return const_iterator(this, true); } - -inline graph::const_iterator graph::cend() const { return const_iterator(this, false); } - -inline graph_node::graph_node(graph& g) : my_graph(g) { - my_graph.register_node(this); -} - -inline graph_node::~graph_node() { - my_graph.remove_node(this); -} - -#include "detail/_flow_graph_node_impl.h" - - -//! An executable node that acts as a source, i.e. it has no predecessors - -template < typename Output > - __TBB_requires(std::copyable) -class input_node : public graph_node, public sender< Output > { -public: - //! The type of the output message, which is complete - typedef Output output_type; - - //! The type of successors of this node - typedef typename sender::successor_type successor_type; - - // Input node has no input type - typedef null_type input_type; - - //! Constructor for a node with a successor - template< typename Body > - __TBB_requires(input_node_body) - __TBB_NOINLINE_SYM input_node( graph &g, Body body ) - : graph_node(g), my_active(false) - , my_body( new input_body_leaf< output_type, Body>(body) ) - , my_init_body( new input_body_leaf< output_type, Body>(body) ) - , my_successors(this), my_reserved(false), my_has_cached_item(false) - { - fgt_node_with_body(CODEPTR(), FLOW_INPUT_NODE, &this->my_graph, - static_cast *>(this), this->my_body); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_requires(input_node_body) - input_node( const node_set& successors, Body body ) - : input_node(successors.graph_reference(), body) - { - make_edges(*this, successors); - } -#endif - - //! Copy constructor - __TBB_NOINLINE_SYM input_node( const input_node& src ) - : graph_node(src.my_graph), sender() - , my_active(false) - , my_body(src.my_init_body->clone()), my_init_body(src.my_init_body->clone()) - , my_successors(this), my_reserved(false), my_has_cached_item(false) - { - fgt_node_with_body(CODEPTR(), FLOW_INPUT_NODE, &this->my_graph, - static_cast *>(this), this->my_body); - } - - //! The destructor - ~input_node() { delete my_body; delete my_init_body; } - - //! Add a new successor to this node - bool register_successor( successor_type &r ) override { - spin_mutex::scoped_lock lock(my_mutex); - my_successors.register_successor(r); - if ( my_active ) - spawn_put(); - return true; - } - - //! Removes a successor from this node - bool remove_successor( successor_type &r ) override { - spin_mutex::scoped_lock lock(my_mutex); - my_successors.remove_successor(r); - return true; - } - - //! Request an item from the node - bool try_get( output_type &v ) override { - spin_mutex::scoped_lock lock(my_mutex); - if ( my_reserved ) - return false; - - if ( my_has_cached_item ) { - v = my_cached_item; - my_has_cached_item = false; - return true; - } - // we've been asked to provide an item, but we have none. enqueue a task to - // provide one. - if ( my_active ) - spawn_put(); - return false; - } - - //! Reserves an item. - bool try_reserve( output_type &v ) override { - spin_mutex::scoped_lock lock(my_mutex); - if ( my_reserved ) { - return false; - } - - if ( my_has_cached_item ) { - v = my_cached_item; - my_reserved = true; - return true; - } else { - return false; - } - } - - //! Release a reserved item. - /** true = item has been released and so remains in sender, dest must request or reserve future items */ - bool try_release( ) override { - spin_mutex::scoped_lock lock(my_mutex); - __TBB_ASSERT( my_reserved && my_has_cached_item, "releasing non-existent reservation" ); - my_reserved = false; - if(!my_successors.empty()) - spawn_put(); - return true; - } - - //! Consumes a reserved item - bool try_consume( ) override { - spin_mutex::scoped_lock lock(my_mutex); - __TBB_ASSERT( my_reserved && my_has_cached_item, "consuming non-existent reservation" ); - my_reserved = false; - my_has_cached_item = false; - if ( !my_successors.empty() ) { - spawn_put(); - } - return true; - } - - //! Activates a node that was created in the inactive state - void activate() { - spin_mutex::scoped_lock lock(my_mutex); - my_active = true; - if (!my_successors.empty()) - spawn_put(); - } - - template - Body copy_function_object() { - input_body &body_ref = *this->my_body; - return dynamic_cast< input_body_leaf & >(body_ref).get_body(); - } - -protected: - - //! resets the input_node to its initial state - void reset_node( reset_flags f) override { - my_active = false; - my_reserved = false; - my_has_cached_item = false; - - if(f & rf_clear_edges) my_successors.clear(); - if(f & rf_reset_bodies) { - input_body *tmp = my_init_body->clone(); - delete my_body; - my_body = tmp; - } - } - -private: - spin_mutex my_mutex; - bool my_active; - input_body *my_body; - input_body *my_init_body; - broadcast_cache< output_type > my_successors; - bool my_reserved; - bool my_has_cached_item; - output_type my_cached_item; - - // used by apply_body_bypass, can invoke body of node. - bool try_reserve_apply_body(output_type &v) { - spin_mutex::scoped_lock lock(my_mutex); - if ( my_reserved ) { - return false; - } - if ( !my_has_cached_item ) { - flow_control control; - - fgt_begin_body( my_body ); - - my_cached_item = (*my_body)(control); - my_has_cached_item = !control.is_pipeline_stopped; - - fgt_end_body( my_body ); - } - if ( my_has_cached_item ) { - v = my_cached_item; - my_reserved = true; - return true; - } else { - return false; - } - } - - graph_task* create_put_task() { - small_object_allocator allocator{}; - typedef input_node_task_bypass< input_node > task_type; - graph_task* t = allocator.new_object(my_graph, allocator, *this); - my_graph.reserve_wait(); - return t; - } - - //! Spawns a task that applies the body - void spawn_put( ) { - if(is_graph_active(this->my_graph)) { - spawn_in_graph_arena(this->my_graph, *create_put_task()); - } - } - - friend class input_node_task_bypass< input_node >; - //! Applies the body. Returning SUCCESSFULLY_ENQUEUED okay; forward_task_bypass will handle it. - graph_task* apply_body_bypass( ) { - output_type v; - if ( !try_reserve_apply_body(v) ) - return nullptr; - - graph_task *last_task = my_successors.try_put_task(v); - if ( last_task ) - try_consume(); - else - try_release(); - return last_task; - } -}; // class input_node - -//! Implements a function node that supports Input -> Output -template - __TBB_requires(std::default_initializable && - std::copy_constructible && - std::copy_constructible) -class function_node - : public graph_node - , public function_input< Input, Output, Policy, cache_aligned_allocator > - , public function_output -{ - typedef cache_aligned_allocator internals_allocator; - -public: - typedef Input input_type; - typedef Output output_type; - typedef function_input input_impl_type; - typedef function_input_queue input_queue_type; - typedef function_output fOutput_type; - typedef typename input_impl_type::predecessor_type predecessor_type; - typedef typename fOutput_type::successor_type successor_type; - - using input_impl_type::my_predecessors; - - //! Constructor - // input_queue_type is allocated here, but destroyed in the function_input_base. - // TODO: pass the graph_buffer_policy to the function_input_base so it can all - // be done in one place. This would be an interface-breaking change. - template< typename Body > - __TBB_requires(function_node_body) - __TBB_NOINLINE_SYM function_node( graph &g, size_t concurrency, - Body body, Policy = Policy(), node_priority_t a_priority = no_priority ) - : graph_node(g), input_impl_type(g, concurrency, body, a_priority), - fOutput_type(g) { - fgt_node_with_body( CODEPTR(), FLOW_FUNCTION_NODE, &this->my_graph, - static_cast *>(this), static_cast *>(this), this->my_body ); - } - - template - __TBB_requires(function_node_body) - function_node( graph& g, size_t concurrency, Body body, node_priority_t a_priority ) - : function_node(g, concurrency, body, Policy(), a_priority) {} - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_requires(function_node_body) - function_node( const node_set& nodes, size_t concurrency, Body body, - Policy p = Policy(), node_priority_t a_priority = no_priority ) - : function_node(nodes.graph_reference(), concurrency, body, p, a_priority) { - make_edges_in_order(nodes, *this); - } - - template - __TBB_requires(function_node_body) - function_node( const node_set& nodes, size_t concurrency, Body body, node_priority_t a_priority ) - : function_node(nodes, concurrency, body, Policy(), a_priority) {} -#endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - - //! Copy constructor - __TBB_NOINLINE_SYM function_node( const function_node& src ) : - graph_node(src.my_graph), - input_impl_type(src), - fOutput_type(src.my_graph) { - fgt_node_with_body( CODEPTR(), FLOW_FUNCTION_NODE, &this->my_graph, - static_cast *>(this), static_cast *>(this), this->my_body ); - } - -protected: - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - using input_impl_type::try_put_task; - - broadcast_cache &successors () override { return fOutput_type::my_successors; } - - void reset_node(reset_flags f) override { - input_impl_type::reset_function_input(f); - // TODO: use clear() instead. - if(f & rf_clear_edges) { - successors().clear(); - my_predecessors.clear(); - } - __TBB_ASSERT(!(f & rf_clear_edges) || successors().empty(), "function_node successors not empty"); - __TBB_ASSERT(this->my_predecessors.empty(), "function_node predecessors not empty"); - } - -}; // class function_node - -//! implements a function node that supports Input -> (set of outputs) -// Output is a tuple of output types. -template - __TBB_requires(std::default_initializable && - std::copy_constructible) -class multifunction_node : - public graph_node, - public multifunction_input - < - Input, - typename wrap_tuple_elements< - std::tuple_size::value, // #elements in tuple - multifunction_output, // wrap this around each element - Output // the tuple providing the types - >::type, - Policy, - cache_aligned_allocator - > -{ - typedef cache_aligned_allocator internals_allocator; - -protected: - static const int N = std::tuple_size::value; -public: - typedef Input input_type; - typedef null_type output_type; - typedef typename wrap_tuple_elements::type output_ports_type; - typedef multifunction_input< - input_type, output_ports_type, Policy, internals_allocator> input_impl_type; - typedef function_input_queue input_queue_type; -private: - using input_impl_type::my_predecessors; -public: - template - __TBB_requires(multifunction_node_body) - __TBB_NOINLINE_SYM multifunction_node( - graph &g, size_t concurrency, - Body body, Policy = Policy(), node_priority_t a_priority = no_priority - ) : graph_node(g), input_impl_type(g, concurrency, body, a_priority) { - fgt_multioutput_node_with_body( - CODEPTR(), FLOW_MULTIFUNCTION_NODE, - &this->my_graph, static_cast *>(this), - this->output_ports(), this->my_body - ); - } - - template - __TBB_requires(multifunction_node_body) - __TBB_NOINLINE_SYM multifunction_node(graph& g, size_t concurrency, Body body, node_priority_t a_priority) - : multifunction_node(g, concurrency, body, Policy(), a_priority) {} - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_requires(multifunction_node_body) - __TBB_NOINLINE_SYM multifunction_node(const node_set& nodes, size_t concurrency, Body body, - Policy p = Policy(), node_priority_t a_priority = no_priority) - : multifunction_node(nodes.graph_reference(), concurrency, body, p, a_priority) { - make_edges_in_order(nodes, *this); - } - - template - __TBB_requires(multifunction_node_body) - __TBB_NOINLINE_SYM multifunction_node(const node_set& nodes, size_t concurrency, Body body, node_priority_t a_priority) - : multifunction_node(nodes, concurrency, body, Policy(), a_priority) {} -#endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - - __TBB_NOINLINE_SYM multifunction_node( const multifunction_node &other) : - graph_node(other.my_graph), input_impl_type(other) { - fgt_multioutput_node_with_body( CODEPTR(), FLOW_MULTIFUNCTION_NODE, - &this->my_graph, static_cast *>(this), - this->output_ports(), this->my_body ); - } - - // all the guts are in multifunction_input... -protected: - void reset_node(reset_flags f) override { input_impl_type::reset(f); } -}; // multifunction_node - -//! split_node: accepts a tuple as input, forwards each element of the tuple to its -// successors. The node has unlimited concurrency, so it does not reject inputs. -template -class split_node : public graph_node, public receiver { - static const int N = std::tuple_size::value; - typedef receiver base_type; -public: - typedef TupleType input_type; - typedef typename wrap_tuple_elements< - N, // #elements in tuple - multifunction_output, // wrap this around each element - TupleType // the tuple providing the types - >::type output_ports_type; - - __TBB_NOINLINE_SYM explicit split_node(graph &g) - : graph_node(g), - my_output_ports(init_output_ports::call(g, my_output_ports)) - { - fgt_multioutput_node(CODEPTR(), FLOW_SPLIT_NODE, &this->my_graph, - static_cast *>(this), this->output_ports()); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_NOINLINE_SYM split_node(const node_set& nodes) : split_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - __TBB_NOINLINE_SYM split_node(const split_node& other) - : graph_node(other.my_graph), base_type(other), - my_output_ports(init_output_ports::call(other.my_graph, my_output_ports)) - { - fgt_multioutput_node(CODEPTR(), FLOW_SPLIT_NODE, &this->my_graph, - static_cast *>(this), this->output_ports()); - } - - output_ports_type &output_ports() { return my_output_ports; } - -protected: - graph_task *try_put_task(const TupleType& t) override { - // Sending split messages in parallel is not justified, as overheads would prevail. - // Also, we do not have successors here. So we just tell the task returned here is successful. - return emit_element::emit_this(this->my_graph, t, output_ports()); - } - void reset_node(reset_flags f) override { - if (f & rf_clear_edges) - clear_element::clear_this(my_output_ports); - - __TBB_ASSERT(!(f & rf_clear_edges) || clear_element::this_empty(my_output_ports), "split_node reset failed"); - } - graph& graph_reference() const override { - return my_graph; - } - -private: - output_ports_type my_output_ports; -}; - -//! Implements an executable node that supports continue_msg -> Output -template > - __TBB_requires(std::copy_constructible) -class continue_node : public graph_node, public continue_input, - public function_output { -public: - typedef continue_msg input_type; - typedef Output output_type; - typedef continue_input input_impl_type; - typedef function_output fOutput_type; - typedef typename input_impl_type::predecessor_type predecessor_type; - typedef typename fOutput_type::successor_type successor_type; - - //! Constructor for executable node with continue_msg -> Output - template - __TBB_requires(continue_node_body) - __TBB_NOINLINE_SYM continue_node( - graph &g, - Body body, Policy = Policy(), node_priority_t a_priority = no_priority - ) : graph_node(g), input_impl_type( g, body, a_priority ), - fOutput_type(g) { - fgt_node_with_body( CODEPTR(), FLOW_CONTINUE_NODE, &this->my_graph, - - static_cast *>(this), - static_cast *>(this), this->my_body ); - } - - template - __TBB_requires(continue_node_body) - continue_node( graph& g, Body body, node_priority_t a_priority ) - : continue_node(g, body, Policy(), a_priority) {} - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_requires(continue_node_body) - continue_node( const node_set& nodes, Body body, - Policy p = Policy(), node_priority_t a_priority = no_priority ) - : continue_node(nodes.graph_reference(), body, p, a_priority ) { - make_edges_in_order(nodes, *this); - } - template - __TBB_requires(continue_node_body) - continue_node( const node_set& nodes, Body body, node_priority_t a_priority) - : continue_node(nodes, body, Policy(), a_priority) {} -#endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - - //! Constructor for executable node with continue_msg -> Output - template - __TBB_requires(continue_node_body) - __TBB_NOINLINE_SYM continue_node( - graph &g, int number_of_predecessors, - Body body, Policy = Policy(), node_priority_t a_priority = no_priority - ) : graph_node(g) - , input_impl_type(g, number_of_predecessors, body, a_priority), - fOutput_type(g) { - fgt_node_with_body( CODEPTR(), FLOW_CONTINUE_NODE, &this->my_graph, - static_cast *>(this), - static_cast *>(this), this->my_body ); - } - - template - __TBB_requires(continue_node_body) - continue_node( graph& g, int number_of_predecessors, Body body, node_priority_t a_priority) - : continue_node(g, number_of_predecessors, body, Policy(), a_priority) {} - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_requires(continue_node_body) - continue_node( const node_set& nodes, int number_of_predecessors, - Body body, Policy p = Policy(), node_priority_t a_priority = no_priority ) - : continue_node(nodes.graph_reference(), number_of_predecessors, body, p, a_priority) { - make_edges_in_order(nodes, *this); - } - - template - __TBB_requires(continue_node_body) - continue_node( const node_set& nodes, int number_of_predecessors, - Body body, node_priority_t a_priority ) - : continue_node(nodes, number_of_predecessors, body, Policy(), a_priority) {} -#endif - - //! Copy constructor - __TBB_NOINLINE_SYM continue_node( const continue_node& src ) : - graph_node(src.my_graph), input_impl_type(src), - function_output(src.my_graph) { - fgt_node_with_body( CODEPTR(), FLOW_CONTINUE_NODE, &this->my_graph, - static_cast *>(this), - static_cast *>(this), this->my_body ); - } - -protected: - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - using input_impl_type::try_put_task; - broadcast_cache &successors () override { return fOutput_type::my_successors; } - - void reset_node(reset_flags f) override { - input_impl_type::reset_receiver(f); - if(f & rf_clear_edges)successors().clear(); - __TBB_ASSERT(!(f & rf_clear_edges) || successors().empty(), "continue_node not reset"); - } -}; // continue_node - -//! Forwards messages of type T to all successors -template -class broadcast_node : public graph_node, public receiver, public sender { -public: - typedef T input_type; - typedef T output_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef typename sender::successor_type successor_type; -private: - broadcast_cache my_successors; -public: - - __TBB_NOINLINE_SYM explicit broadcast_node(graph& g) : graph_node(g), my_successors(this) { - fgt_node( CODEPTR(), FLOW_BROADCAST_NODE, &this->my_graph, - static_cast *>(this), static_cast *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - broadcast_node(const node_set& nodes) : broadcast_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM broadcast_node( const broadcast_node& src ) : broadcast_node(src.my_graph) {} - - //! Adds a successor - bool register_successor( successor_type &r ) override { - my_successors.register_successor( r ); - return true; - } - - //! Removes s as a successor - bool remove_successor( successor_type &r ) override { - my_successors.remove_successor( r ); - return true; - } - -protected: - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - //! build a task to run the successor if possible. Default is old behavior. - graph_task *try_put_task(const T& t) override { - graph_task *new_task = my_successors.try_put_task(t); - if (!new_task) new_task = SUCCESSFULLY_ENQUEUED; - return new_task; - } - - graph& graph_reference() const override { - return my_graph; - } - - void reset_node(reset_flags f) override { - if (f&rf_clear_edges) { - my_successors.clear(); - } - __TBB_ASSERT(!(f & rf_clear_edges) || my_successors.empty(), "Error resetting broadcast_node"); - } -}; // broadcast_node - -//! Forwards messages in arbitrary order -template -class buffer_node - : public graph_node - , public reservable_item_buffer< T, cache_aligned_allocator > - , public receiver, public sender -{ - typedef cache_aligned_allocator internals_allocator; - -public: - typedef T input_type; - typedef T output_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef typename sender::successor_type successor_type; - typedef buffer_node class_type; - -protected: - typedef size_t size_type; - round_robin_cache< T, null_rw_mutex > my_successors; - - friend class forward_task_bypass< class_type >; - - enum op_type {reg_succ, rem_succ, req_item, res_item, rel_res, con_res, put_item, try_fwd_task - }; - - // implements the aggregator_operation concept - class buffer_operation : public aggregated_operation< buffer_operation > { - public: - char type; - T* elem; - graph_task* ltask; - successor_type *r; - - buffer_operation(const T& e, op_type t) : type(char(t)) - , elem(const_cast(&e)) , ltask(nullptr) - , r(nullptr) - {} - buffer_operation(op_type t) : type(char(t)), elem(nullptr), ltask(nullptr), r(nullptr) {} - }; - - bool forwarder_busy; - typedef aggregating_functor handler_type; - friend class aggregating_functor; - aggregator< handler_type, buffer_operation> my_aggregator; - - virtual void handle_operations(buffer_operation *op_list) { - handle_operations_impl(op_list, this); - } - - template - void handle_operations_impl(buffer_operation *op_list, derived_type* derived) { - __TBB_ASSERT(static_cast(derived) == this, "'this' is not a base class for derived"); - - buffer_operation *tmp = nullptr; - bool try_forwarding = false; - while (op_list) { - tmp = op_list; - op_list = op_list->next; - switch (tmp->type) { - case reg_succ: internal_reg_succ(tmp); try_forwarding = true; break; - case rem_succ: internal_rem_succ(tmp); break; - case req_item: internal_pop(tmp); break; - case res_item: internal_reserve(tmp); break; - case rel_res: internal_release(tmp); try_forwarding = true; break; - case con_res: internal_consume(tmp); try_forwarding = true; break; - case put_item: try_forwarding = internal_push(tmp); break; - case try_fwd_task: internal_forward_task(tmp); break; - } - } - - derived->order(); - - if (try_forwarding && !forwarder_busy) { - if(is_graph_active(this->my_graph)) { - forwarder_busy = true; - typedef forward_task_bypass task_type; - small_object_allocator allocator{}; - graph_task* new_task = allocator.new_object(graph_reference(), allocator, *this); - my_graph.reserve_wait(); - // tmp should point to the last item handled by the aggregator. This is the operation - // the handling thread enqueued. So modifying that record will be okay. - // TODO revamp: check that the issue is still present - // workaround for icc bug (at least 12.0 and 13.0) - // error: function "tbb::flow::interfaceX::combine_tasks" cannot be called with the given argument list - // argument types are: (graph, graph_task *, graph_task *) - graph_task *z = tmp->ltask; - graph &g = this->my_graph; - tmp->ltask = combine_tasks(g, z, new_task); // in case the op generated a task - } - } - } // handle_operations - - inline graph_task *grab_forwarding_task( buffer_operation &op_data) { - return op_data.ltask; - } - - inline bool enqueue_forwarding_task(buffer_operation &op_data) { - graph_task *ft = grab_forwarding_task(op_data); - if(ft) { - spawn_in_graph_arena(graph_reference(), *ft); - return true; - } - return false; - } - - //! This is executed by an enqueued task, the "forwarder" - virtual graph_task *forward_task() { - buffer_operation op_data(try_fwd_task); - graph_task *last_task = nullptr; - do { - op_data.status = WAIT; - op_data.ltask = nullptr; - my_aggregator.execute(&op_data); - - // workaround for icc bug - graph_task *xtask = op_data.ltask; - graph& g = this->my_graph; - last_task = combine_tasks(g, last_task, xtask); - } while (op_data.status ==SUCCEEDED); - return last_task; - } - - //! Register successor - virtual void internal_reg_succ(buffer_operation *op) { - __TBB_ASSERT(op->r, nullptr); - my_successors.register_successor(*(op->r)); - op->status.store(SUCCEEDED, std::memory_order_release); - } - - //! Remove successor - virtual void internal_rem_succ(buffer_operation *op) { - __TBB_ASSERT(op->r, nullptr); - my_successors.remove_successor(*(op->r)); - op->status.store(SUCCEEDED, std::memory_order_release); - } - -private: - void order() {} - - bool is_item_valid() { - return this->my_item_valid(this->my_tail - 1); - } - - void try_put_and_add_task(graph_task*& last_task) { - graph_task *new_task = my_successors.try_put_task(this->back()); - if (new_task) { - // workaround for icc bug - graph& g = this->my_graph; - last_task = combine_tasks(g, last_task, new_task); - this->destroy_back(); - } - } - -protected: - //! Tries to forward valid items to successors - virtual void internal_forward_task(buffer_operation *op) { - internal_forward_task_impl(op, this); - } - - template - void internal_forward_task_impl(buffer_operation *op, derived_type* derived) { - __TBB_ASSERT(static_cast(derived) == this, "'this' is not a base class for derived"); - - if (this->my_reserved || !derived->is_item_valid()) { - op->status.store(FAILED, std::memory_order_release); - this->forwarder_busy = false; - return; - } - // Try forwarding, giving each successor a chance - graph_task* last_task = nullptr; - size_type counter = my_successors.size(); - for (; counter > 0 && derived->is_item_valid(); --counter) - derived->try_put_and_add_task(last_task); - - op->ltask = last_task; // return task - if (last_task && !counter) { - op->status.store(SUCCEEDED, std::memory_order_release); - } - else { - op->status.store(FAILED, std::memory_order_release); - forwarder_busy = false; - } - } - - virtual bool internal_push(buffer_operation *op) { - __TBB_ASSERT(op->elem, nullptr); - this->push_back(*(op->elem)); - op->status.store(SUCCEEDED, std::memory_order_release); - return true; - } - - virtual void internal_pop(buffer_operation *op) { - __TBB_ASSERT(op->elem, nullptr); - if(this->pop_back(*(op->elem))) { - op->status.store(SUCCEEDED, std::memory_order_release); - } - else { - op->status.store(FAILED, std::memory_order_release); - } - } - - virtual void internal_reserve(buffer_operation *op) { - __TBB_ASSERT(op->elem, nullptr); - if(this->reserve_front(*(op->elem))) { - op->status.store(SUCCEEDED, std::memory_order_release); - } - else { - op->status.store(FAILED, std::memory_order_release); - } - } - - virtual void internal_consume(buffer_operation *op) { - this->consume_front(); - op->status.store(SUCCEEDED, std::memory_order_release); - } - - virtual void internal_release(buffer_operation *op) { - this->release_front(); - op->status.store(SUCCEEDED, std::memory_order_release); - } - -public: - //! Constructor - __TBB_NOINLINE_SYM explicit buffer_node( graph &g ) - : graph_node(g), reservable_item_buffer(), receiver(), - sender(), my_successors(this), forwarder_busy(false) - { - my_aggregator.initialize_handler(handler_type(this)); - fgt_node( CODEPTR(), FLOW_BUFFER_NODE, &this->my_graph, - static_cast *>(this), static_cast *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - buffer_node(const node_set& nodes) : buffer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - //! Copy constructor - __TBB_NOINLINE_SYM buffer_node( const buffer_node& src ) : buffer_node(src.my_graph) {} - - // - // message sender implementation - // - - //! Adds a new successor. - /** Adds successor r to the list of successors; may forward tasks. */ - bool register_successor( successor_type &r ) override { - buffer_operation op_data(reg_succ); - op_data.r = &r; - my_aggregator.execute(&op_data); - (void)enqueue_forwarding_task(op_data); - return true; - } - - //! Removes a successor. - /** Removes successor r from the list of successors. - It also calls r.remove_predecessor(*this) to remove this node as a predecessor. */ - bool remove_successor( successor_type &r ) override { - // TODO revamp: investigate why full qualification is necessary here - tbb::detail::d1::remove_predecessor(r, *this); - buffer_operation op_data(rem_succ); - op_data.r = &r; - my_aggregator.execute(&op_data); - // even though this operation does not cause a forward, if we are the handler, and - // a forward is scheduled, we may be the first to reach this point after the aggregator, - // and so should check for the task. - (void)enqueue_forwarding_task(op_data); - return true; - } - - //! Request an item from the buffer_node - /** true = v contains the returned item
- false = no item has been returned */ - bool try_get( T &v ) override { - buffer_operation op_data(req_item); - op_data.elem = &v; - my_aggregator.execute(&op_data); - (void)enqueue_forwarding_task(op_data); - return (op_data.status==SUCCEEDED); - } - - //! Reserves an item. - /** false = no item can be reserved
- true = an item is reserved */ - bool try_reserve( T &v ) override { - buffer_operation op_data(res_item); - op_data.elem = &v; - my_aggregator.execute(&op_data); - (void)enqueue_forwarding_task(op_data); - return (op_data.status==SUCCEEDED); - } - - //! Release a reserved item. - /** true = item has been released and so remains in sender */ - bool try_release() override { - buffer_operation op_data(rel_res); - my_aggregator.execute(&op_data); - (void)enqueue_forwarding_task(op_data); - return true; - } - - //! Consumes a reserved item. - /** true = item is removed from sender and reservation removed */ - bool try_consume() override { - buffer_operation op_data(con_res); - my_aggregator.execute(&op_data); - (void)enqueue_forwarding_task(op_data); - return true; - } - -protected: - - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - //! receive an item, return a task *if possible - graph_task *try_put_task(const T &t) override { - buffer_operation op_data(t, put_item); - my_aggregator.execute(&op_data); - graph_task *ft = grab_forwarding_task(op_data); - // sequencer_nodes can return failure (if an item has been previously inserted) - // We have to spawn the returned task if our own operation fails. - - if(ft && op_data.status ==FAILED) { - // we haven't succeeded queueing the item, but for some reason the - // call returned a task (if another request resulted in a successful - // forward this could happen.) Queue the task and reset the pointer. - spawn_in_graph_arena(graph_reference(), *ft); ft = nullptr; - } - else if(!ft && op_data.status ==SUCCEEDED) { - ft = SUCCESSFULLY_ENQUEUED; - } - return ft; - } - - graph& graph_reference() const override { - return my_graph; - } - -protected: - void reset_node( reset_flags f) override { - reservable_item_buffer::reset(); - // TODO: just clear structures - if (f&rf_clear_edges) { - my_successors.clear(); - } - forwarder_busy = false; - } -}; // buffer_node - -//! Forwards messages in FIFO order -template -class queue_node : public buffer_node { -protected: - typedef buffer_node base_type; - typedef typename base_type::size_type size_type; - typedef typename base_type::buffer_operation queue_operation; - typedef queue_node class_type; - -private: - template friend class buffer_node; - - bool is_item_valid() { - return this->my_item_valid(this->my_head); - } - - void try_put_and_add_task(graph_task*& last_task) { - graph_task *new_task = this->my_successors.try_put_task(this->front()); - if (new_task) { - // workaround for icc bug - graph& graph_ref = this->graph_reference(); - last_task = combine_tasks(graph_ref, last_task, new_task); - this->destroy_front(); - } - } - -protected: - void internal_forward_task(queue_operation *op) override { - this->internal_forward_task_impl(op, this); - } - - void internal_pop(queue_operation *op) override { - if ( this->my_reserved || !this->my_item_valid(this->my_head)){ - op->status.store(FAILED, std::memory_order_release); - } - else { - this->pop_front(*(op->elem)); - op->status.store(SUCCEEDED, std::memory_order_release); - } - } - void internal_reserve(queue_operation *op) override { - if (this->my_reserved || !this->my_item_valid(this->my_head)) { - op->status.store(FAILED, std::memory_order_release); - } - else { - this->reserve_front(*(op->elem)); - op->status.store(SUCCEEDED, std::memory_order_release); - } - } - void internal_consume(queue_operation *op) override { - this->consume_front(); - op->status.store(SUCCEEDED, std::memory_order_release); - } - -public: - typedef T input_type; - typedef T output_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef typename sender::successor_type successor_type; - - //! Constructor - __TBB_NOINLINE_SYM explicit queue_node( graph &g ) : base_type(g) { - fgt_node( CODEPTR(), FLOW_QUEUE_NODE, &(this->my_graph), - static_cast *>(this), - static_cast *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - queue_node( const node_set& nodes) : queue_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - //! Copy constructor - __TBB_NOINLINE_SYM queue_node( const queue_node& src) : base_type(src) { - fgt_node( CODEPTR(), FLOW_QUEUE_NODE, &(this->my_graph), - static_cast *>(this), - static_cast *>(this) ); - } - - -protected: - void reset_node( reset_flags f) override { - base_type::reset_node(f); - } -}; // queue_node - -//! Forwards messages in sequence order -template - __TBB_requires(std::copyable) -class sequencer_node : public queue_node { - function_body< T, size_t > *my_sequencer; - // my_sequencer should be a benign function and must be callable - // from a parallel context. Does this mean it needn't be reset? -public: - typedef T input_type; - typedef T output_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef typename sender::successor_type successor_type; - - //! Constructor - template< typename Sequencer > - __TBB_requires(sequencer) - __TBB_NOINLINE_SYM sequencer_node( graph &g, const Sequencer& s ) : queue_node(g), - my_sequencer(new function_body_leaf< T, size_t, Sequencer>(s) ) { - fgt_node( CODEPTR(), FLOW_SEQUENCER_NODE, &(this->my_graph), - static_cast *>(this), - static_cast *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_requires(sequencer) - sequencer_node( const node_set& nodes, const Sequencer& s) - : sequencer_node(nodes.graph_reference(), s) { - make_edges_in_order(nodes, *this); - } -#endif - - //! Copy constructor - __TBB_NOINLINE_SYM sequencer_node( const sequencer_node& src ) : queue_node(src), - my_sequencer( src.my_sequencer->clone() ) { - fgt_node( CODEPTR(), FLOW_SEQUENCER_NODE, &(this->my_graph), - static_cast *>(this), - static_cast *>(this) ); - } - - //! Destructor - ~sequencer_node() { delete my_sequencer; } - -protected: - typedef typename buffer_node::size_type size_type; - typedef typename buffer_node::buffer_operation sequencer_operation; - -private: - bool internal_push(sequencer_operation *op) override { - size_type tag = (*my_sequencer)(*(op->elem)); -#if !TBB_DEPRECATED_SEQUENCER_DUPLICATES - if (tag < this->my_head) { - // have already emitted a message with this tag - op->status.store(FAILED, std::memory_order_release); - return false; - } -#endif - // cannot modify this->my_tail now; the buffer would be inconsistent. - size_t new_tail = (tag+1 > this->my_tail) ? tag+1 : this->my_tail; - - if (this->size(new_tail) > this->capacity()) { - this->grow_my_array(this->size(new_tail)); - } - this->my_tail = new_tail; - - const op_stat res = this->place_item(tag, *(op->elem)) ? SUCCEEDED : FAILED; - op->status.store(res, std::memory_order_release); - return res ==SUCCEEDED; - } -}; // sequencer_node - -//! Forwards messages in priority order -template> -class priority_queue_node : public buffer_node { -public: - typedef T input_type; - typedef T output_type; - typedef buffer_node base_type; - typedef priority_queue_node class_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef typename sender::successor_type successor_type; - - //! Constructor - __TBB_NOINLINE_SYM explicit priority_queue_node( graph &g, const Compare& comp = Compare() ) - : buffer_node(g), compare(comp), mark(0) { - fgt_node( CODEPTR(), FLOW_PRIORITY_QUEUE_NODE, &(this->my_graph), - static_cast *>(this), - static_cast *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - priority_queue_node(const node_set& nodes, const Compare& comp = Compare()) - : priority_queue_node(nodes.graph_reference(), comp) { - make_edges_in_order(nodes, *this); - } -#endif - - //! Copy constructor - __TBB_NOINLINE_SYM priority_queue_node( const priority_queue_node &src ) - : buffer_node(src), mark(0) - { - fgt_node( CODEPTR(), FLOW_PRIORITY_QUEUE_NODE, &(this->my_graph), - static_cast *>(this), - static_cast *>(this) ); - } - -protected: - - void reset_node( reset_flags f) override { - mark = 0; - base_type::reset_node(f); - } - - typedef typename buffer_node::size_type size_type; - typedef typename buffer_node::item_type item_type; - typedef typename buffer_node::buffer_operation prio_operation; - - //! Tries to forward valid items to successors - void internal_forward_task(prio_operation *op) override { - this->internal_forward_task_impl(op, this); - } - - void handle_operations(prio_operation *op_list) override { - this->handle_operations_impl(op_list, this); - } - - bool internal_push(prio_operation *op) override { - prio_push(*(op->elem)); - op->status.store(SUCCEEDED, std::memory_order_release); - return true; - } - - void internal_pop(prio_operation *op) override { - // if empty or already reserved, don't pop - if ( this->my_reserved == true || this->my_tail == 0 ) { - op->status.store(FAILED, std::memory_order_release); - return; - } - - *(op->elem) = prio(); - op->status.store(SUCCEEDED, std::memory_order_release); - prio_pop(); - - } - - // pops the highest-priority item, saves copy - void internal_reserve(prio_operation *op) override { - if (this->my_reserved == true || this->my_tail == 0) { - op->status.store(FAILED, std::memory_order_release); - return; - } - this->my_reserved = true; - *(op->elem) = prio(); - reserved_item = *(op->elem); - op->status.store(SUCCEEDED, std::memory_order_release); - prio_pop(); - } - - void internal_consume(prio_operation *op) override { - op->status.store(SUCCEEDED, std::memory_order_release); - this->my_reserved = false; - reserved_item = input_type(); - } - - void internal_release(prio_operation *op) override { - op->status.store(SUCCEEDED, std::memory_order_release); - prio_push(reserved_item); - this->my_reserved = false; - reserved_item = input_type(); - } - -private: - template friend class buffer_node; - - void order() { - if (mark < this->my_tail) heapify(); - __TBB_ASSERT(mark == this->my_tail, "mark unequal after heapify"); - } - - bool is_item_valid() { - return this->my_tail > 0; - } - - void try_put_and_add_task(graph_task*& last_task) { - graph_task * new_task = this->my_successors.try_put_task(this->prio()); - if (new_task) { - // workaround for icc bug - graph& graph_ref = this->graph_reference(); - last_task = combine_tasks(graph_ref, last_task, new_task); - prio_pop(); - } - } - -private: - Compare compare; - size_type mark; - - input_type reserved_item; - - // in case a reheap has not been done after a push, check if the mark item is higher than the 0'th item - bool prio_use_tail() { - __TBB_ASSERT(mark <= this->my_tail, "mark outside bounds before test"); - return mark < this->my_tail && compare(this->get_my_item(0), this->get_my_item(this->my_tail - 1)); - } - - // prio_push: checks that the item will fit, expand array if necessary, put at end - void prio_push(const T &src) { - if ( this->my_tail >= this->my_array_size ) - this->grow_my_array( this->my_tail + 1 ); - (void) this->place_item(this->my_tail, src); - ++(this->my_tail); - __TBB_ASSERT(mark < this->my_tail, "mark outside bounds after push"); - } - - // prio_pop: deletes highest priority item from the array, and if it is item - // 0, move last item to 0 and reheap. If end of array, just destroy and decrement tail - // and mark. Assumes the array has already been tested for emptiness; no failure. - void prio_pop() { - if (prio_use_tail()) { - // there are newly pushed elements; last one higher than top - // copy the data - this->destroy_item(this->my_tail-1); - --(this->my_tail); - __TBB_ASSERT(mark <= this->my_tail, "mark outside bounds after pop"); - return; - } - this->destroy_item(0); - if(this->my_tail > 1) { - // push the last element down heap - __TBB_ASSERT(this->my_item_valid(this->my_tail - 1), nullptr); - this->move_item(0,this->my_tail - 1); - } - --(this->my_tail); - if(mark > this->my_tail) --mark; - if (this->my_tail > 1) // don't reheap for heap of size 1 - reheap(); - __TBB_ASSERT(mark <= this->my_tail, "mark outside bounds after pop"); - } - - const T& prio() { - return this->get_my_item(prio_use_tail() ? this->my_tail-1 : 0); - } - - // turn array into heap - void heapify() { - if(this->my_tail == 0) { - mark = 0; - return; - } - if (!mark) mark = 1; - for (; markmy_tail; ++mark) { // for each unheaped element - size_type cur_pos = mark; - input_type to_place; - this->fetch_item(mark,to_place); - do { // push to_place up the heap - size_type parent = (cur_pos-1)>>1; - if (!compare(this->get_my_item(parent), to_place)) - break; - this->move_item(cur_pos, parent); - cur_pos = parent; - } while( cur_pos ); - (void) this->place_item(cur_pos, to_place); - } - } - - // otherwise heapified array with new root element; rearrange to heap - void reheap() { - size_type cur_pos=0, child=1; - while (child < mark) { - size_type target = child; - if (child+1get_my_item(child), - this->get_my_item(child+1))) - ++target; - // target now has the higher priority child - if (compare(this->get_my_item(target), - this->get_my_item(cur_pos))) - break; - // swap - this->swap_items(cur_pos, target); - cur_pos = target; - child = (cur_pos<<1)+1; - } - } -}; // priority_queue_node - -//! Forwards messages only if the threshold has not been reached -/** This node forwards items until its threshold is reached. - It contains no buffering. If the downstream node rejects, the - message is dropped. */ -template< typename T, typename DecrementType=continue_msg > -class limiter_node : public graph_node, public receiver< T >, public sender< T > { -public: - typedef T input_type; - typedef T output_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef typename sender::successor_type successor_type; - //TODO: There is a lack of predefined types for its controlling "decrementer" port. It should be fixed later. - -private: - size_t my_threshold; - size_t my_count; // number of successful puts - size_t my_tries; // number of active put attempts - size_t my_future_decrement; // number of active decrement - reservable_predecessor_cache< T, spin_mutex > my_predecessors; - spin_mutex my_mutex; - broadcast_cache< T > my_successors; - - //! The internal receiver< DecrementType > that adjusts the count - threshold_regulator< limiter_node, DecrementType > decrement; - - graph_task* decrement_counter( long long delta ) { - if ( delta > 0 && size_t(delta) > my_threshold ) { - delta = my_threshold; - } - - { - spin_mutex::scoped_lock lock(my_mutex); - if ( delta > 0 && size_t(delta) > my_count ) { - if( my_tries > 0 ) { - my_future_decrement += (size_t(delta) - my_count); - } - my_count = 0; - } - else if ( delta < 0 && size_t(-delta) > my_threshold - my_count ) { - my_count = my_threshold; - } - else { - my_count -= size_t(delta); // absolute value of delta is sufficiently small - } - __TBB_ASSERT(my_count <= my_threshold, "counter values are truncated to be inside the [0, threshold] interval"); - } - return forward_task(); - } - - // Let threshold_regulator call decrement_counter() - friend class threshold_regulator< limiter_node, DecrementType >; - - friend class forward_task_bypass< limiter_node >; - - bool check_conditions() { // always called under lock - return ( my_count + my_tries < my_threshold && !my_predecessors.empty() && !my_successors.empty() ); - } - - // only returns a valid task pointer or nullptr, never SUCCESSFULLY_ENQUEUED - graph_task* forward_task() { - input_type v; - graph_task* rval = nullptr; - bool reserved = false; - - { - spin_mutex::scoped_lock lock(my_mutex); - if ( check_conditions() ) - ++my_tries; - else - return nullptr; - } - - //SUCCESS - // if we can reserve and can put, we consume the reservation - // we increment the count and decrement the tries - if ( (my_predecessors.try_reserve(v)) == true ) { - reserved = true; - if ( (rval = my_successors.try_put_task(v)) != nullptr ) { - { - spin_mutex::scoped_lock lock(my_mutex); - ++my_count; - if ( my_future_decrement ) { - if ( my_count > my_future_decrement ) { - my_count -= my_future_decrement; - my_future_decrement = 0; - } - else { - my_future_decrement -= my_count; - my_count = 0; - } - } - --my_tries; - my_predecessors.try_consume(); - if ( check_conditions() ) { - if ( is_graph_active(this->my_graph) ) { - typedef forward_task_bypass> task_type; - small_object_allocator allocator{}; - graph_task* rtask = allocator.new_object( my_graph, allocator, *this ); - my_graph.reserve_wait(); - spawn_in_graph_arena(graph_reference(), *rtask); - } - } - } - return rval; - } - } - //FAILURE - //if we can't reserve, we decrement the tries - //if we can reserve but can't put, we decrement the tries and release the reservation - { - spin_mutex::scoped_lock lock(my_mutex); - --my_tries; - if (reserved) my_predecessors.try_release(); - if ( check_conditions() ) { - if ( is_graph_active(this->my_graph) ) { - small_object_allocator allocator{}; - typedef forward_task_bypass> task_type; - graph_task* t = allocator.new_object(my_graph, allocator, *this); - my_graph.reserve_wait(); - __TBB_ASSERT(!rval, "Have two tasks to handle"); - return t; - } - } - return rval; - } - } - - void initialize() { - fgt_node( - CODEPTR(), FLOW_LIMITER_NODE, &this->my_graph, - static_cast *>(this), static_cast *>(&decrement), - static_cast *>(this) - ); - } - -public: - //! Constructor - limiter_node(graph &g, size_t threshold) - : graph_node(g), my_threshold(threshold), my_count(0), my_tries(0), my_future_decrement(0), - my_predecessors(this), my_successors(this), decrement(this) - { - initialize(); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - limiter_node(const node_set& nodes, size_t threshold) - : limiter_node(nodes.graph_reference(), threshold) { - make_edges_in_order(nodes, *this); - } -#endif - - //! Copy constructor - limiter_node( const limiter_node& src ) : limiter_node(src.my_graph, src.my_threshold) {} - - //! The interface for accessing internal receiver< DecrementType > that adjusts the count - receiver& decrementer() { return decrement; } - - //! Replace the current successor with this new successor - bool register_successor( successor_type &r ) override { - spin_mutex::scoped_lock lock(my_mutex); - bool was_empty = my_successors.empty(); - my_successors.register_successor(r); - //spawn a forward task if this is the only successor - if ( was_empty && !my_predecessors.empty() && my_count + my_tries < my_threshold ) { - if ( is_graph_active(this->my_graph) ) { - small_object_allocator allocator{}; - typedef forward_task_bypass> task_type; - graph_task* t = allocator.new_object(my_graph, allocator, *this); - my_graph.reserve_wait(); - spawn_in_graph_arena(graph_reference(), *t); - } - } - return true; - } - - //! Removes a successor from this node - /** r.remove_predecessor(*this) is also called. */ - bool remove_successor( successor_type &r ) override { - // TODO revamp: investigate why qualification is needed for remove_predecessor() call - tbb::detail::d1::remove_predecessor(r, *this); - my_successors.remove_successor(r); - return true; - } - - //! Adds src to the list of cached predecessors. - bool register_predecessor( predecessor_type &src ) override { - spin_mutex::scoped_lock lock(my_mutex); - my_predecessors.add( src ); - if ( my_count + my_tries < my_threshold && !my_successors.empty() && is_graph_active(this->my_graph) ) { - small_object_allocator allocator{}; - typedef forward_task_bypass> task_type; - graph_task* t = allocator.new_object(my_graph, allocator, *this); - my_graph.reserve_wait(); - spawn_in_graph_arena(graph_reference(), *t); - } - return true; - } - - //! Removes src from the list of cached predecessors. - bool remove_predecessor( predecessor_type &src ) override { - my_predecessors.remove( src ); - return true; - } - -protected: - - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - //! Puts an item to this receiver - graph_task* try_put_task( const T &t ) override { - { - spin_mutex::scoped_lock lock(my_mutex); - if ( my_count + my_tries >= my_threshold ) - return nullptr; - else - ++my_tries; - } - - graph_task* rtask = my_successors.try_put_task(t); - if ( !rtask ) { // try_put_task failed. - spin_mutex::scoped_lock lock(my_mutex); - --my_tries; - if (check_conditions() && is_graph_active(this->my_graph)) { - small_object_allocator allocator{}; - typedef forward_task_bypass> task_type; - rtask = allocator.new_object(my_graph, allocator, *this); - my_graph.reserve_wait(); - } - } - else { - spin_mutex::scoped_lock lock(my_mutex); - ++my_count; - if ( my_future_decrement ) { - if ( my_count > my_future_decrement ) { - my_count -= my_future_decrement; - my_future_decrement = 0; - } - else { - my_future_decrement -= my_count; - my_count = 0; - } - } - --my_tries; - } - return rtask; - } - - graph& graph_reference() const override { return my_graph; } - - void reset_node( reset_flags f ) override { - my_count = 0; - if ( f & rf_clear_edges ) { - my_predecessors.clear(); - my_successors.clear(); - } - else { - my_predecessors.reset(); - } - decrement.reset_receiver(f); - } -}; // limiter_node - -#include "detail/_flow_graph_join_impl.h" - -template class join_node; - -template -class join_node: public unfolded_join_node::value, reserving_port, OutputTuple, reserving> { -private: - static const int N = std::tuple_size::value; - typedef unfolded_join_node unfolded_type; -public: - typedef OutputTuple output_type; - typedef typename unfolded_type::input_ports_type input_ports_type; - __TBB_NOINLINE_SYM explicit join_node(graph &g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_RESERVING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_NOINLINE_SYM join_node(const node_set& nodes, reserving = reserving()) : join_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - __TBB_NOINLINE_SYM join_node(const join_node &other) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_RESERVING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; - -template -class join_node: public unfolded_join_node::value, queueing_port, OutputTuple, queueing> { -private: - static const int N = std::tuple_size::value; - typedef unfolded_join_node unfolded_type; -public: - typedef OutputTuple output_type; - typedef typename unfolded_type::input_ports_type input_ports_type; - __TBB_NOINLINE_SYM explicit join_node(graph &g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_QUEUEING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_NOINLINE_SYM join_node(const node_set& nodes, queueing = queueing()) : join_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - __TBB_NOINLINE_SYM join_node(const join_node &other) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_QUEUEING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; - -#if __TBB_CPP20_CONCEPTS_PRESENT -// Helper function which is well-formed only if all of the elements in OutputTuple -// satisfies join_node_function_object -template -void join_node_function_objects_helper( std::index_sequence ) - requires (std::tuple_size_v == sizeof...(Functions)) && - (... && join_node_function_object, K>); - -template -concept join_node_functions = requires { - join_node_function_objects_helper(std::make_index_sequence{}); -}; - -#endif - -// template for key_matching join_node -// tag_matching join_node is a specialization of key_matching, and is source-compatible. -template -class join_node > : public unfolded_join_node::value, - key_matching_port, OutputTuple, key_matching > { -private: - static const int N = std::tuple_size::value; - typedef unfolded_join_node > unfolded_type; -public: - typedef OutputTuple output_type; - typedef typename unfolded_type::input_ports_type input_ports_type; - -#if __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING - join_node(graph &g) : unfolded_type(g) {} -#endif /* __TBB_PREVIEW_MESSAGE_BASED_KEY_MATCHING */ - - template - __TBB_requires(join_node_functions) - __TBB_NOINLINE_SYM join_node(graph &g, __TBB_B0 b0, __TBB_B1 b1) : unfolded_type(g, b0, b1) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - template - __TBB_requires(join_node_functions) - __TBB_NOINLINE_SYM join_node(graph &g, __TBB_B0 b0, __TBB_B1 b1, __TBB_B2 b2) : unfolded_type(g, b0, b1, b2) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - template - __TBB_requires(join_node_functions) - __TBB_NOINLINE_SYM join_node(graph &g, __TBB_B0 b0, __TBB_B1 b1, __TBB_B2 b2, __TBB_B3 b3) : unfolded_type(g, b0, b1, b2, b3) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - template - __TBB_requires(join_node_functions) - __TBB_NOINLINE_SYM join_node(graph &g, __TBB_B0 b0, __TBB_B1 b1, __TBB_B2 b2, __TBB_B3 b3, __TBB_B4 b4) : - unfolded_type(g, b0, b1, b2, b3, b4) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } -#if __TBB_VARIADIC_MAX >= 6 - template - __TBB_requires(join_node_functions) - __TBB_NOINLINE_SYM join_node(graph &g, __TBB_B0 b0, __TBB_B1 b1, __TBB_B2 b2, __TBB_B3 b3, __TBB_B4 b4, __TBB_B5 b5) : - unfolded_type(g, b0, b1, b2, b3, b4, b5) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } -#endif -#if __TBB_VARIADIC_MAX >= 7 - template - __TBB_requires(join_node_functions) - __TBB_NOINLINE_SYM join_node(graph &g, __TBB_B0 b0, __TBB_B1 b1, __TBB_B2 b2, __TBB_B3 b3, __TBB_B4 b4, __TBB_B5 b5, __TBB_B6 b6) : - unfolded_type(g, b0, b1, b2, b3, b4, b5, b6) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } -#endif -#if __TBB_VARIADIC_MAX >= 8 - template - __TBB_requires(join_node_functions) - __TBB_NOINLINE_SYM join_node(graph &g, __TBB_B0 b0, __TBB_B1 b1, __TBB_B2 b2, __TBB_B3 b3, __TBB_B4 b4, __TBB_B5 b5, __TBB_B6 b6, - __TBB_B7 b7) : unfolded_type(g, b0, b1, b2, b3, b4, b5, b6, b7) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } -#endif -#if __TBB_VARIADIC_MAX >= 9 - template - __TBB_requires(join_node_functions) - __TBB_NOINLINE_SYM join_node(graph &g, __TBB_B0 b0, __TBB_B1 b1, __TBB_B2 b2, __TBB_B3 b3, __TBB_B4 b4, __TBB_B5 b5, __TBB_B6 b6, - __TBB_B7 b7, __TBB_B8 b8) : unfolded_type(g, b0, b1, b2, b3, b4, b5, b6, b7, b8) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } -#endif -#if __TBB_VARIADIC_MAX >= 10 - template - __TBB_requires(join_node_functions) - __TBB_NOINLINE_SYM join_node(graph &g, __TBB_B0 b0, __TBB_B1 b1, __TBB_B2 b2, __TBB_B3 b3, __TBB_B4 b4, __TBB_B5 b5, __TBB_B6 b6, - __TBB_B7 b7, __TBB_B8 b8, __TBB_B9 b9) : unfolded_type(g, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } -#endif - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template < -#if (__clang_major__ == 3 && __clang_minor__ == 4) - // clang 3.4 misdeduces 'Args...' for 'node_set' while it can cope with template template parameter. - template class node_set, -#endif - typename... Args, typename... Bodies - > - __TBB_requires((sizeof...(Bodies) == 0) || join_node_functions) - __TBB_NOINLINE_SYM join_node(const node_set& nodes, Bodies... bodies) - : join_node(nodes.graph_reference(), bodies...) { - make_edges_in_order(nodes, *this); - } -#endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - - __TBB_NOINLINE_SYM join_node(const join_node &other) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_JOIN_NODE_TAG_MATCHING, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; - -// indexer node -#include "detail/_flow_graph_indexer_impl.h" - -// TODO: Implement interface with variadic template or tuple -template class indexer_node; - -//indexer node specializations -template -class indexer_node : public unfolded_indexer_node > { -private: - static const int N = 1; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - __TBB_NOINLINE_SYM indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } -}; - -template -class indexer_node : public unfolded_indexer_node > { -private: - static const int N = 2; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - __TBB_NOINLINE_SYM indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; - -template -class indexer_node : public unfolded_indexer_node > { -private: - static const int N = 3; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - __TBB_NOINLINE_SYM indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; - -template -class indexer_node : public unfolded_indexer_node > { -private: - static const int N = 4; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - __TBB_NOINLINE_SYM indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; - -template -class indexer_node : public unfolded_indexer_node > { -private: - static const int N = 5; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - __TBB_NOINLINE_SYM indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; - -#if __TBB_VARIADIC_MAX >= 6 -template -class indexer_node : public unfolded_indexer_node > { -private: - static const int N = 6; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - __TBB_NOINLINE_SYM indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; -#endif //variadic max 6 - -#if __TBB_VARIADIC_MAX >= 7 -template -class indexer_node : public unfolded_indexer_node > { -private: - static const int N = 7; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - __TBB_NOINLINE_SYM indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; -#endif //variadic max 7 - -#if __TBB_VARIADIC_MAX >= 8 -template -class indexer_node : public unfolded_indexer_node > { -private: - static const int N = 8; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; -#endif //variadic max 8 - -#if __TBB_VARIADIC_MAX >= 9 -template -class indexer_node : public unfolded_indexer_node > { -private: - static const int N = 9; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - __TBB_NOINLINE_SYM indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; -#endif //variadic max 9 - -#if __TBB_VARIADIC_MAX >= 10 -template -class indexer_node/*default*/ : public unfolded_indexer_node > { -private: - static const int N = 10; -public: - typedef std::tuple InputTuple; - typedef tagged_msg output_type; - typedef unfolded_indexer_node unfolded_type; - __TBB_NOINLINE_SYM indexer_node(graph& g) : unfolded_type(g) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - indexer_node(const node_set& nodes) : indexer_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - // Copy constructor - __TBB_NOINLINE_SYM indexer_node( const indexer_node& other ) : unfolded_type(other) { - fgt_multiinput_node( CODEPTR(), FLOW_INDEXER_NODE, &this->my_graph, - this->input_ports(), static_cast< sender< output_type > *>(this) ); - } - -}; -#endif //variadic max 10 - -template< typename T > -inline void internal_make_edge( sender &p, receiver &s ) { - register_successor(p, s); - fgt_make_edge( &p, &s ); -} - -//! Makes an edge between a single predecessor and a single successor -template< typename T > -inline void make_edge( sender &p, receiver &s ) { - internal_make_edge( p, s ); -} - -//Makes an edge from port 0 of a multi-output predecessor to port 0 of a multi-input successor. -template< typename T, typename V, - typename = typename T::output_ports_type, typename = typename V::input_ports_type > -inline void make_edge( T& output, V& input) { - make_edge(std::get<0>(output.output_ports()), std::get<0>(input.input_ports())); -} - -//Makes an edge from port 0 of a multi-output predecessor to a receiver. -template< typename T, typename R, - typename = typename T::output_ports_type > -inline void make_edge( T& output, receiver& input) { - make_edge(std::get<0>(output.output_ports()), input); -} - -//Makes an edge from a sender to port 0 of a multi-input successor. -template< typename S, typename V, - typename = typename V::input_ports_type > -inline void make_edge( sender& output, V& input) { - make_edge(output, std::get<0>(input.input_ports())); -} - -template< typename T > -inline void internal_remove_edge( sender &p, receiver &s ) { - remove_successor( p, s ); - fgt_remove_edge( &p, &s ); -} - -//! Removes an edge between a single predecessor and a single successor -template< typename T > -inline void remove_edge( sender &p, receiver &s ) { - internal_remove_edge( p, s ); -} - -//Removes an edge between port 0 of a multi-output predecessor and port 0 of a multi-input successor. -template< typename T, typename V, - typename = typename T::output_ports_type, typename = typename V::input_ports_type > -inline void remove_edge( T& output, V& input) { - remove_edge(std::get<0>(output.output_ports()), std::get<0>(input.input_ports())); -} - -//Removes an edge between port 0 of a multi-output predecessor and a receiver. -template< typename T, typename R, - typename = typename T::output_ports_type > -inline void remove_edge( T& output, receiver& input) { - remove_edge(std::get<0>(output.output_ports()), input); -} -//Removes an edge between a sender and port 0 of a multi-input successor. -template< typename S, typename V, - typename = typename V::input_ports_type > -inline void remove_edge( sender& output, V& input) { - remove_edge(output, std::get<0>(input.input_ports())); -} - -//! Returns a copy of the body from a function or continue node -template< typename Body, typename Node > -Body copy_body( Node &n ) { - return n.template copy_function_object(); -} - -//composite_node -template< typename InputTuple, typename OutputTuple > class composite_node; - -template< typename... InputTypes, typename... OutputTypes> -class composite_node , std::tuple > : public graph_node { - -public: - typedef std::tuple< receiver&... > input_ports_type; - typedef std::tuple< sender&... > output_ports_type; - -private: - std::unique_ptr my_input_ports; - std::unique_ptr my_output_ports; - - static const size_t NUM_INPUTS = sizeof...(InputTypes); - static const size_t NUM_OUTPUTS = sizeof...(OutputTypes); - -protected: - void reset_node(reset_flags) override {} - -public: - composite_node( graph &g ) : graph_node(g) { - fgt_multiinput_multioutput_node( CODEPTR(), FLOW_COMPOSITE_NODE, this, &this->my_graph ); - } - - template - void set_external_ports(T1&& input_ports_tuple, T2&& output_ports_tuple) { - static_assert(NUM_INPUTS == std::tuple_size::value, "number of arguments does not match number of input ports"); - static_assert(NUM_OUTPUTS == std::tuple_size::value, "number of arguments does not match number of output ports"); - - fgt_internal_input_alias_helper::alias_port( this, input_ports_tuple); - fgt_internal_output_alias_helper::alias_port( this, output_ports_tuple); - - my_input_ports.reset( new input_ports_type(std::forward(input_ports_tuple)) ); - my_output_ports.reset( new output_ports_type(std::forward(output_ports_tuple)) ); - } - - template< typename... NodeTypes > - void add_visible_nodes(const NodeTypes&... n) { add_nodes_impl(this, true, n...); } - - template< typename... NodeTypes > - void add_nodes(const NodeTypes&... n) { add_nodes_impl(this, false, n...); } - - - input_ports_type& input_ports() { - __TBB_ASSERT(my_input_ports, "input ports not set, call set_external_ports to set input ports"); - return *my_input_ports; - } - - output_ports_type& output_ports() { - __TBB_ASSERT(my_output_ports, "output ports not set, call set_external_ports to set output ports"); - return *my_output_ports; - } -}; // class composite_node - -//composite_node with only input ports -template< typename... InputTypes> -class composite_node , std::tuple<> > : public graph_node { -public: - typedef std::tuple< receiver&... > input_ports_type; - -private: - std::unique_ptr my_input_ports; - static const size_t NUM_INPUTS = sizeof...(InputTypes); - -protected: - void reset_node(reset_flags) override {} - -public: - composite_node( graph &g ) : graph_node(g) { - fgt_composite( CODEPTR(), this, &g ); - } - - template - void set_external_ports(T&& input_ports_tuple) { - static_assert(NUM_INPUTS == std::tuple_size::value, "number of arguments does not match number of input ports"); - - fgt_internal_input_alias_helper::alias_port( this, input_ports_tuple); - - my_input_ports.reset( new input_ports_type(std::forward(input_ports_tuple)) ); - } - - template< typename... NodeTypes > - void add_visible_nodes(const NodeTypes&... n) { add_nodes_impl(this, true, n...); } - - template< typename... NodeTypes > - void add_nodes( const NodeTypes&... n) { add_nodes_impl(this, false, n...); } - - - input_ports_type& input_ports() { - __TBB_ASSERT(my_input_ports, "input ports not set, call set_external_ports to set input ports"); - return *my_input_ports; - } - -}; // class composite_node - -//composite_nodes with only output_ports -template -class composite_node , std::tuple > : public graph_node { -public: - typedef std::tuple< sender&... > output_ports_type; - -private: - std::unique_ptr my_output_ports; - static const size_t NUM_OUTPUTS = sizeof...(OutputTypes); - -protected: - void reset_node(reset_flags) override {} - -public: - __TBB_NOINLINE_SYM composite_node( graph &g ) : graph_node(g) { - fgt_composite( CODEPTR(), this, &g ); - } - - template - void set_external_ports(T&& output_ports_tuple) { - static_assert(NUM_OUTPUTS == std::tuple_size::value, "number of arguments does not match number of output ports"); - - fgt_internal_output_alias_helper::alias_port( this, output_ports_tuple); - - my_output_ports.reset( new output_ports_type(std::forward(output_ports_tuple)) ); - } - - template - void add_visible_nodes(const NodeTypes&... n) { add_nodes_impl(this, true, n...); } - - template - void add_nodes(const NodeTypes&... n) { add_nodes_impl(this, false, n...); } - - - output_ports_type& output_ports() { - __TBB_ASSERT(my_output_ports, "output ports not set, call set_external_ports to set output ports"); - return *my_output_ports; - } - -}; // class composite_node - -template -class async_body_base: no_assign { -public: - typedef Gateway gateway_type; - - async_body_base(gateway_type *gateway): my_gateway(gateway) { } - void set_gateway(gateway_type *gateway) { - my_gateway = gateway; - } - -protected: - gateway_type *my_gateway; -}; - -template -class async_body: public async_body_base { -private: - Body my_body; - -public: - typedef async_body_base base_type; - typedef Gateway gateway_type; - - async_body(const Body &body, gateway_type *gateway) - : base_type(gateway), my_body(body) { } - - void operator()( const Input &v, Ports & ) noexcept(noexcept(tbb::detail::invoke(my_body, v, std::declval()))) { - tbb::detail::invoke(my_body, v, *this->my_gateway); - } - - Body get_body() { return my_body; } -}; - -//! Implements async node -template < typename Input, typename Output, - typename Policy = queueing_lightweight > - __TBB_requires(std::default_initializable && std::copy_constructible) -class async_node - : public multifunction_node< Input, std::tuple< Output >, Policy >, public sender< Output > -{ - typedef multifunction_node< Input, std::tuple< Output >, Policy > base_type; - typedef multifunction_input< - Input, typename base_type::output_ports_type, Policy, cache_aligned_allocator> mfn_input_type; - -public: - typedef Input input_type; - typedef Output output_type; - typedef receiver receiver_type; - typedef receiver successor_type; - typedef sender predecessor_type; - typedef receiver_gateway gateway_type; - typedef async_body_base async_body_base_type; - typedef typename base_type::output_ports_type output_ports_type; - -private: - class receiver_gateway_impl: public receiver_gateway { - public: - receiver_gateway_impl(async_node* node): my_node(node) {} - void reserve_wait() override { - fgt_async_reserve(static_cast(my_node), &my_node->my_graph); - my_node->my_graph.reserve_wait(); - } - - void release_wait() override { - async_node* n = my_node; - graph* g = &n->my_graph; - g->release_wait(); - fgt_async_commit(static_cast(n), g); - } - - //! Implements gateway_type::try_put for an external activity to submit a message to FG - bool try_put(const Output &i) override { - return my_node->try_put_impl(i); - } - - private: - async_node* my_node; - } my_gateway; - - //The substitute of 'this' for member construction, to prevent compiler warnings - async_node* self() { return this; } - - //! Implements gateway_type::try_put for an external activity to submit a message to FG - bool try_put_impl(const Output &i) { - multifunction_output &port_0 = output_port<0>(*this); - broadcast_cache& port_successors = port_0.successors(); - fgt_async_try_put_begin(this, &port_0); - // TODO revamp: change to std::list - graph_task_list tasks; - bool is_at_least_one_put_successful = port_successors.gather_successful_try_puts(i, tasks); - __TBB_ASSERT( is_at_least_one_put_successful || tasks.empty(), - "Return status is inconsistent with the method operation." ); - - while( !tasks.empty() ) { - enqueue_in_graph_arena(this->my_graph, tasks.pop_front()); - } - fgt_async_try_put_end(this, &port_0); - return is_at_least_one_put_successful; - } - -public: - template - __TBB_requires(async_node_body) - __TBB_NOINLINE_SYM async_node( - graph &g, size_t concurrency, - Body body, Policy = Policy(), node_priority_t a_priority = no_priority - ) : base_type( - g, concurrency, - async_body - (body, &my_gateway), a_priority ), my_gateway(self()) { - fgt_multioutput_node_with_body<1>( - CODEPTR(), FLOW_ASYNC_NODE, - &this->my_graph, static_cast *>(this), - this->output_ports(), this->my_body - ); - } - - template - __TBB_requires(async_node_body) - __TBB_NOINLINE_SYM async_node(graph& g, size_t concurrency, Body body, node_priority_t a_priority) - : async_node(g, concurrency, body, Policy(), a_priority) {} - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - __TBB_requires(async_node_body) - __TBB_NOINLINE_SYM async_node( - const node_set& nodes, size_t concurrency, Body body, - Policy = Policy(), node_priority_t a_priority = no_priority ) - : async_node(nodes.graph_reference(), concurrency, body, a_priority) { - make_edges_in_order(nodes, *this); - } - - template - __TBB_requires(async_node_body) - __TBB_NOINLINE_SYM async_node(const node_set& nodes, size_t concurrency, Body body, node_priority_t a_priority) - : async_node(nodes, concurrency, body, Policy(), a_priority) {} -#endif // __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - - __TBB_NOINLINE_SYM async_node( const async_node &other ) : base_type(other), sender(), my_gateway(self()) { - static_cast(this->my_body->get_body_ptr())->set_gateway(&my_gateway); - static_cast(this->my_init_body->get_body_ptr())->set_gateway(&my_gateway); - - fgt_multioutput_node_with_body<1>( CODEPTR(), FLOW_ASYNC_NODE, - &this->my_graph, static_cast *>(this), - this->output_ports(), this->my_body ); - } - - gateway_type& gateway() { - return my_gateway; - } - - // Define sender< Output > - - //! Add a new successor to this node - bool register_successor(successor_type&) override { - __TBB_ASSERT(false, "Successors must be registered only via ports"); - return false; - } - - //! Removes a successor from this node - bool remove_successor(successor_type&) override { - __TBB_ASSERT(false, "Successors must be removed only via ports"); - return false; - } - - template - Body copy_function_object() { - typedef multifunction_body mfn_body_type; - typedef async_body async_body_type; - mfn_body_type &body_ref = *this->my_body; - async_body_type ab = *static_cast(dynamic_cast< multifunction_body_leaf & >(body_ref).get_body_ptr()); - return ab.get_body(); - } - -protected: - - void reset_node( reset_flags f) override { - base_type::reset_node(f); - } -}; - -#include "detail/_flow_graph_node_set_impl.h" - -template< typename T > -class overwrite_node : public graph_node, public receiver, public sender { -public: - typedef T input_type; - typedef T output_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef typename sender::successor_type successor_type; - - __TBB_NOINLINE_SYM explicit overwrite_node(graph &g) - : graph_node(g), my_successors(this), my_buffer_is_valid(false) - { - fgt_node( CODEPTR(), FLOW_OVERWRITE_NODE, &this->my_graph, - static_cast *>(this), static_cast *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - overwrite_node(const node_set& nodes) : overwrite_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - //! Copy constructor; doesn't take anything from src; default won't work - __TBB_NOINLINE_SYM overwrite_node( const overwrite_node& src ) : overwrite_node(src.my_graph) {} - - ~overwrite_node() {} - - bool register_successor( successor_type &s ) override { - spin_mutex::scoped_lock l( my_mutex ); - if (my_buffer_is_valid && is_graph_active( my_graph )) { - // We have a valid value that must be forwarded immediately. - bool ret = s.try_put( my_buffer ); - if ( ret ) { - // We add the successor that accepted our put - my_successors.register_successor( s ); - } else { - // In case of reservation a race between the moment of reservation and register_successor can appear, - // because failed reserve does not mean that register_successor is not ready to put a message immediately. - // We have some sort of infinite loop: reserving node tries to set pull state for the edge, - // but overwrite_node tries to return push state back. That is why we have to break this loop with task creation. - small_object_allocator allocator{}; - typedef register_predecessor_task task_type; - graph_task* t = allocator.new_object(graph_reference(), allocator, *this, s); - graph_reference().reserve_wait(); - spawn_in_graph_arena( my_graph, *t ); - } - } else { - // No valid value yet, just add as successor - my_successors.register_successor( s ); - } - return true; - } - - bool remove_successor( successor_type &s ) override { - spin_mutex::scoped_lock l( my_mutex ); - my_successors.remove_successor(s); - return true; - } - - bool try_get( input_type &v ) override { - spin_mutex::scoped_lock l( my_mutex ); - if ( my_buffer_is_valid ) { - v = my_buffer; - return true; - } - return false; - } - - //! Reserves an item - bool try_reserve( T &v ) override { - return try_get(v); - } - - //! Releases the reserved item - bool try_release() override { return true; } - - //! Consumes the reserved item - bool try_consume() override { return true; } - - bool is_valid() { - spin_mutex::scoped_lock l( my_mutex ); - return my_buffer_is_valid; - } - - void clear() { - spin_mutex::scoped_lock l( my_mutex ); - my_buffer_is_valid = false; - } - -protected: - - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - graph_task* try_put_task( const input_type &v ) override { - spin_mutex::scoped_lock l( my_mutex ); - return try_put_task_impl(v); - } - - graph_task * try_put_task_impl(const input_type &v) { - my_buffer = v; - my_buffer_is_valid = true; - graph_task* rtask = my_successors.try_put_task(v); - if (!rtask) rtask = SUCCESSFULLY_ENQUEUED; - return rtask; - } - - graph& graph_reference() const override { - return my_graph; - } - - //! Breaks an infinite loop between the node reservation and register_successor call - struct register_predecessor_task : public graph_task { - register_predecessor_task( - graph& g, small_object_allocator& allocator, predecessor_type& owner, successor_type& succ) - : graph_task(g, allocator), o(owner), s(succ) {}; - - task* execute(execution_data& ed) override { - // TODO revamp: investigate why qualification is needed for register_successor() call - using tbb::detail::d1::register_predecessor; - using tbb::detail::d1::register_successor; - if ( !register_predecessor(s, o) ) { - register_successor(o, s); - } - finalize(ed); - return nullptr; - } - - task* cancel(execution_data& ed) override { - finalize(ed); - return nullptr; - } - - predecessor_type& o; - successor_type& s; - }; - - spin_mutex my_mutex; - broadcast_cache< input_type, null_rw_mutex > my_successors; - input_type my_buffer; - bool my_buffer_is_valid; - - void reset_node( reset_flags f) override { - my_buffer_is_valid = false; - if (f&rf_clear_edges) { - my_successors.clear(); - } - } -}; // overwrite_node - -template< typename T > -class write_once_node : public overwrite_node { -public: - typedef T input_type; - typedef T output_type; - typedef overwrite_node base_type; - typedef typename receiver::predecessor_type predecessor_type; - typedef typename sender::successor_type successor_type; - - //! Constructor - __TBB_NOINLINE_SYM explicit write_once_node(graph& g) : base_type(g) { - fgt_node( CODEPTR(), FLOW_WRITE_ONCE_NODE, &(this->my_graph), - static_cast *>(this), - static_cast *>(this) ); - } - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - template - write_once_node(const node_set& nodes) : write_once_node(nodes.graph_reference()) { - make_edges_in_order(nodes, *this); - } -#endif - - //! Copy constructor: call base class copy constructor - __TBB_NOINLINE_SYM write_once_node( const write_once_node& src ) : base_type(src) { - fgt_node( CODEPTR(), FLOW_WRITE_ONCE_NODE, &(this->my_graph), - static_cast *>(this), - static_cast *>(this) ); - } - -protected: - template< typename R, typename B > friend class run_and_put_task; - template friend class broadcast_cache; - template friend class round_robin_cache; - graph_task *try_put_task( const T &v ) override { - spin_mutex::scoped_lock l( this->my_mutex ); - return this->my_buffer_is_valid ? nullptr : this->try_put_task_impl(v); - } -}; // write_once_node - -inline void set_name(const graph& g, const char *name) { - fgt_graph_desc(&g, name); -} - -template -inline void set_name(const input_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const function_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const continue_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const broadcast_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const buffer_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const queue_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const sequencer_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const priority_queue_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const limiter_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const join_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const indexer_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const overwrite_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const write_once_node& node, const char *name) { - fgt_node_desc(&node, name); -} - -template -inline void set_name(const multifunction_node& node, const char *name) { - fgt_multioutput_node_desc(&node, name); -} - -template -inline void set_name(const split_node& node, const char *name) { - fgt_multioutput_node_desc(&node, name); -} - -template< typename InputTuple, typename OutputTuple > -inline void set_name(const composite_node& node, const char *name) { - fgt_multiinput_multioutput_node_desc(&node, name); -} - -template -inline void set_name(const async_node& node, const char *name) -{ - fgt_multioutput_node_desc(&node, name); -} -} // d1 -} // detail -} // tbb - - -// Include deduction guides for node classes -#include "detail/_flow_graph_nodes_deduction.h" - -namespace tbb { -namespace flow { -inline namespace v1 { - using detail::d1::receiver; - using detail::d1::sender; - - using detail::d1::serial; - using detail::d1::unlimited; - - using detail::d1::reset_flags; - using detail::d1::rf_reset_protocol; - using detail::d1::rf_reset_bodies; - using detail::d1::rf_clear_edges; - - using detail::d1::graph; - using detail::d1::graph_node; - using detail::d1::continue_msg; - - using detail::d1::input_node; - using detail::d1::function_node; - using detail::d1::multifunction_node; - using detail::d1::split_node; - using detail::d1::output_port; - using detail::d1::indexer_node; - using detail::d1::tagged_msg; - using detail::d1::cast_to; - using detail::d1::is_a; - using detail::d1::continue_node; - using detail::d1::overwrite_node; - using detail::d1::write_once_node; - using detail::d1::broadcast_node; - using detail::d1::buffer_node; - using detail::d1::queue_node; - using detail::d1::sequencer_node; - using detail::d1::priority_queue_node; - using detail::d1::limiter_node; - using namespace detail::d1::graph_policy_namespace; - using detail::d1::join_node; - using detail::d1::input_port; - using detail::d1::copy_body; - using detail::d1::make_edge; - using detail::d1::remove_edge; - using detail::d1::tag_value; - using detail::d1::composite_node; - using detail::d1::async_node; - using detail::d1::node_priority_t; - using detail::d1::no_priority; - -#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET - using detail::d1::follows; - using detail::d1::precedes; - using detail::d1::make_node_set; - using detail::d1::make_edges; -#endif - -} // v1 -} // flow - - using detail::d1::flow_control; - -namespace profiling { - using detail::d1::set_name; -} // profiling - -} // tbb - - -#if TBB_USE_PROFILING_TOOLS && ( __unix__ || __APPLE__ ) - // We don't do pragma pop here, since it still gives warning on the USER side - #undef __TBB_NOINLINE_SYM -#endif - -#endif // __TBB_flow_graph_H diff --git a/include/oneapi/tbb/flow_graph_abstractions.h b/include/oneapi/tbb/flow_graph_abstractions.h deleted file mode 100644 index 121f167..0000000 --- a/include/oneapi/tbb/flow_graph_abstractions.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_flow_graph_abstractions_H -#define __TBB_flow_graph_abstractions_H - -namespace tbb { -namespace detail { -namespace d1 { - -//! Pure virtual template classes that define interfaces for async communication -class graph_proxy { -public: - //! Inform a graph that messages may come from outside, to prevent premature graph completion - virtual void reserve_wait() = 0; - - //! Inform a graph that a previous call to reserve_wait is no longer in effect - virtual void release_wait() = 0; - - virtual ~graph_proxy() {} -}; - -template -class receiver_gateway : public graph_proxy { -public: - //! Type of inputing data into FG. - typedef Input input_type; - - //! Submit signal from an asynchronous activity to FG. - virtual bool try_put(const input_type&) = 0; -}; - -} // d1 - - -} // detail -} // tbb -#endif diff --git a/include/oneapi/tbb/global_control.h b/include/oneapi/tbb/global_control.h deleted file mode 100644 index 57f3b9d..0000000 --- a/include/oneapi/tbb/global_control.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_global_control_H -#define __TBB_global_control_H - -#include "detail/_config.h" - -#include "detail/_assert.h" -#include "detail/_attach.h" -#include "detail/_exception.h" -#include "detail/_namespace_injection.h" -#include "detail/_template_helpers.h" - -#include -#include // std::nothrow_t - -namespace tbb { -namespace detail { - -namespace d1 { -class global_control; -class task_scheduler_handle; -} - -namespace r1 { -TBB_EXPORT void __TBB_EXPORTED_FUNC create(d1::global_control&); -TBB_EXPORT void __TBB_EXPORTED_FUNC destroy(d1::global_control&); -TBB_EXPORT std::size_t __TBB_EXPORTED_FUNC global_control_active_value(int); -struct global_control_impl; -struct control_storage_comparator; -void release_impl(d1::task_scheduler_handle& handle); -bool finalize_impl(d1::task_scheduler_handle& handle); -TBB_EXPORT void __TBB_EXPORTED_FUNC get(d1::task_scheduler_handle&); -TBB_EXPORT bool __TBB_EXPORTED_FUNC finalize(d1::task_scheduler_handle&, std::intptr_t mode); -} - -namespace d1 { - -class global_control { -public: - enum parameter { - max_allowed_parallelism, - thread_stack_size, - terminate_on_exception, - scheduler_handle, // not a public parameter - parameter_max // insert new parameters above this point - }; - - global_control(parameter p, std::size_t value) : - my_value(value), my_reserved(), my_param(p) { - suppress_unused_warning(my_reserved); - __TBB_ASSERT(my_param < parameter_max, "Invalid parameter"); -#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00) - // For Windows 8 Store* apps it's impossible to set stack size - if (p==thread_stack_size) - return; -#elif __TBB_x86_64 && (_WIN32 || _WIN64) - if (p==thread_stack_size) - __TBB_ASSERT_RELEASE((unsigned)value == value, "Stack size is limited to unsigned int range"); -#endif - if (my_param==max_allowed_parallelism) - __TBB_ASSERT_RELEASE(my_value>0, "max_allowed_parallelism cannot be 0."); - r1::create(*this); - } - - ~global_control() { - __TBB_ASSERT(my_param < parameter_max, "Invalid parameter"); -#if __TBB_WIN8UI_SUPPORT && (_WIN32_WINNT < 0x0A00) - // For Windows 8 Store* apps it's impossible to set stack size - if (my_param==thread_stack_size) - return; -#endif - r1::destroy(*this); - } - - static std::size_t active_value(parameter p) { - __TBB_ASSERT(p < parameter_max, "Invalid parameter"); - return r1::global_control_active_value((int)p); - } - -private: - std::size_t my_value; - std::intptr_t my_reserved; // TODO: substitution of global_control* not to break backward compatibility - parameter my_param; - - friend struct r1::global_control_impl; - friend struct r1::control_storage_comparator; -}; - -//! Finalization options. -//! Outside of the class to avoid extensive friendship. -static constexpr std::intptr_t release_nothrowing = 0; -static constexpr std::intptr_t finalize_nothrowing = 1; -static constexpr std::intptr_t finalize_throwing = 2; - -//! User side wrapper for a task scheduler lifetime control object -class task_scheduler_handle { -public: - //! Creates an empty task_scheduler_handle - task_scheduler_handle() = default; - - //! Creates an attached instance of task_scheduler_handle - task_scheduler_handle(attach) { - r1::get(*this); - } - - //! Release a reference if any - ~task_scheduler_handle() { - release(); - } - - //! No copy - task_scheduler_handle(const task_scheduler_handle& other) = delete; - task_scheduler_handle& operator=(const task_scheduler_handle& other) = delete; - - //! Move only - task_scheduler_handle(task_scheduler_handle&& other) noexcept { - std::swap(m_ctl, other.m_ctl); - } - task_scheduler_handle& operator=(task_scheduler_handle&& other) noexcept { - std::swap(m_ctl, other.m_ctl); - return *this; - }; - - //! Checks if the task_scheduler_handle is empty - explicit operator bool() const noexcept { - return m_ctl != nullptr; - } - - //! Release the reference and deactivate handle - void release() { - if (m_ctl != nullptr) { - r1::finalize(*this, release_nothrowing); - m_ctl = nullptr; - } - } - -private: - friend void r1::release_impl(task_scheduler_handle& handle); - friend bool r1::finalize_impl(task_scheduler_handle& handle); - friend void __TBB_EXPORTED_FUNC r1::get(task_scheduler_handle&); - - friend void finalize(task_scheduler_handle&); - friend bool finalize(task_scheduler_handle&, const std::nothrow_t&) noexcept; - - global_control* m_ctl{nullptr}; -}; - -#if TBB_USE_EXCEPTIONS -//! Waits for worker threads termination. Throws exception on error. -inline void finalize(task_scheduler_handle& handle) { - try_call([&] { - if (handle.m_ctl != nullptr) { - bool finalized = r1::finalize(handle, finalize_throwing); - __TBB_ASSERT_EX(finalized, "r1::finalize did not respect finalize_throwing ?"); - - } - }).on_completion([&] { - __TBB_ASSERT(!handle, "The handle should be empty after finalize"); - }); -} -#endif -//! Waits for worker threads termination. Returns false on error. -inline bool finalize(task_scheduler_handle& handle, const std::nothrow_t&) noexcept { - bool finalized = true; - if (handle.m_ctl != nullptr) { - finalized = r1::finalize(handle, finalize_nothrowing); - } - __TBB_ASSERT(!handle, "The handle should be empty after finalize"); - return finalized; -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::global_control; -using detail::d1::attach; -using detail::d1::finalize; -using detail::d1::task_scheduler_handle; -using detail::r1::unsafe_wait; -} // namespace v1 - -} // namespace tbb - -#endif // __TBB_global_control_H diff --git a/include/oneapi/tbb/info.h b/include/oneapi/tbb/info.h deleted file mode 100644 index dfcfcc0..0000000 --- a/include/oneapi/tbb/info.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - Copyright (c) 2019-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_info_H -#define __TBB_info_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" - -#if __TBB_ARENA_BINDING -#include -#include - -namespace tbb { -namespace detail { - -namespace d1{ - -using numa_node_id = int; -using core_type_id = int; - -// TODO: consider version approach to resolve backward compatibility potential issues. -struct constraints { -#if !__TBB_CPP20_PRESENT - constraints(numa_node_id id = -1, int maximal_concurrency = -1) - : numa_id(id) - , max_concurrency(maximal_concurrency) - {} -#endif /*!__TBB_CPP20_PRESENT*/ - - constraints& set_numa_id(numa_node_id id) { - numa_id = id; - return *this; - } - constraints& set_max_concurrency(int maximal_concurrency) { - max_concurrency = maximal_concurrency; - return *this; - } - constraints& set_core_type(core_type_id id) { - core_type = id; - return *this; - } - constraints& set_max_threads_per_core(int threads_number) { - max_threads_per_core = threads_number; - return *this; - } - - numa_node_id numa_id = -1; - int max_concurrency = -1; - core_type_id core_type = -1; - int max_threads_per_core = -1; -}; - -} // namespace d1 - -namespace r1 { -TBB_EXPORT unsigned __TBB_EXPORTED_FUNC numa_node_count(); -TBB_EXPORT void __TBB_EXPORTED_FUNC fill_numa_indices(int* index_array); -TBB_EXPORT int __TBB_EXPORTED_FUNC numa_default_concurrency(int numa_id); - -// Reserved fields are required to save binary backward compatibility in case of future changes. -// They must be defined to 0 at this moment. -TBB_EXPORT unsigned __TBB_EXPORTED_FUNC core_type_count(intptr_t reserved = 0); -TBB_EXPORT void __TBB_EXPORTED_FUNC fill_core_type_indices(int* index_array, intptr_t reserved = 0); - -TBB_EXPORT int __TBB_EXPORTED_FUNC constraints_default_concurrency(const d1::constraints& c, intptr_t reserved = 0); -TBB_EXPORT int __TBB_EXPORTED_FUNC constraints_threads_per_core(const d1::constraints& c, intptr_t reserved = 0); -} // namespace r1 - -namespace d1 { - -inline std::vector numa_nodes() { - std::vector node_indices(r1::numa_node_count()); - r1::fill_numa_indices(node_indices.data()); - return node_indices; -} - -inline int default_concurrency(numa_node_id id = -1) { - return r1::numa_default_concurrency(id); -} - -inline std::vector core_types() { - std::vector core_type_indexes(r1::core_type_count()); - r1::fill_core_type_indices(core_type_indexes.data()); - return core_type_indexes; -} - -inline int default_concurrency(constraints c) { - if (c.max_concurrency > 0) { return c.max_concurrency; } - return r1::constraints_default_concurrency(c); -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::numa_node_id; -using detail::d1::core_type_id; - -namespace info { -using detail::d1::numa_nodes; -using detail::d1::core_types; - -using detail::d1::default_concurrency; -} // namespace info -} // namespace v1 - -} // namespace tbb - -#endif /*__TBB_ARENA_BINDING*/ - -#endif /*__TBB_info_H*/ diff --git a/include/oneapi/tbb/memory_pool.h b/include/oneapi/tbb/memory_pool.h deleted file mode 100644 index b2e6b05..0000000 --- a/include/oneapi/tbb/memory_pool.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_memory_pool_H -#define __TBB_memory_pool_H - -#if !TBB_PREVIEW_MEMORY_POOL -#error Set TBB_PREVIEW_MEMORY_POOL to include memory_pool.h -#endif -/** @file */ - -#include "scalable_allocator.h" - -#include // std::bad_alloc -#include // std::runtime_error, std::invalid_argument -#include // std::forward - - -#if __TBB_EXTRA_DEBUG -#define __TBBMALLOC_ASSERT ASSERT -#else -#define __TBBMALLOC_ASSERT(a,b) ((void)0) -#endif - -namespace tbb { -namespace detail { -namespace d1 { - -//! Base of thread-safe pool allocator for variable-size requests -class pool_base : no_copy { - // Pool interface is separate from standard allocator classes because it has - // to maintain internal state, no copy or assignment. Move and swap are possible. -public: - //! Reset pool to reuse its memory (free all objects at once) - void recycle() { rml::pool_reset(my_pool); } - - //! The "malloc" analogue to allocate block of memory of size bytes - void *malloc(size_t size) { return rml::pool_malloc(my_pool, size); } - - //! The "free" analogue to discard a previously allocated piece of memory. - void free(void* ptr) { rml::pool_free(my_pool, ptr); } - - //! The "realloc" analogue complementing pool_malloc. - // Enables some low-level optimization possibilities - void *realloc(void* ptr, size_t size) { - return rml::pool_realloc(my_pool, ptr, size); - } - -protected: - //! destroy pool - must be called in a child class - void destroy() { rml::pool_destroy(my_pool); } - - rml::MemoryPool *my_pool; -}; - -#if _MSC_VER && !defined(__INTEL_COMPILER) - // Workaround for erroneous "unreferenced parameter" warning in method destroy. - #pragma warning (push) - #pragma warning (disable: 4100) -#endif - -//! Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5 -/** @ingroup memory_allocation */ -template -class memory_pool_allocator { -protected: - typedef P pool_type; - pool_type *my_pool; - template - friend class memory_pool_allocator; - template - friend bool operator==( const memory_pool_allocator& a, const memory_pool_allocator& b); - template - friend bool operator!=( const memory_pool_allocator& a, const memory_pool_allocator& b); -public: - typedef T value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - template struct rebind { - typedef memory_pool_allocator other; - }; - - explicit memory_pool_allocator(pool_type &pool) throw() : my_pool(&pool) {} - memory_pool_allocator(const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {} - template - memory_pool_allocator(const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {} - - pointer address(reference x) const { return &x; } - const_pointer address(const_reference x) const { return &x; } - - //! Allocate space for n objects. - pointer allocate( size_type n, const void* /*hint*/ = nullptr) { - pointer p = static_cast( my_pool->malloc( n*sizeof(value_type) ) ); - if (!p) - throw_exception(std::bad_alloc()); - return p; - } - //! Free previously allocated block of memory. - void deallocate( pointer p, size_type ) { - my_pool->free(p); - } - //! Largest value for which method allocate might succeed. - size_type max_size() const throw() { - size_type max = static_cast(-1) / sizeof (value_type); - return (max > 0 ? max : 1); - } - //! Copy-construct value at location pointed to by p. - - template - void construct(U *p, Args&&... args) - { ::new((void *)p) U(std::forward(args)...); } - - //! Destroy value at location pointed to by p. - void destroy( pointer p ) { p->~value_type(); } - -}; - -#if _MSC_VER && !defined(__INTEL_COMPILER) - #pragma warning (pop) -#endif // warning 4100 is back - -//! Analogous to std::allocator, as defined in ISO C++ Standard, Section 20.4.1 -/** @ingroup memory_allocation */ -template -class memory_pool_allocator { -public: - typedef P pool_type; - typedef void* pointer; - typedef const void* const_pointer; - typedef void value_type; - template struct rebind { - typedef memory_pool_allocator other; - }; - - explicit memory_pool_allocator( pool_type &pool) throw() : my_pool(&pool) {} - memory_pool_allocator( const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {} - template - memory_pool_allocator(const memory_pool_allocator& src) throw() : my_pool(src.my_pool) {} - -protected: - pool_type *my_pool; - template - friend class memory_pool_allocator; - template - friend bool operator==( const memory_pool_allocator& a, const memory_pool_allocator& b); - template - friend bool operator!=( const memory_pool_allocator& a, const memory_pool_allocator& b); -}; - -template -inline bool operator==( const memory_pool_allocator& a, const memory_pool_allocator& b) {return a.my_pool==b.my_pool;} - -template -inline bool operator!=( const memory_pool_allocator& a, const memory_pool_allocator& b) {return a.my_pool!=b.my_pool;} - -//! Thread-safe growable pool allocator for variable-size requests -template -class memory_pool : public pool_base { - Alloc my_alloc; // TODO: base-class optimization - static void *allocate_request(intptr_t pool_id, size_t & bytes); - static int deallocate_request(intptr_t pool_id, void*, size_t raw_bytes); - -public: - //! construct pool with underlying allocator - explicit memory_pool(const Alloc &src = Alloc()); - - //! destroy pool - ~memory_pool() { destroy(); } // call the callbacks first and destroy my_alloc latter -}; - -class fixed_pool : public pool_base { - void *my_buffer; - size_t my_size; - inline static void *allocate_request(intptr_t pool_id, size_t & bytes); - -public: - //! construct pool with underlying allocator - inline fixed_pool(void *buf, size_t size); - //! destroy pool - ~fixed_pool() { destroy(); } -}; - -//////////////// Implementation /////////////// - -template -memory_pool::memory_pool(const Alloc &src) : my_alloc(src) { - rml::MemPoolPolicy args(allocate_request, deallocate_request, - sizeof(typename Alloc::value_type)); - rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool); - if (res!=rml::POOL_OK) - throw_exception(std::runtime_error("Can't create pool")); -} -template -void *memory_pool::allocate_request(intptr_t pool_id, size_t & bytes) { - memory_pool &self = *reinterpret_cast*>(pool_id); - const size_t unit_size = sizeof(typename Alloc::value_type); - __TBBMALLOC_ASSERT( 0 == bytes%unit_size, nullptr); - void *ptr; -#if TBB_USE_EXCEPTIONS - try { -#endif - ptr = self.my_alloc.allocate( bytes/unit_size ); -#if TBB_USE_EXCEPTIONS - } catch(...) { - return nullptr; - } -#endif - return ptr; -} -#if __TBB_MSVC_UNREACHABLE_CODE_IGNORED - // Workaround for erroneous "unreachable code" warning in the template below. - // Specific for VC++ 17-18 compiler - #pragma warning (push) - #pragma warning (disable: 4702) -#endif -template -int memory_pool::deallocate_request(intptr_t pool_id, void* raw_ptr, size_t raw_bytes) { - memory_pool &self = *reinterpret_cast*>(pool_id); - const size_t unit_size = sizeof(typename Alloc::value_type); - __TBBMALLOC_ASSERT( 0 == raw_bytes%unit_size, nullptr); - self.my_alloc.deallocate( static_cast(raw_ptr), raw_bytes/unit_size ); - return 0; -} -#if __TBB_MSVC_UNREACHABLE_CODE_IGNORED - #pragma warning (pop) -#endif -inline fixed_pool::fixed_pool(void *buf, size_t size) : my_buffer(buf), my_size(size) { - if (!buf || !size) - // TODO: improve support for mode with exceptions disabled - throw_exception(std::invalid_argument("Zero in parameter is invalid")); - rml::MemPoolPolicy args(allocate_request, nullptr, size, /*fixedPool=*/true); - rml::MemPoolError res = rml::pool_create_v1(intptr_t(this), &args, &my_pool); - if (res!=rml::POOL_OK) - throw_exception(std::runtime_error("Can't create pool")); -} -inline void *fixed_pool::allocate_request(intptr_t pool_id, size_t & bytes) { - fixed_pool &self = *reinterpret_cast(pool_id); - __TBBMALLOC_ASSERT(0 != self.my_size, "The buffer must not be used twice."); - bytes = self.my_size; - self.my_size = 0; // remember that buffer has been used - return self.my_buffer; -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::memory_pool_allocator; -using detail::d1::memory_pool; -using detail::d1::fixed_pool; -} // inline namepspace v1 -} // namespace tbb - -#undef __TBBMALLOC_ASSERT -#endif// __TBB_memory_pool_H diff --git a/include/oneapi/tbb/mutex.h b/include/oneapi/tbb/mutex.h deleted file mode 100644 index a4d2a9a..0000000 --- a/include/oneapi/tbb/mutex.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - Copyright (c) 2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_mutex_H -#define __TBB_mutex_H - -#include "detail/_namespace_injection.h" -#include "detail/_utils.h" -#include "detail/_scoped_lock.h" -#include "detail/_waitable_atomic.h" -#include "detail/_mutex_common.h" -#include "profiling.h" - -namespace tbb { -namespace detail { -namespace d1 { - -class mutex { -public: - //! Constructors - mutex() { - create_itt_sync(this, "tbb::mutex", ""); - }; - - //! Destructor - ~mutex() { - __TBB_ASSERT(!my_flag.load(std::memory_order_relaxed), "destruction of an acquired mutex"); - } - - //! No Copy - mutex(const mutex&) = delete; - mutex& operator=(const mutex&) = delete; - - using scoped_lock = unique_scoped_lock; - - //! Mutex traits - static constexpr bool is_rw_mutex = false; - static constexpr bool is_recursive_mutex = false; - static constexpr bool is_fair_mutex = false; - - //! Acquire lock - /** Spin if the lock is taken */ - void lock() { - call_itt_notify(prepare, this); - while (!try_lock()) { - my_flag.wait(true, /* context = */ 0, std::memory_order_relaxed); - } - } - - //! Try acquiring lock (non-blocking) - /** Return true if lock acquired; false otherwise. */ - bool try_lock() { - bool result = !my_flag.load(std::memory_order_relaxed) && !my_flag.exchange(true); - if (result) { - call_itt_notify(acquired, this); - } - return result; - } - - //! Release lock - void unlock() { - call_itt_notify(releasing, this); - // We need Write Read memory barrier before notify that reads the waiter list. - // In C++ only full fence covers this type of barrier. - my_flag.exchange(false); - my_flag.notify_one_relaxed(); - } - -private: - waitable_atomic my_flag{0}; -}; // class mutex - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::mutex; -} // namespace v1 - -} // namespace tbb - -#endif // __TBB_mutex_H diff --git a/include/oneapi/tbb/null_mutex.h b/include/oneapi/tbb/null_mutex.h deleted file mode 100644 index 1797e35..0000000 --- a/include/oneapi/tbb/null_mutex.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_null_mutex_H -#define __TBB_null_mutex_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_mutex_common.h" - -namespace tbb { -namespace detail { -namespace d1 { - -//! A mutex which does nothing -/** A null_mutex does no operation and simulates success. - @ingroup synchronization */ -class null_mutex { -public: - //! Constructors - constexpr null_mutex() noexcept = default; - - //! Destructor - ~null_mutex() = default; - - //! No Copy - null_mutex(const null_mutex&) = delete; - null_mutex& operator=(const null_mutex&) = delete; - - //! Represents acquisition of a mutex. - class scoped_lock { - public: - //! Constructors - constexpr scoped_lock() noexcept = default; - scoped_lock(null_mutex&) {} - - //! Destructor - ~scoped_lock() = default; - - //! No Copy - scoped_lock(const scoped_lock&) = delete; - scoped_lock& operator=(const scoped_lock&) = delete; - - void acquire(null_mutex&) {} - bool try_acquire(null_mutex&) { return true; } - void release() {} - }; - - //! Mutex traits - static constexpr bool is_rw_mutex = false; - static constexpr bool is_recursive_mutex = true; - static constexpr bool is_fair_mutex = true; - - void lock() {} - bool try_lock() { return true; } - void unlock() {} -}; // class null_mutex - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::null_mutex; -} // namespace v1 -} // namespace tbb - -#endif /* __TBB_null_mutex_H */ diff --git a/include/oneapi/tbb/null_rw_mutex.h b/include/oneapi/tbb/null_rw_mutex.h deleted file mode 100644 index 43983a0..0000000 --- a/include/oneapi/tbb/null_rw_mutex.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_null_rw_mutex_H -#define __TBB_null_rw_mutex_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_mutex_common.h" - -namespace tbb { -namespace detail { -namespace d1 { - -//! A rw mutex which does nothing -/** A null_rw_mutex is a rw mutex that does nothing and simulates successful operation. - @ingroup synchronization */ -class null_rw_mutex { -public: - //! Constructors - constexpr null_rw_mutex() noexcept = default; - - //! Destructor - ~null_rw_mutex() = default; - - //! No Copy - null_rw_mutex(const null_rw_mutex&) = delete; - null_rw_mutex& operator=(const null_rw_mutex&) = delete; - - //! Represents acquisition of a mutex. - class scoped_lock { - public: - //! Constructors - constexpr scoped_lock() noexcept = default; - scoped_lock(null_rw_mutex&, bool = true) {} - - //! Destructor - ~scoped_lock() = default; - - //! No Copy - scoped_lock(const scoped_lock&) = delete; - scoped_lock& operator=(const scoped_lock&) = delete; - - void acquire(null_rw_mutex&, bool = true) {} - bool try_acquire(null_rw_mutex&, bool = true) { return true; } - void release() {} - bool upgrade_to_writer() { return true; } - bool downgrade_to_reader() { return true; } - - bool is_writer() const { return true; } - }; - - //! Mutex traits - static constexpr bool is_rw_mutex = true; - static constexpr bool is_recursive_mutex = true; - static constexpr bool is_fair_mutex = true; - - void lock() {} - bool try_lock() { return true; } - void unlock() {} - void lock_shared() {} - bool try_lock_shared() { return true; } - void unlock_shared() {} -}; // class null_rw_mutex - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::null_rw_mutex; -} // namespace v1 -} // namespace tbb - -#endif /* __TBB_null_rw_mutex_H */ diff --git a/include/oneapi/tbb/parallel_for.h b/include/oneapi/tbb/parallel_for.h deleted file mode 100644 index 91c7c44..0000000 --- a/include/oneapi/tbb/parallel_for.h +++ /dev/null @@ -1,469 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_parallel_for_H -#define __TBB_parallel_for_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_exception.h" -#include "detail/_task.h" -#include "detail/_small_object_pool.h" -#include "profiling.h" - -#include "partitioner.h" -#include "blocked_range.h" -#include "task_group.h" - -#include -#include - -namespace tbb { -namespace detail { -#if __TBB_CPP20_CONCEPTS_PRESENT -inline namespace d0 { - -template -concept parallel_for_body = std::copy_constructible && std::invocable&, Range&>; - -template -concept parallel_for_index = std::constructible_from && - std::copyable && - requires( const std::remove_reference_t& lhs, const std::remove_reference_t& rhs ) { - { lhs < rhs } -> adaptive_same_as; - { lhs - rhs } -> std::convertible_to; - { lhs + (rhs - lhs) } -> std::convertible_to; - }; - -template -concept parallel_for_function = std::invocable&, Index>; - -} // namespace d0 -#endif // __TBB_CPP20_CONCEPTS_PRESENT -namespace d1 { - -//! Task type used in parallel_for -/** @ingroup algorithms */ -template -struct start_for : public task { - Range my_range; - const Body my_body; - node* my_parent; - - typename Partitioner::task_partition_type my_partition; - small_object_allocator my_allocator; - - task* execute(execution_data&) override; - task* cancel(execution_data&) override; - void finalize(const execution_data&); - - //! Constructor for root task. - start_for( const Range& range, const Body& body, Partitioner& partitioner, small_object_allocator& alloc ) : - my_range(range), - my_body(body), - my_parent(nullptr), - my_partition(partitioner), - my_allocator(alloc) {} - //! Splitting constructor used to generate children. - /** parent_ becomes left child. Newly constructed object is right child. */ - start_for( start_for& parent_, typename Partitioner::split_type& split_obj, small_object_allocator& alloc ) : - my_range(parent_.my_range, get_range_split_object(split_obj)), - my_body(parent_.my_body), - my_parent(nullptr), - my_partition(parent_.my_partition, split_obj), - my_allocator(alloc) {} - //! Construct right child from the given range as response to the demand. - /** parent_ remains left child. Newly constructed object is right child. */ - start_for( start_for& parent_, const Range& r, depth_t d, small_object_allocator& alloc ) : - my_range(r), - my_body(parent_.my_body), - my_parent(nullptr), - my_partition(parent_.my_partition, split()), - my_allocator(alloc) - { - my_partition.align_depth( d ); - } - static void run(const Range& range, const Body& body, Partitioner& partitioner) { - task_group_context context(PARALLEL_FOR); - run(range, body, partitioner, context); - } - - static void run(const Range& range, const Body& body, Partitioner& partitioner, task_group_context& context) { - if ( !range.empty() ) { - small_object_allocator alloc{}; - start_for& for_task = *alloc.new_object(range, body, partitioner, alloc); - - // defer creation of the wait node until task allocation succeeds - wait_node wn; - for_task.my_parent = &wn; - execute_and_wait(for_task, context, wn.m_wait, context); - } - } - //! Run body for range, serves as callback for partitioner - void run_body( Range &r ) { - tbb::detail::invoke(my_body, r); - } - - //! spawn right task, serves as callback for partitioner - void offer_work(typename Partitioner::split_type& split_obj, execution_data& ed) { - offer_work_impl(ed, *this, split_obj); - } - - //! spawn right task, serves as callback for partitioner - void offer_work(const Range& r, depth_t d, execution_data& ed) { - offer_work_impl(ed, *this, r, d); - } - -private: - template - void offer_work_impl(execution_data& ed, Args&&... constructor_args) { - // New right child - small_object_allocator alloc{}; - start_for& right_child = *alloc.new_object(ed, std::forward(constructor_args)..., alloc); - - // New root node as a continuation and ref count. Left and right child attach to the new parent. - right_child.my_parent = my_parent = alloc.new_object(ed, my_parent, 2, alloc); - // Spawn the right sibling - right_child.spawn_self(ed); - } - - void spawn_self(execution_data& ed) { - my_partition.spawn_task(*this, *context(ed)); - } -}; - -//! fold the tree and deallocate the task -template -void start_for::finalize(const execution_data& ed) { - // Get the current parent and allocator an object destruction - node* parent = my_parent; - auto allocator = my_allocator; - // Task execution finished - destroy it - this->~start_for(); - // Unwind the tree decrementing the parent`s reference count - - fold_tree(parent, ed); - allocator.deallocate(this, ed); - -} - -//! execute task for parallel_for -template -task* start_for::execute(execution_data& ed) { - if (!is_same_affinity(ed)) { - my_partition.note_affinity(execution_slot(ed)); - } - my_partition.check_being_stolen(*this, ed); - my_partition.execute(*this, my_range, ed); - finalize(ed); - return nullptr; -} - -//! cancel task for parallel_for -template -task* start_for::cancel(execution_data& ed) { - finalize(ed); - return nullptr; -} - -//! Calls the function with values from range [begin, end) with a step provided -template -class parallel_for_body_wrapper : detail::no_assign { - const Function &my_func; - const Index my_begin; - const Index my_step; -public: - parallel_for_body_wrapper( const Function& _func, Index& _begin, Index& _step ) - : my_func(_func), my_begin(_begin), my_step(_step) {} - - void operator()( const blocked_range& r ) const { - // A set of local variables to help the compiler with vectorization of the following loop. - Index b = r.begin(); - Index e = r.end(); - Index ms = my_step; - Index k = my_begin + b*ms; - -#if __INTEL_COMPILER -#pragma ivdep -#if __TBB_ASSERT_ON_VECTORIZATION_FAILURE -#pragma vector always assert -#endif -#endif - for ( Index i = b; i < e; ++i, k += ms ) { - tbb::detail::invoke(my_func, k); - } - } -}; - -// Requirements on Range concept are documented in blocked_range.h - -/** \page parallel_for_body_req Requirements on parallel_for body - Class \c Body implementing the concept of parallel_for body must define: - - \code Body::Body( const Body& ); \endcode Copy constructor - - \code Body::~Body(); \endcode Destructor - - \code void Body::operator()( Range& r ) const; \endcode Function call operator applying the body to range \c r. -**/ - -/** \name parallel_for - See also requirements on \ref range_req "Range" and \ref parallel_for_body_req "parallel_for Body". **/ -//@{ - -//! Parallel iteration over range with default partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body ) { - start_for::run(range,body,__TBB_DEFAULT_PARTITIONER()); -} - -//! Parallel iteration over range with simple partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body, const simple_partitioner& partitioner ) { - start_for::run(range,body,partitioner); -} - -//! Parallel iteration over range with auto_partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body, const auto_partitioner& partitioner ) { - start_for::run(range,body,partitioner); -} - -//! Parallel iteration over range with static_partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body, const static_partitioner& partitioner ) { - start_for::run(range,body,partitioner); -} - -//! Parallel iteration over range with affinity_partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body, affinity_partitioner& partitioner ) { - start_for::run(range,body,partitioner); -} - -//! Parallel iteration over range with default partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body, task_group_context& context ) { - start_for::run(range, body, __TBB_DEFAULT_PARTITIONER(), context); -} - -//! Parallel iteration over range with simple partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body, const simple_partitioner& partitioner, task_group_context& context ) { - start_for::run(range, body, partitioner, context); -} - -//! Parallel iteration over range with auto_partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body, const auto_partitioner& partitioner, task_group_context& context ) { - start_for::run(range, body, partitioner, context); -} - -//! Parallel iteration over range with static_partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body, const static_partitioner& partitioner, task_group_context& context ) { - start_for::run(range, body, partitioner, context); -} - -//! Parallel iteration over range with affinity_partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_for_body) -void parallel_for( const Range& range, const Body& body, affinity_partitioner& partitioner, task_group_context& context ) { - start_for::run(range,body,partitioner, context); -} - -//! Implementation of parallel iteration over stepped range of integers with explicit step and partitioner -template -void parallel_for_impl(Index first, Index last, Index step, const Function& f, Partitioner& partitioner) { - if (step <= 0 ) - throw_exception(exception_id::nonpositive_step); // throws std::invalid_argument - else if (first < last) { - // Above "else" avoids "potential divide by zero" warning on some platforms - Index end = Index(last - first - 1ul) / step + Index(1); - blocked_range range(static_cast(0), end); - parallel_for_body_wrapper body(f, first, step); - parallel_for(range, body, partitioner); - } -} - -//! Parallel iteration over a range of integers with a step provided and default partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f) { - parallel_for_impl(first, last, step, f, auto_partitioner()); -} -//! Parallel iteration over a range of integers with a step provided and simple partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f, const simple_partitioner& partitioner) { - parallel_for_impl(first, last, step, f, partitioner); -} -//! Parallel iteration over a range of integers with a step provided and auto partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f, const auto_partitioner& partitioner) { - parallel_for_impl(first, last, step, f, partitioner); -} -//! Parallel iteration over a range of integers with a step provided and static partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f, const static_partitioner& partitioner) { - parallel_for_impl(first, last, step, f, partitioner); -} -//! Parallel iteration over a range of integers with a step provided and affinity partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f, affinity_partitioner& partitioner) { - parallel_for_impl(first, last, step, f, partitioner); -} - -//! Parallel iteration over a range of integers with a default step value and default partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f) { - parallel_for_impl(first, last, static_cast(1), f, auto_partitioner()); -} -//! Parallel iteration over a range of integers with a default step value and simple partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f, const simple_partitioner& partitioner) { - parallel_for_impl(first, last, static_cast(1), f, partitioner); -} -//! Parallel iteration over a range of integers with a default step value and auto partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f, const auto_partitioner& partitioner) { - parallel_for_impl(first, last, static_cast(1), f, partitioner); -} -//! Parallel iteration over a range of integers with a default step value and static partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f, const static_partitioner& partitioner) { - parallel_for_impl(first, last, static_cast(1), f, partitioner); -} -//! Parallel iteration over a range of integers with a default step value and affinity partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f, affinity_partitioner& partitioner) { - parallel_for_impl(first, last, static_cast(1), f, partitioner); -} - -//! Implementation of parallel iteration over stepped range of integers with explicit step, task group context, and partitioner -template -void parallel_for_impl(Index first, Index last, Index step, const Function& f, Partitioner& partitioner, task_group_context &context) { - if (step <= 0 ) - throw_exception(exception_id::nonpositive_step); // throws std::invalid_argument - else if (first < last) { - // Above "else" avoids "potential divide by zero" warning on some platforms - Index end = (last - first - Index(1)) / step + Index(1); - blocked_range range(static_cast(0), end); - parallel_for_body_wrapper body(f, first, step); - parallel_for(range, body, partitioner, context); - } -} - -//! Parallel iteration over a range of integers with explicit step, task group context, and default partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f, task_group_context &context) { - parallel_for_impl(first, last, step, f, auto_partitioner(), context); -} -//! Parallel iteration over a range of integers with explicit step, task group context, and simple partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f, const simple_partitioner& partitioner, task_group_context &context) { - parallel_for_impl(first, last, step, f, partitioner, context); -} -//! Parallel iteration over a range of integers with explicit step, task group context, and auto partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f, const auto_partitioner& partitioner, task_group_context &context) { - parallel_for_impl(first, last, step, f, partitioner, context); -} -//! Parallel iteration over a range of integers with explicit step, task group context, and static partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f, const static_partitioner& partitioner, task_group_context &context) { - parallel_for_impl(first, last, step, f, partitioner, context); -} -//! Parallel iteration over a range of integers with explicit step, task group context, and affinity partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, Index step, const Function& f, affinity_partitioner& partitioner, task_group_context &context) { - parallel_for_impl(first, last, step, f, partitioner, context); -} - -//! Parallel iteration over a range of integers with a default step value, explicit task group context, and default partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f, task_group_context &context) { - parallel_for_impl(first, last, static_cast(1), f, auto_partitioner(), context); -} -//! Parallel iteration over a range of integers with a default step value, explicit task group context, and simple partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f, const simple_partitioner& partitioner, task_group_context &context) { - parallel_for_impl(first, last, static_cast(1), f, partitioner, context); -} -//! Parallel iteration over a range of integers with a default step value, explicit task group context, and auto partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f, const auto_partitioner& partitioner, task_group_context &context) { - parallel_for_impl(first, last, static_cast(1), f, partitioner, context); -} -//! Parallel iteration over a range of integers with a default step value, explicit task group context, and static partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f, const static_partitioner& partitioner, task_group_context &context) { - parallel_for_impl(first, last, static_cast(1), f, partitioner, context); -} -//! Parallel iteration over a range of integers with a default step value, explicit task group context, and affinity_partitioner -template - __TBB_requires(parallel_for_index && parallel_for_function) -void parallel_for(Index first, Index last, const Function& f, affinity_partitioner& partitioner, task_group_context &context) { - parallel_for_impl(first, last, static_cast(1), f, partitioner, context); -} -// @} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::parallel_for; -// Split types -using detail::split; -using detail::proportional_split; -} // namespace v1 - -} // namespace tbb - -#endif /* __TBB_parallel_for_H */ diff --git a/include/oneapi/tbb/parallel_for_each.h b/include/oneapi/tbb/parallel_for_each.h deleted file mode 100644 index 56dbeb4..0000000 --- a/include/oneapi/tbb/parallel_for_each.h +++ /dev/null @@ -1,681 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_parallel_for_each_H -#define __TBB_parallel_for_each_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_exception.h" -#include "detail/_task.h" -#include "detail/_aligned_space.h" -#include "detail/_small_object_pool.h" -#include "detail/_utils.h" - -#include "parallel_for.h" -#include "task_group.h" // task_group_context - -#include -#include - -namespace tbb { -namespace detail { -#if __TBB_CPP20_CONCEPTS_PRESENT -namespace d1 { -template -class feeder; - -} // namespace d1 -inline namespace d0 { - -template -concept parallel_for_each_body = std::invocable&, ItemType&&> || - std::invocable&, ItemType&&, tbb::detail::d1::feeder&>; - -} // namespace d0 -#endif // __TBB_CPP20_CONCEPTS_PRESENT -namespace d2 { -template class feeder_impl; -} // namespace d2 - -namespace d1 { -//! Class the user supplied algorithm body uses to add new tasks -template -class feeder { - feeder() {} - feeder(const feeder&) = delete; - void operator=( const feeder&) = delete; - - virtual ~feeder () {} - virtual void internal_add_copy(const Item& item) = 0; - virtual void internal_add_move(Item&& item) = 0; - - template friend class d2::feeder_impl; -public: - //! Add a work item to a running parallel_for_each. - void add(const Item& item) {internal_add_copy(item);} - void add(Item&& item) {internal_add_move(std::move(item));} -}; - -} // namespace d1 - -namespace d2 { -using namespace tbb::detail::d1; -/** Selects one of the two possible forms of function call member operator. - @ingroup algorithms **/ -template -struct parallel_for_each_operator_selector { -public: - template - static auto call(const Body& body, ItemArg&& item, FeederArg*) - -> decltype(tbb::detail::invoke(body, std::forward(item)), void()) { - #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) - // Suppression of Microsoft non-standard extension warnings - #pragma warning (push) - #pragma warning (disable: 4239) - #endif - - tbb::detail::invoke(body, std::forward(item)); - - #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) - #pragma warning (pop) - #endif - } - - template - static auto call(const Body& body, ItemArg&& item, FeederArg* feeder) - -> decltype(tbb::detail::invoke(body, std::forward(item), *feeder), void()) { - #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) - // Suppression of Microsoft non-standard extension warnings - #pragma warning (push) - #pragma warning (disable: 4239) - #endif - __TBB_ASSERT(feeder, "Feeder was not created but should be"); - - tbb::detail::invoke(body, std::forward(item), *feeder); - - #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) - #pragma warning (pop) - #endif - } -}; - -template -struct feeder_item_task: public task { - using feeder_type = feeder_impl; - - template - feeder_item_task(ItemType&& input_item, feeder_type& feeder, small_object_allocator& alloc) : - item(std::forward(input_item)), - my_feeder(feeder), - my_allocator(alloc) - {} - - void finalize(const execution_data& ed) { - my_feeder.my_wait_context.release(); - my_allocator.delete_object(this, ed); - } - - //! Hack for resolve ambiguity between calls to the body with and without moving the stored copy - //! Executing body with moving the copy should have higher priority - using first_priority = int; - using second_priority = double; - - template - static auto call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, first_priority) - -> decltype(parallel_for_each_operator_selector::call(call_body, std::move(call_item), &call_feeder), void()) - { - parallel_for_each_operator_selector::call(call_body, std::move(call_item), &call_feeder); - } - - template - static void call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, second_priority) { - parallel_for_each_operator_selector::call(call_body, call_item, &call_feeder); - } - - task* execute(execution_data& ed) override { - call(my_feeder.my_body, item, my_feeder, first_priority{}); - finalize(ed); - return nullptr; - } - - task* cancel(execution_data& ed) override { - finalize(ed); - return nullptr; - } - - Item item; - feeder_type& my_feeder; - small_object_allocator my_allocator; -}; // class feeder_item_task - -/** Implements new task adding procedure. - @ingroup algorithms **/ -template -class feeder_impl : public feeder { - // Avoiding use of copy constructor in a virtual method if the type does not support it - void internal_add_copy_impl(std::true_type, const Item& item) { - using feeder_task = feeder_item_task; - small_object_allocator alloc; - auto task = alloc.new_object(item, *this, alloc); - - my_wait_context.reserve(); - spawn(*task, my_execution_context); - } - - void internal_add_copy_impl(std::false_type, const Item&) { - __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object"); - } - - void internal_add_copy(const Item& item) override { - internal_add_copy_impl(typename std::is_copy_constructible::type(), item); - } - - void internal_add_move(Item&& item) override { - using feeder_task = feeder_item_task; - small_object_allocator alloc{}; - auto task = alloc.new_object(std::move(item), *this, alloc); - - my_wait_context.reserve(); - spawn(*task, my_execution_context); - } -public: - feeder_impl(const Body& body, wait_context& w_context, task_group_context &context) - : my_body(body), - my_wait_context(w_context) - , my_execution_context(context) - {} - - const Body& my_body; - wait_context& my_wait_context; - task_group_context& my_execution_context; -}; // class feeder_impl - -/** Execute computation under one element of the range - @ingroup algorithms **/ -template -struct for_each_iteration_task: public task { - using feeder_type = feeder_impl; - - for_each_iteration_task(Iterator input_item_ptr, const Body& body, feeder_impl* feeder_ptr, wait_context& wait_context) : - item_ptr(input_item_ptr), my_body(body), my_feeder_ptr(feeder_ptr), parent_wait_context(wait_context) - {} - - void finalize() { - parent_wait_context.release(); - } - - task* execute(execution_data&) override { - parallel_for_each_operator_selector::call(my_body, *item_ptr, my_feeder_ptr); - finalize(); - return nullptr; - } - - task* cancel(execution_data&) override { - finalize(); - return nullptr; - } - - Iterator item_ptr; - const Body& my_body; - feeder_impl* my_feeder_ptr; - wait_context& parent_wait_context; -}; // class for_each_iteration_task - -// Helper to get the type of the iterator to the internal sequence of copies -// If the element can be passed to the body as an rvalue - this iterator should be move_iterator -template -struct input_iteration_task_iterator_helper { - // For input iterators we pass const lvalue reference to the body - // It is prohibited to take non-constant lvalue references for input iterators - using type = const Item*; -}; - -template -struct input_iteration_task_iterator_helper::call(std::declval(), - std::declval(), - std::declval*>()))>> -{ - using type = std::move_iterator; -}; - -/** Split one block task to several(max_block_size) iteration tasks for input iterators - @ingroup algorithms **/ -template -struct input_block_handling_task : public task { - static constexpr size_t max_block_size = 4; - - using feeder_type = feeder_impl; - using iteration_task_iterator_type = typename input_iteration_task_iterator_helper::type; - using iteration_task = for_each_iteration_task; - - input_block_handling_task(wait_context& root_wait_context, task_group_context& e_context, - const Body& body, feeder_impl* feeder_ptr, small_object_allocator& alloc) - :my_size(0), my_wait_context(0), my_root_wait_context(root_wait_context), - my_execution_context(e_context), my_allocator(alloc) - { - auto item_it = block_iteration_space.begin(); - for (auto* it = task_pool.begin(); it != task_pool.end(); ++it) { - new (it) iteration_task(iteration_task_iterator_type(item_it++), body, feeder_ptr, my_wait_context); - } - } - - void finalize(const execution_data& ed) { - my_root_wait_context.release(); - my_allocator.delete_object(this, ed); - } - - task* execute(execution_data& ed) override { - __TBB_ASSERT( my_size > 0, "Negative size was passed to task"); - for (std::size_t counter = 1; counter < my_size; ++counter) { - my_wait_context.reserve(); - spawn(*(task_pool.begin() + counter), my_execution_context); - } - my_wait_context.reserve(); - execute_and_wait(*task_pool.begin(), my_execution_context, - my_wait_context, my_execution_context); - - // deallocate current task after children execution - finalize(ed); - return nullptr; - } - - task* cancel(execution_data& ed) override { - finalize(ed); - return nullptr; - } - - ~input_block_handling_task() { - for(std::size_t counter = 0; counter < max_block_size; ++counter) { - (task_pool.begin() + counter)->~iteration_task(); - if (counter < my_size) { - (block_iteration_space.begin() + counter)->~Item(); - } - } - } - - aligned_space block_iteration_space; - aligned_space task_pool; - std::size_t my_size; - wait_context my_wait_context; - wait_context& my_root_wait_context; - task_group_context& my_execution_context; - small_object_allocator my_allocator; -}; // class input_block_handling_task - -/** Split one block task to several(max_block_size) iteration tasks for forward iterators - @ingroup algorithms **/ -template -struct forward_block_handling_task : public task { - static constexpr size_t max_block_size = 4; - - using iteration_task = for_each_iteration_task; - - forward_block_handling_task(Iterator first, std::size_t size, - wait_context& w_context, task_group_context& e_context, - const Body& body, feeder_impl* feeder_ptr, - small_object_allocator& alloc) - : my_size(size), my_wait_context(0), my_root_wait_context(w_context), - my_execution_context(e_context), my_allocator(alloc) - { - auto* task_it = task_pool.begin(); - for (std::size_t i = 0; i < size; i++) { - new (task_it++) iteration_task(first, body, feeder_ptr, my_wait_context); - ++first; - } - } - - void finalize(const execution_data& ed) { - my_root_wait_context.release(); - my_allocator.delete_object(this, ed); - } - - task* execute(execution_data& ed) override { - __TBB_ASSERT( my_size > 0, "Negative size was passed to task"); - for(std::size_t counter = 1; counter < my_size; ++counter) { - my_wait_context.reserve(); - spawn(*(task_pool.begin() + counter), my_execution_context); - } - my_wait_context.reserve(); - execute_and_wait(*task_pool.begin(), my_execution_context, - my_wait_context, my_execution_context); - - // deallocate current task after children execution - finalize(ed); - return nullptr; - } - - task* cancel(execution_data& ed) override { - finalize(ed); - return nullptr; - } - - ~forward_block_handling_task() { - for(std::size_t counter = 0; counter < my_size; ++counter) { - (task_pool.begin() + counter)->~iteration_task(); - } - } - - aligned_space task_pool; - std::size_t my_size; - wait_context my_wait_context; - wait_context& my_root_wait_context; - task_group_context& my_execution_context; - small_object_allocator my_allocator; -}; // class forward_block_handling_task - -/** Body for parallel_for algorithm. - * Allows to redirect operations under random access iterators range to the parallel_for algorithm. - @ingroup algorithms **/ -template -class parallel_for_body_wrapper { - Iterator my_first; - const Body& my_body; - feeder_impl* my_feeder_ptr; -public: - parallel_for_body_wrapper(Iterator first, const Body& body, feeder_impl* feeder_ptr) - : my_first(first), my_body(body), my_feeder_ptr(feeder_ptr) {} - - void operator()(tbb::blocked_range range) const { -#if __INTEL_COMPILER -#pragma ivdep -#endif - for (std::size_t count = range.begin(); count != range.end(); count++) { - parallel_for_each_operator_selector::call(my_body, *(my_first + count), - my_feeder_ptr); - } - } -}; // class parallel_for_body_wrapper - - -/** Helper for getting iterators tag including inherited custom tags - @ingroup algorithms */ -template -using tag = typename std::iterator_traits::iterator_category; - -template -using iterator_tag_dispatch = typename - std::conditional< - std::is_base_of>::value, - std::random_access_iterator_tag, - typename std::conditional< - std::is_base_of>::value, - std::forward_iterator_tag, - std::input_iterator_tag - >::type - >::type; - -template -using feeder_is_required = tbb::detail::void_t(), - std::declval::reference>(), - std::declval&>()))>; - -// Creates feeder object only if the body can accept it -template -struct feeder_holder { - feeder_holder( wait_context&, task_group_context&, const Body& ) {} - - feeder_impl* feeder_ptr() { return nullptr; } -}; // class feeder_holder - -template -class feeder_holder> { -public: - feeder_holder( wait_context& w_context, task_group_context& context, const Body& body ) - : my_feeder(body, w_context, context) {} - - feeder_impl* feeder_ptr() { return &my_feeder; } -private: - feeder_impl my_feeder; -}; // class feeder_holder - -template -class for_each_root_task_base : public task { -public: - for_each_root_task_base(Iterator first, Iterator last, const Body& body, wait_context& w_context, task_group_context& e_context) - : my_first(first), my_last(last), my_wait_context(w_context), my_execution_context(e_context), - my_body(body), my_feeder_holder(my_wait_context, my_execution_context, my_body) - { - my_wait_context.reserve(); - } -private: - task* cancel(execution_data&) override { - this->my_wait_context.release(); - return nullptr; - } -protected: - Iterator my_first; - Iterator my_last; - wait_context& my_wait_context; - task_group_context& my_execution_context; - const Body& my_body; - feeder_holder my_feeder_holder; -}; // class for_each_root_task_base - -/** parallel_for_each algorithm root task - most generic version - * Splits input range to blocks - @ingroup algorithms **/ -template > -class for_each_root_task : public for_each_root_task_base -{ - using base_type = for_each_root_task_base; -public: - using base_type::base_type; -private: - task* execute(execution_data& ed) override { - using block_handling_type = input_block_handling_task; - - if (this->my_first == this->my_last) { - this->my_wait_context.release(); - return nullptr; - } - - this->my_wait_context.reserve(); - small_object_allocator alloc{}; - auto block_handling_task = alloc.new_object(ed, this->my_wait_context, this->my_execution_context, - this->my_body, this->my_feeder_holder.feeder_ptr(), - alloc); - - auto* block_iterator = block_handling_task->block_iteration_space.begin(); - for (; !(this->my_first == this->my_last) && block_handling_task->my_size < block_handling_type::max_block_size; ++this->my_first) { - // Move semantics are automatically used when supported by the iterator - new (block_iterator++) Item(*this->my_first); - ++block_handling_task->my_size; - } - - // Do not access this after spawn to avoid races - spawn(*this, this->my_execution_context); - return block_handling_task; - } -}; // class for_each_root_task - most generic implementation - -/** parallel_for_each algorithm root task - forward iterator based specialization - * Splits input range to blocks - @ingroup algorithms **/ -template -class for_each_root_task - : public for_each_root_task_base -{ - using base_type = for_each_root_task_base; -public: - using base_type::base_type; -private: - task* execute(execution_data& ed) override { - using block_handling_type = forward_block_handling_task; - if (this->my_first == this->my_last) { - this->my_wait_context.release(); - return nullptr; - } - - std::size_t block_size{0}; - Iterator first_block_element = this->my_first; - for (; !(this->my_first == this->my_last) && block_size < block_handling_type::max_block_size; ++this->my_first) { - ++block_size; - } - - this->my_wait_context.reserve(); - small_object_allocator alloc{}; - auto block_handling_task = alloc.new_object(ed, first_block_element, block_size, - this->my_wait_context, this->my_execution_context, - this->my_body, this->my_feeder_holder.feeder_ptr(), alloc); - - // Do not access this after spawn to avoid races - spawn(*this, this->my_execution_context); - return block_handling_task; - } -}; // class for_each_root_task - forward iterator based specialization - -/** parallel_for_each algorithm root task - random access iterator based specialization - * Splits input range to blocks - @ingroup algorithms **/ -template -class for_each_root_task - : public for_each_root_task_base -{ - using base_type = for_each_root_task_base; -public: - using base_type::base_type; -private: - task* execute(execution_data&) override { - tbb::parallel_for( - tbb::blocked_range(0, std::distance(this->my_first, this->my_last)), - parallel_for_body_wrapper(this->my_first, this->my_body, this->my_feeder_holder.feeder_ptr()) - , this->my_execution_context - ); - - this->my_wait_context.release(); - return nullptr; - } -}; // class for_each_root_task - random access iterator based specialization - -/** Helper for getting item type. If item type can be deduced from feeder - got it from feeder, - if feeder is generic - got item type from range. - @ingroup algorithms */ -template -auto feeder_argument_parser(void (Body::*)(Item, feeder&) const) -> FeederArg; - -template -decltype(feeder_argument_parser(&Body::operator())) get_item_type_impl(int); // for (T, feeder) -template Item get_item_type_impl(...); // stub - -template -using get_item_type = decltype(get_item_type_impl(0)); - -#if __TBB_CPP20_CONCEPTS_PRESENT -template -using feeder_item_type = std::remove_cvref_t>; - -template -concept parallel_for_each_iterator_body = - parallel_for_each_body, feeder_item_type>>; - -template -concept parallel_for_each_range_body = - parallel_for_each_body, feeder_item_type>>; -#endif - -/** Implements parallel iteration over a range. - @ingroup algorithms */ -template -void run_parallel_for_each( Iterator first, Iterator last, const Body& body, task_group_context& context) -{ - if (!(first == last)) { - using ItemType = get_item_type::value_type>; - wait_context w_context(0); - - for_each_root_task root_task(first, last, body, w_context, context); - - execute_and_wait(root_task, context, w_context, context); - } -} - -/** \page parallel_for_each_body_req Requirements on parallel_for_each body - Class \c Body implementing the concept of parallel_for_each body must define: - - \code - B::operator()( - cv_item_type item, - feeder& feeder - ) const - - OR - - B::operator()( cv_item_type& item ) const - \endcode Process item. - May be invoked concurrently for the same \c this but different \c item. - - - \code item_type( const item_type& ) \endcode - Copy a work item. - - \code ~item_type() \endcode Destroy a work item -**/ - -/** \name parallel_for_each - See also requirements on \ref parallel_for_each_body_req "parallel_for_each Body". **/ -//@{ -//! Parallel iteration over a range, with optional addition of more work. -/** @ingroup algorithms */ -template - __TBB_requires(std::input_iterator && parallel_for_each_iterator_body) -void parallel_for_each(Iterator first, Iterator last, const Body& body) { - task_group_context context(PARALLEL_FOR_EACH); - run_parallel_for_each(first, last, body, context); -} - -template - __TBB_requires(container_based_sequence && parallel_for_each_range_body) -void parallel_for_each(Range& rng, const Body& body) { - parallel_for_each(std::begin(rng), std::end(rng), body); -} - -template - __TBB_requires(container_based_sequence && parallel_for_each_range_body) -void parallel_for_each(const Range& rng, const Body& body) { - parallel_for_each(std::begin(rng), std::end(rng), body); -} - -//! Parallel iteration over a range, with optional addition of more work and user-supplied context -/** @ingroup algorithms */ -template - __TBB_requires(std::input_iterator && parallel_for_each_iterator_body) -void parallel_for_each(Iterator first, Iterator last, const Body& body, task_group_context& context) { - run_parallel_for_each(first, last, body, context); -} - -template - __TBB_requires(container_based_sequence && parallel_for_each_range_body) -void parallel_for_each(Range& rng, const Body& body, task_group_context& context) { - parallel_for_each(std::begin(rng), std::end(rng), body, context); -} - -template - __TBB_requires(container_based_sequence && parallel_for_each_range_body) -void parallel_for_each(const Range& rng, const Body& body, task_group_context& context) { - parallel_for_each(std::begin(rng), std::end(rng), body, context); -} - -} // namespace d2 -} // namespace detail -//! @endcond -//@} - -inline namespace v1 { -using detail::d2::parallel_for_each; -using detail::d1::feeder; -} // namespace v1 - -} // namespace tbb - -#endif /* __TBB_parallel_for_each_H */ diff --git a/include/oneapi/tbb/parallel_invoke.h b/include/oneapi/tbb/parallel_invoke.h deleted file mode 100644 index 6eb0f2e..0000000 --- a/include/oneapi/tbb/parallel_invoke.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_parallel_invoke_H -#define __TBB_parallel_invoke_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_exception.h" -#include "detail/_task.h" -#include "detail/_template_helpers.h" -#include "detail/_small_object_pool.h" - -#include "task_group.h" - -#include -#include -#include - -namespace tbb { -namespace detail { -namespace d1 { - -//! Simple task object, executing user method -template -struct function_invoker : public task { - function_invoker(const Function& function, WaitObject& wait_ctx) : - my_function(function), - parent_wait_ctx(wait_ctx) - {} - - task* execute(execution_data& ed) override { - my_function(); - parent_wait_ctx.release(ed); - call_itt_task_notify(destroy, this); - return nullptr; - } - - task* cancel(execution_data& ed) override { - parent_wait_ctx.release(ed); - return nullptr; - } - - const Function& my_function; - WaitObject& parent_wait_ctx; -}; // struct function_invoker - -//! Task object for managing subroots in trinary task trees. -// Endowed with additional synchronization logic (compatible with wait object intefaces) to support -// continuation passing execution. This task spawns 2 function_invoker tasks with first and second functors -// and then executes first functor by itself. But only the last executed functor must destruct and deallocate -// the subroot task. -template -struct invoke_subroot_task : public task { - wait_context& root_wait_ctx; - std::atomic ref_count{0}; - bool child_spawned = false; - - const F1& self_invoked_functor; - function_invoker> f2_invoker; - function_invoker> f3_invoker; - - task_group_context& my_execution_context; - small_object_allocator my_allocator; - - invoke_subroot_task(const F1& f1, const F2& f2, const F3& f3, wait_context& wait_ctx, task_group_context& context, - small_object_allocator& alloc) : - root_wait_ctx(wait_ctx), - self_invoked_functor(f1), - f2_invoker(f2, *this), - f3_invoker(f3, *this), - my_execution_context(context), - my_allocator(alloc) - { - root_wait_ctx.reserve(); - } - - void finalize(const execution_data& ed) { - root_wait_ctx.release(); - - my_allocator.delete_object(this, ed); - } - - void release(const execution_data& ed) { - __TBB_ASSERT(ref_count > 0, nullptr); - call_itt_task_notify(releasing, this); - if( --ref_count == 0 ) { - call_itt_task_notify(acquired, this); - finalize(ed); - } - } - - task* execute(execution_data& ed) override { - ref_count.fetch_add(3, std::memory_order_relaxed); - spawn(f3_invoker, my_execution_context); - spawn(f2_invoker, my_execution_context); - self_invoked_functor(); - - release(ed); - return nullptr; - } - - task* cancel(execution_data& ed) override { - if( ref_count > 0 ) { // detect children spawn - release(ed); - } else { - finalize(ed); - } - return nullptr; - } -}; // struct subroot_task - -class invoke_root_task { -public: - invoke_root_task(wait_context& wc) : my_wait_context(wc) {} - void release(const execution_data&) { - my_wait_context.release(); - } -private: - wait_context& my_wait_context; -}; - -template -void invoke_recursive_separation(wait_context& root_wait_ctx, task_group_context& context, const F1& f1) { - root_wait_ctx.reserve(1); - invoke_root_task root(root_wait_ctx); - function_invoker invoker1(f1, root); - - execute_and_wait(invoker1, context, root_wait_ctx, context); -} - -template -void invoke_recursive_separation(wait_context& root_wait_ctx, task_group_context& context, const F1& f1, const F2& f2) { - root_wait_ctx.reserve(2); - invoke_root_task root(root_wait_ctx); - function_invoker invoker1(f1, root); - function_invoker invoker2(f2, root); - - spawn(invoker1, context); - execute_and_wait(invoker2, context, root_wait_ctx, context); -} - -template -void invoke_recursive_separation(wait_context& root_wait_ctx, task_group_context& context, const F1& f1, const F2& f2, const F3& f3) { - root_wait_ctx.reserve(3); - invoke_root_task root(root_wait_ctx); - function_invoker invoker1(f1, root); - function_invoker invoker2(f2, root); - function_invoker invoker3(f3, root); - - //TODO: implement sub root for two tasks (measure performance) - spawn(invoker1, context); - spawn(invoker2, context); - execute_and_wait(invoker3, context, root_wait_ctx, context); -} - -template -void invoke_recursive_separation(wait_context& root_wait_ctx, task_group_context& context, - const F1& f1, const F2& f2, const F3& f3, const Fs&... fs) { - small_object_allocator alloc{}; - auto sub_root = alloc.new_object>(f1, f2, f3, root_wait_ctx, context, alloc); - spawn(*sub_root, context); - - invoke_recursive_separation(root_wait_ctx, context, fs...); -} - -template -void parallel_invoke_impl(task_group_context& context, const Fs&... fs) { - static_assert(sizeof...(Fs) >= 2, "Parallel invoke may be called with at least two callable"); - wait_context root_wait_ctx{0}; - - invoke_recursive_separation(root_wait_ctx, context, fs...); -} - -template -void parallel_invoke_impl(const F1& f1, const Fs&... fs) { - static_assert(sizeof...(Fs) >= 1, "Parallel invoke may be called with at least two callable"); - task_group_context context(PARALLEL_INVOKE); - wait_context root_wait_ctx{0}; - - invoke_recursive_separation(root_wait_ctx, context, fs..., f1); -} - -//! Passes last argument of variadic pack as first for handling user provided task_group_context -template -struct invoke_helper; - -template -struct invoke_helper, T, Fs...> : invoke_helper, Fs...> {}; - -template -struct invoke_helper, T> { - void operator()(Fs&&... args, T&& t) { - parallel_invoke_impl(std::forward(t), std::forward(args)...); - } -}; - -//! Parallel execution of several function objects -// We need to pass parameter pack through forwarding reference, -// since this pack may contain task_group_context that must be passed via lvalue non-const reference -template -void parallel_invoke(Fs&&... fs) { - invoke_helper, Fs...>()(std::forward(fs)...); -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::parallel_invoke; -} // namespace v1 - -} // namespace tbb -#endif /* __TBB_parallel_invoke_H */ diff --git a/include/oneapi/tbb/parallel_pipeline.h b/include/oneapi/tbb/parallel_pipeline.h deleted file mode 100644 index a204b9c..0000000 --- a/include/oneapi/tbb/parallel_pipeline.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_parallel_pipeline_H -#define __TBB_parallel_pipeline_H - -#include "detail/_pipeline_filters.h" -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "task_group.h" - -#include -#include -#include - -namespace tbb { -namespace detail { - -namespace r1 { -TBB_EXPORT void __TBB_EXPORTED_FUNC parallel_pipeline(task_group_context&, std::size_t, const d1::filter_node&); -} - -namespace d1 { - -enum class filter_mode : unsigned int -{ - //! processes multiple items in parallel and in no particular order - parallel = base_filter::filter_is_out_of_order, - //! processes items one at a time; all such filters process items in the same order - serial_in_order = base_filter::filter_is_serial, - //! processes items one at a time and in no particular order - serial_out_of_order = base_filter::filter_is_serial | base_filter::filter_is_out_of_order -}; -//! Class representing a chain of type-safe pipeline filters -/** @ingroup algorithms */ -template -class filter { - filter_node_ptr my_root; - filter( filter_node_ptr root ) : my_root(root) {} - friend void parallel_pipeline( size_t, const filter&, task_group_context& ); - template - friend filter make_filter( filter_mode, const Body& ); - template - friend filter operator&( const filter&, const filter& ); -public: - filter() = default; - filter( const filter& rhs ) : my_root(rhs.my_root) {} - filter( filter&& rhs ) : my_root(std::move(rhs.my_root)) {} - - void operator=(const filter& rhs) { - my_root = rhs.my_root; - } - void operator=( filter&& rhs ) { - my_root = std::move(rhs.my_root); - } - - template - filter( filter_mode mode, const Body& body ) : - my_root( new(r1::allocate_memory(sizeof(filter_node_leaf))) - filter_node_leaf(static_cast(mode), body) ) { - } - - filter& operator&=( const filter& right ) { - *this = *this & right; - return *this; - } - - void clear() { - // Like operator= with filter() on right side. - my_root = nullptr; - } -}; - -//! Create a filter to participate in parallel_pipeline -/** @ingroup algorithms */ -template -filter make_filter( filter_mode mode, const Body& body ) { - return filter_node_ptr( new(r1::allocate_memory(sizeof(filter_node_leaf))) - filter_node_leaf(static_cast(mode), body) ); -} - -//! Create a filter to participate in parallel_pipeline -/** @ingroup algorithms */ -template -filter, filter_output> make_filter( filter_mode mode, const Body& body ) { - return make_filter, filter_output>(mode, body); -} - -//! Composition of filters left and right. -/** @ingroup algorithms */ -template -filter operator&( const filter& left, const filter& right ) { - __TBB_ASSERT(left.my_root,"cannot use default-constructed filter as left argument of '&'"); - __TBB_ASSERT(right.my_root,"cannot use default-constructed filter as right argument of '&'"); - return filter_node_ptr( new (r1::allocate_memory(sizeof(filter_node))) filter_node(left.my_root,right.my_root) ); -} - -#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT -template -filter(filter_mode, Body) -->filter, filter_output>; -#endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT - -//! Parallel pipeline over chain of filters with user-supplied context. -/** @ingroup algorithms **/ -inline void parallel_pipeline(size_t max_number_of_live_tokens, const filter& filter_chain, task_group_context& context) { - r1::parallel_pipeline(context, max_number_of_live_tokens, *filter_chain.my_root); -} - -//! Parallel pipeline over chain of filters. -/** @ingroup algorithms **/ -inline void parallel_pipeline(size_t max_number_of_live_tokens, const filter& filter_chain) { - task_group_context context; - parallel_pipeline(max_number_of_live_tokens, filter_chain, context); -} - -//! Parallel pipeline over sequence of filters. -/** @ingroup algorithms **/ -template -void parallel_pipeline(size_t max_number_of_live_tokens, - const F1& filter1, - const F2& filter2, - FiltersContext&&... filters) { - parallel_pipeline(max_number_of_live_tokens, filter1 & filter2, std::forward(filters)...); -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 -{ -using detail::d1::parallel_pipeline; -using detail::d1::filter; -using detail::d1::make_filter; -using detail::d1::filter_mode; -using detail::d1::flow_control; -} -} // tbb - -#endif /* __TBB_parallel_pipeline_H */ diff --git a/include/oneapi/tbb/parallel_reduce.h b/include/oneapi/tbb/parallel_reduce.h deleted file mode 100644 index 401ad00..0000000 --- a/include/oneapi/tbb/parallel_reduce.h +++ /dev/null @@ -1,771 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_parallel_reduce_H -#define __TBB_parallel_reduce_H - -#include -#include "detail/_namespace_injection.h" -#include "detail/_task.h" -#include "detail/_aligned_space.h" -#include "detail/_small_object_pool.h" -#include "detail/_range_common.h" - -#include "task_group.h" // task_group_context -#include "partitioner.h" -#include "profiling.h" - -namespace tbb { -namespace detail { -#if __TBB_CPP20_CONCEPTS_PRESENT -inline namespace d0 { - -template -concept parallel_reduce_body = splittable && - requires( Body& body, const Range& range, Body& rhs ) { - body(range); - body.join(rhs); - }; - -template -concept parallel_reduce_function = std::invocable&, - const Range&, const Value&> && - std::convertible_to&, - const Range&, const Value&>, - Value>; - -template -concept parallel_reduce_combine = std::invocable&, - const Value&, const Value&> && - std::convertible_to&, - const Value&, const Value&>, - Value>; - -} // namespace d0 -#endif // __TBB_CPP20_CONCEPTS_PRESENT -namespace d1 { - -//! Tree node type for parallel_reduce. -/** @ingroup algorithms */ -//TODO: consider folding tree via bypass execution(instead of manual folding) -// for better cancellation and critical tasks handling (performance measurements required). -template -struct reduction_tree_node : public tree_node { - tbb::detail::aligned_space zombie_space; - Body& left_body; - bool has_right_zombie{false}; - - reduction_tree_node(node* parent, int ref_count, Body& input_left_body, small_object_allocator& alloc) : - tree_node{parent, ref_count, alloc}, - left_body(input_left_body) /* gcc4.8 bug - braced-initialization doesn't work for class members of reference type */ - {} - - void join(task_group_context* context) { - if (has_right_zombie && !context->is_group_execution_cancelled()) - left_body.join(*zombie_space.begin()); - } - - ~reduction_tree_node() { - if( has_right_zombie ) zombie_space.begin()->~Body(); - } -}; - -//! Task type used to split the work of parallel_reduce. -/** @ingroup algorithms */ -template -struct start_reduce : public task { - Range my_range; - Body* my_body; - node* my_parent; - - typename Partitioner::task_partition_type my_partition; - small_object_allocator my_allocator; - bool is_right_child; - - task* execute(execution_data&) override; - task* cancel(execution_data&) override; - void finalize(const execution_data&); - - using tree_node_type = reduction_tree_node; - - //! Constructor reduce root task. - start_reduce( const Range& range, Body& body, Partitioner& partitioner, small_object_allocator& alloc ) : - my_range(range), - my_body(&body), - my_parent(nullptr), - my_partition(partitioner), - my_allocator(alloc), - is_right_child(false) {} - //! Splitting constructor used to generate children. - /** parent_ becomes left child. Newly constructed object is right child. */ - start_reduce( start_reduce& parent_, typename Partitioner::split_type& split_obj, small_object_allocator& alloc ) : - my_range(parent_.my_range, get_range_split_object(split_obj)), - my_body(parent_.my_body), - my_parent(nullptr), - my_partition(parent_.my_partition, split_obj), - my_allocator(alloc), - is_right_child(true) - { - parent_.is_right_child = false; - } - //! Construct right child from the given range as response to the demand. - /** parent_ remains left child. Newly constructed object is right child. */ - start_reduce( start_reduce& parent_, const Range& r, depth_t d, small_object_allocator& alloc ) : - my_range(r), - my_body(parent_.my_body), - my_parent(nullptr), - my_partition(parent_.my_partition, split()), - my_allocator(alloc), - is_right_child(true) - { - my_partition.align_depth( d ); - parent_.is_right_child = false; - } - static void run(const Range& range, Body& body, Partitioner& partitioner, task_group_context& context) { - if ( !range.empty() ) { - wait_node wn; - small_object_allocator alloc{}; - auto reduce_task = alloc.new_object(range, body, partitioner, alloc); - reduce_task->my_parent = &wn; - execute_and_wait(*reduce_task, context, wn.m_wait, context); - } - } - static void run(const Range& range, Body& body, Partitioner& partitioner) { - // Bound context prevents exceptions from body to affect nesting or sibling algorithms, - // and allows users to handle exceptions safely by wrapping parallel_reduce in the try-block. - task_group_context context(PARALLEL_REDUCE); - run(range, body, partitioner, context); - } - //! Run body for range, serves as callback for partitioner - void run_body( Range &r ) { - tbb::detail::invoke(*my_body, r); - } - - //! spawn right task, serves as callback for partitioner - void offer_work(typename Partitioner::split_type& split_obj, execution_data& ed) { - offer_work_impl(ed, *this, split_obj); - } - //! spawn right task, serves as callback for partitioner - void offer_work(const Range& r, depth_t d, execution_data& ed) { - offer_work_impl(ed, *this, r, d); - } - -private: - template - void offer_work_impl(execution_data& ed, Args&&... args) { - small_object_allocator alloc{}; - // New right child - auto right_child = alloc.new_object(ed, std::forward(args)..., alloc); - - // New root node as a continuation and ref count. Left and right child attach to the new parent. - right_child->my_parent = my_parent = alloc.new_object(ed, my_parent, 2, *my_body, alloc); - - // Spawn the right sibling - right_child->spawn_self(ed); - } - - void spawn_self(execution_data& ed) { - my_partition.spawn_task(*this, *context(ed)); - } -}; - -//! fold the tree and deallocate the task -template -void start_reduce::finalize(const execution_data& ed) { - // Get the current parent and wait object before an object destruction - node* parent = my_parent; - auto allocator = my_allocator; - // Task execution finished - destroy it - this->~start_reduce(); - // Unwind the tree decrementing the parent`s reference count - fold_tree(parent, ed); - allocator.deallocate(this, ed); -} - -//! Execute parallel_reduce task -template -task* start_reduce::execute(execution_data& ed) { - if (!is_same_affinity(ed)) { - my_partition.note_affinity(execution_slot(ed)); - } - my_partition.check_being_stolen(*this, ed); - - // The acquire barrier synchronizes the data pointed with my_body if the left - // task has already finished. - __TBB_ASSERT(my_parent, nullptr); - if( is_right_child && my_parent->m_ref_count.load(std::memory_order_acquire) == 2 ) { - tree_node_type* parent_ptr = static_cast(my_parent); - my_body = static_cast(new( parent_ptr->zombie_space.begin() ) Body(*my_body, split())); - parent_ptr->has_right_zombie = true; - } - __TBB_ASSERT(my_body != nullptr, "Incorrect body value"); - - my_partition.execute(*this, my_range, ed); - - finalize(ed); - return nullptr; -} - -//! Cancel parallel_reduce task -template -task* start_reduce::cancel(execution_data& ed) { - finalize(ed); - return nullptr; -} - -//! Tree node type for parallel_deterministic_reduce. -/** @ingroup algorithms */ -template -struct deterministic_reduction_tree_node : public tree_node { - Body right_body; - Body& left_body; - - deterministic_reduction_tree_node(node* parent, int ref_count, Body& input_left_body, small_object_allocator& alloc) : - tree_node{parent, ref_count, alloc}, - right_body{input_left_body, detail::split()}, - left_body(input_left_body) - {} - - void join(task_group_context* context) { - if (!context->is_group_execution_cancelled()) - left_body.join(right_body); - } -}; - -//! Task type used to split the work of parallel_deterministic_reduce. -/** @ingroup algorithms */ -template -struct start_deterministic_reduce : public task { - Range my_range; - Body& my_body; - node* my_parent; - - typename Partitioner::task_partition_type my_partition; - small_object_allocator my_allocator; - - task* execute(execution_data&) override; - task* cancel(execution_data&) override; - void finalize(const execution_data&); - - using tree_node_type = deterministic_reduction_tree_node; - - //! Constructor deterministic_reduce root task. - start_deterministic_reduce( const Range& range, Partitioner& partitioner, Body& body, small_object_allocator& alloc ) : - my_range(range), - my_body(body), - my_parent(nullptr), - my_partition(partitioner), - my_allocator(alloc) {} - //! Splitting constructor used to generate children. - /** parent_ becomes left child. Newly constructed object is right child. */ - start_deterministic_reduce( start_deterministic_reduce& parent_, typename Partitioner::split_type& split_obj, Body& body, - small_object_allocator& alloc ) : - my_range(parent_.my_range, get_range_split_object(split_obj)), - my_body(body), - my_parent(nullptr), - my_partition(parent_.my_partition, split_obj), - my_allocator(alloc) {} - static void run(const Range& range, Body& body, Partitioner& partitioner, task_group_context& context) { - if ( !range.empty() ) { - wait_node wn; - small_object_allocator alloc{}; - auto deterministic_reduce_task = - alloc.new_object(range, partitioner, body, alloc); - deterministic_reduce_task->my_parent = &wn; - execute_and_wait(*deterministic_reduce_task, context, wn.m_wait, context); - } - } - static void run(const Range& range, Body& body, Partitioner& partitioner) { - // Bound context prevents exceptions from body to affect nesting or sibling algorithms, - // and allows users to handle exceptions safely by wrapping parallel_deterministic_reduce - // in the try-block. - task_group_context context(PARALLEL_REDUCE); - run(range, body, partitioner, context); - } - //! Run body for range, serves as callback for partitioner - void run_body( Range &r ) { - tbb::detail::invoke(my_body, r); - } - //! Spawn right task, serves as callback for partitioner - void offer_work(typename Partitioner::split_type& split_obj, execution_data& ed) { - offer_work_impl(ed, *this, split_obj); - } -private: - template - void offer_work_impl(execution_data& ed, Args&&... args) { - small_object_allocator alloc{}; - // New root node as a continuation and ref count. Left and right child attach to the new parent. Split the body. - auto new_tree_node = alloc.new_object(ed, my_parent, 2, my_body, alloc); - - // New right child - auto right_child = alloc.new_object(ed, std::forward(args)..., new_tree_node->right_body, alloc); - - right_child->my_parent = my_parent = new_tree_node; - - // Spawn the right sibling - right_child->spawn_self(ed); - } - - void spawn_self(execution_data& ed) { - my_partition.spawn_task(*this, *context(ed)); - } -}; - -//! Fold the tree and deallocate the task -template -void start_deterministic_reduce::finalize(const execution_data& ed) { - // Get the current parent and wait object before an object destruction - node* parent = my_parent; - - auto allocator = my_allocator; - // Task execution finished - destroy it - this->~start_deterministic_reduce(); - // Unwind the tree decrementing the parent`s reference count - fold_tree(parent, ed); - allocator.deallocate(this, ed); -} - -//! Execute parallel_deterministic_reduce task -template -task* start_deterministic_reduce::execute(execution_data& ed) { - if (!is_same_affinity(ed)) { - my_partition.note_affinity(execution_slot(ed)); - } - my_partition.check_being_stolen(*this, ed); - - my_partition.execute(*this, my_range, ed); - - finalize(ed); - return nullptr; -} - -//! Cancel parallel_deterministic_reduce task -template -task* start_deterministic_reduce::cancel(execution_data& ed) { - finalize(ed); - return nullptr; -} - - -//! Auxiliary class for parallel_reduce; for internal use only. -/** The adaptor class that implements \ref parallel_reduce_body_req "parallel_reduce Body" - using given \ref parallel_reduce_lambda_req "anonymous function objects". - **/ -/** @ingroup algorithms */ -template -class lambda_reduce_body { -//TODO: decide if my_real_body, my_reduction, and my_identity_element should be copied or referenced -// (might require some performance measurements) - - const Value& my_identity_element; - const RealBody& my_real_body; - const Reduction& my_reduction; - Value my_value; - lambda_reduce_body& operator= ( const lambda_reduce_body& other ); -public: - lambda_reduce_body( const Value& identity, const RealBody& body, const Reduction& reduction ) - : my_identity_element(identity) - , my_real_body(body) - , my_reduction(reduction) - , my_value(identity) - { } - lambda_reduce_body( const lambda_reduce_body& other ) = default; - lambda_reduce_body( lambda_reduce_body& other, tbb::split ) - : my_identity_element(other.my_identity_element) - , my_real_body(other.my_real_body) - , my_reduction(other.my_reduction) - , my_value(other.my_identity_element) - { } - void operator()(Range& range) { - my_value = tbb::detail::invoke(my_real_body, range, const_cast(my_value)); - } - void join( lambda_reduce_body& rhs ) { - my_value = tbb::detail::invoke(my_reduction, const_cast(my_value), - const_cast(rhs.my_value)); - } - Value result() const { - return my_value; - } -}; - - -// Requirements on Range concept are documented in blocked_range.h - -/** \page parallel_reduce_body_req Requirements on parallel_reduce body - Class \c Body implementing the concept of parallel_reduce body must define: - - \code Body::Body( Body&, split ); \endcode Splitting constructor. - Must be able to run concurrently with operator() and method \c join - - \code Body::~Body(); \endcode Destructor - - \code void Body::operator()( Range& r ); \endcode Function call operator applying body to range \c r - and accumulating the result - - \code void Body::join( Body& b ); \endcode Join results. - The result in \c b should be merged into the result of \c this -**/ - -/** \page parallel_reduce_lambda_req Requirements on parallel_reduce anonymous function objects (lambda functions) - TO BE DOCUMENTED -**/ - -/** \name parallel_reduce - See also requirements on \ref range_req "Range" and \ref parallel_reduce_body_req "parallel_reduce Body". **/ -//@{ - -//! Parallel iteration with reduction and default partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body ) { - start_reduce::run( range, body, __TBB_DEFAULT_PARTITIONER() ); -} - -//! Parallel iteration with reduction and simple_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body, const simple_partitioner& partitioner ) { - start_reduce::run( range, body, partitioner ); -} - -//! Parallel iteration with reduction and auto_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body, const auto_partitioner& partitioner ) { - start_reduce::run( range, body, partitioner ); -} - -//! Parallel iteration with reduction and static_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body, const static_partitioner& partitioner ) { - start_reduce::run( range, body, partitioner ); -} - -//! Parallel iteration with reduction and affinity_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body, affinity_partitioner& partitioner ) { - start_reduce::run( range, body, partitioner ); -} - -//! Parallel iteration with reduction, default partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body, task_group_context& context ) { - start_reduce::run( range, body, __TBB_DEFAULT_PARTITIONER(), context ); -} - -//! Parallel iteration with reduction, simple partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body, const simple_partitioner& partitioner, task_group_context& context ) { - start_reduce::run( range, body, partitioner, context ); -} - -//! Parallel iteration with reduction, auto_partitioner and user-supplied context -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body, const auto_partitioner& partitioner, task_group_context& context ) { - start_reduce::run( range, body, partitioner, context ); -} - -//! Parallel iteration with reduction, static_partitioner and user-supplied context -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body, const static_partitioner& partitioner, task_group_context& context ) { - start_reduce::run( range, body, partitioner, context ); -} - -//! Parallel iteration with reduction, affinity_partitioner and user-supplied context -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_reduce( const Range& range, Body& body, affinity_partitioner& partitioner, task_group_context& context ) { - start_reduce::run( range, body, partitioner, context ); -} -/** parallel_reduce overloads that work with anonymous function objects - (see also \ref parallel_reduce_lambda_req "requirements on parallel_reduce anonymous function objects"). **/ - -//! Parallel iteration with reduction and default partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,const __TBB_DEFAULT_PARTITIONER> - ::run(range, body, __TBB_DEFAULT_PARTITIONER() ); - return body.result(); -} - -//! Parallel iteration with reduction and simple_partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - const simple_partitioner& partitioner ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,const simple_partitioner> - ::run(range, body, partitioner ); - return body.result(); -} - -//! Parallel iteration with reduction and auto_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - const auto_partitioner& partitioner ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,const auto_partitioner> - ::run( range, body, partitioner ); - return body.result(); -} - -//! Parallel iteration with reduction and static_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - const static_partitioner& partitioner ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,const static_partitioner> - ::run( range, body, partitioner ); - return body.result(); -} - -//! Parallel iteration with reduction and affinity_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - affinity_partitioner& partitioner ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,affinity_partitioner> - ::run( range, body, partitioner ); - return body.result(); -} - -//! Parallel iteration with reduction, default partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - task_group_context& context ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,const __TBB_DEFAULT_PARTITIONER> - ::run( range, body, __TBB_DEFAULT_PARTITIONER(), context ); - return body.result(); -} - -//! Parallel iteration with reduction, simple partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - const simple_partitioner& partitioner, task_group_context& context ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,const simple_partitioner> - ::run( range, body, partitioner, context ); - return body.result(); -} - -//! Parallel iteration with reduction, auto_partitioner and user-supplied context -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - const auto_partitioner& partitioner, task_group_context& context ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,const auto_partitioner> - ::run( range, body, partitioner, context ); - return body.result(); -} - -//! Parallel iteration with reduction, static_partitioner and user-supplied context -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - const static_partitioner& partitioner, task_group_context& context ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,const static_partitioner> - ::run( range, body, partitioner, context ); - return body.result(); -} - -//! Parallel iteration with reduction, affinity_partitioner and user-supplied context -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - affinity_partitioner& partitioner, task_group_context& context ) { - lambda_reduce_body body(identity, real_body, reduction); - start_reduce,affinity_partitioner> - ::run( range, body, partitioner, context ); - return body.result(); -} - -//! Parallel iteration with deterministic reduction and default simple partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_deterministic_reduce( const Range& range, Body& body ) { - start_deterministic_reduce::run(range, body, simple_partitioner()); -} - -//! Parallel iteration with deterministic reduction and simple partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_deterministic_reduce( const Range& range, Body& body, const simple_partitioner& partitioner ) { - start_deterministic_reduce::run(range, body, partitioner); -} - -//! Parallel iteration with deterministic reduction and static partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_deterministic_reduce( const Range& range, Body& body, const static_partitioner& partitioner ) { - start_deterministic_reduce::run(range, body, partitioner); -} - -//! Parallel iteration with deterministic reduction, default simple partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_deterministic_reduce( const Range& range, Body& body, task_group_context& context ) { - start_deterministic_reduce::run( range, body, simple_partitioner(), context ); -} - -//! Parallel iteration with deterministic reduction, simple partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_deterministic_reduce( const Range& range, Body& body, const simple_partitioner& partitioner, task_group_context& context ) { - start_deterministic_reduce::run(range, body, partitioner, context); -} - -//! Parallel iteration with deterministic reduction, static partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_body) -void parallel_deterministic_reduce( const Range& range, Body& body, const static_partitioner& partitioner, task_group_context& context ) { - start_deterministic_reduce::run(range, body, partitioner, context); -} - -/** parallel_reduce overloads that work with anonymous function objects - (see also \ref parallel_reduce_lambda_req "requirements on parallel_reduce anonymous function objects"). **/ - -//! Parallel iteration with deterministic reduction and default simple partitioner. -// TODO: consider making static_partitioner the default -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction ) { - return parallel_deterministic_reduce(range, identity, real_body, reduction, simple_partitioner()); -} - -//! Parallel iteration with deterministic reduction and simple partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, const simple_partitioner& partitioner ) { - lambda_reduce_body body(identity, real_body, reduction); - start_deterministic_reduce, const simple_partitioner> - ::run(range, body, partitioner); - return body.result(); -} - -//! Parallel iteration with deterministic reduction and static partitioner. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, const static_partitioner& partitioner ) { - lambda_reduce_body body(identity, real_body, reduction); - start_deterministic_reduce, const static_partitioner> - ::run(range, body, partitioner); - return body.result(); -} - -//! Parallel iteration with deterministic reduction, default simple partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - task_group_context& context ) { - return parallel_deterministic_reduce(range, identity, real_body, reduction, simple_partitioner(), context); -} - -//! Parallel iteration with deterministic reduction, simple partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - const simple_partitioner& partitioner, task_group_context& context ) { - lambda_reduce_body body(identity, real_body, reduction); - start_deterministic_reduce, const simple_partitioner> - ::run(range, body, partitioner, context); - return body.result(); -} - -//! Parallel iteration with deterministic reduction, static partitioner and user-supplied context. -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_reduce_function && - parallel_reduce_combine) -Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, - const static_partitioner& partitioner, task_group_context& context ) { - lambda_reduce_body body(identity, real_body, reduction); - start_deterministic_reduce, const static_partitioner> - ::run(range, body, partitioner, context); - return body.result(); -} -//@} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::parallel_reduce; -using detail::d1::parallel_deterministic_reduce; -// Split types -using detail::split; -using detail::proportional_split; -} // namespace v1 - -} // namespace tbb -#endif /* __TBB_parallel_reduce_H */ diff --git a/include/oneapi/tbb/parallel_scan.h b/include/oneapi/tbb/parallel_scan.h deleted file mode 100644 index 6d2a4d6..0000000 --- a/include/oneapi/tbb/parallel_scan.h +++ /dev/null @@ -1,630 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_parallel_scan_H -#define __TBB_parallel_scan_H - -#include - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_exception.h" -#include "detail/_task.h" - -#include "profiling.h" -#include "partitioner.h" -#include "blocked_range.h" -#include "task_group.h" - -namespace tbb { -namespace detail { -namespace d1 { - -//! Used to indicate that the initial scan is being performed. -/** @ingroup algorithms */ -struct pre_scan_tag { - static bool is_final_scan() {return false;} - operator bool() {return is_final_scan();} -}; - -//! Used to indicate that the final scan is being performed. -/** @ingroup algorithms */ -struct final_scan_tag { - static bool is_final_scan() {return true;} - operator bool() {return is_final_scan();} -}; - -template -struct sum_node; - -#if __TBB_CPP20_CONCEPTS_PRESENT -} // namespace d1 -namespace d0 { - -template -concept parallel_scan_body = splittable && - requires( Body& body, const Range& range, Body& other ) { - body(range, tbb::detail::d1::pre_scan_tag{}); - body(range, tbb::detail::d1::final_scan_tag{}); - body.reverse_join(other); - body.assign(other); - }; - -template -concept parallel_scan_function = std::invocable&, - const Range&, const Value&, bool> && - std::convertible_to&, - const Range&, const Value&, bool>, - Value>; - -template -concept parallel_scan_combine = std::invocable&, - const Value&, const Value&> && - std::convertible_to&, - const Value&, const Value&>, - Value>; - -} // namespace d0 -namespace d1 { -#endif // __TBB_CPP20_CONCEPTS_PRESENT - -//! Performs final scan for a leaf -/** @ingroup algorithms */ -template -struct final_sum : public task { -private: - using sum_node_type = sum_node; - Body m_body; - aligned_space m_range; - //! Where to put result of last subrange, or nullptr if not last subrange. - Body* m_stuff_last; - - wait_context& m_wait_context; - sum_node_type* m_parent = nullptr; -public: - small_object_allocator m_allocator; - final_sum( Body& body, wait_context& w_o, small_object_allocator& alloc ) : - m_body(body, split()), m_wait_context(w_o), m_allocator(alloc) { - poison_pointer(m_stuff_last); - } - - final_sum( final_sum& sum, small_object_allocator& alloc ) : - m_body(sum.m_body, split()), m_wait_context(sum.m_wait_context), m_allocator(alloc) { - poison_pointer(m_stuff_last); - } - - ~final_sum() { - m_range.begin()->~Range(); - } - void finish_construction( sum_node_type* parent, const Range& range, Body* stuff_last ) { - __TBB_ASSERT( m_parent == nullptr, nullptr ); - m_parent = parent; - new( m_range.begin() ) Range(range); - m_stuff_last = stuff_last; - } -private: - sum_node_type* release_parent() { - call_itt_task_notify(releasing, m_parent); - if (m_parent) { - auto parent = m_parent; - m_parent = nullptr; - if (parent->ref_count.fetch_sub(1) == 1) { - return parent; - } - } - else - m_wait_context.release(); - return nullptr; - } - sum_node_type* finalize(const execution_data& ed){ - sum_node_type* next_task = release_parent(); - m_allocator.delete_object(this, ed); - return next_task; - } - -public: - task* execute(execution_data& ed) override { - m_body( *m_range.begin(), final_scan_tag() ); - if( m_stuff_last ) - m_stuff_last->assign(m_body); - - return finalize(ed); - } - task* cancel(execution_data& ed) override { - return finalize(ed); - } - template - void operator()( const Range& r, Tag tag ) { - m_body( r, tag ); - } - void reverse_join( final_sum& a ) { - m_body.reverse_join(a.m_body); - } - void reverse_join( Body& body ) { - m_body.reverse_join(body); - } - void assign_to( Body& body ) { - body.assign(m_body); - } - void self_destroy(const execution_data& ed) { - m_allocator.delete_object(this, ed); - } -}; - -//! Split work to be done in the scan. -/** @ingroup algorithms */ -template -struct sum_node : public task { -private: - using final_sum_type = final_sum; -public: - final_sum_type *m_incoming; - final_sum_type *m_body; - Body *m_stuff_last; -private: - final_sum_type *m_left_sum; - sum_node *m_left; - sum_node *m_right; - bool m_left_is_final; - Range m_range; - wait_context& m_wait_context; - sum_node* m_parent; - small_object_allocator m_allocator; -public: - std::atomic ref_count{0}; - sum_node( const Range range, bool left_is_final_, sum_node* parent, wait_context& w_o, small_object_allocator& alloc ) : - m_stuff_last(nullptr), - m_left_sum(nullptr), - m_left(nullptr), - m_right(nullptr), - m_left_is_final(left_is_final_), - m_range(range), - m_wait_context(w_o), - m_parent(parent), - m_allocator(alloc) - { - if( m_parent ) - m_parent->ref_count.fetch_add(1); - // Poison fields that will be set by second pass. - poison_pointer(m_body); - poison_pointer(m_incoming); - } - - ~sum_node() { - if (m_parent) - m_parent->ref_count.fetch_sub(1); - } -private: - sum_node* release_parent() { - call_itt_task_notify(releasing, m_parent); - if (m_parent) { - auto parent = m_parent; - m_parent = nullptr; - if (parent->ref_count.fetch_sub(1) == 1) { - return parent; - } - } - else - m_wait_context.release(); - return nullptr; - } - task* create_child( const Range& range, final_sum_type& body, sum_node* child, final_sum_type* incoming, Body* stuff_last ) { - if( child ) { - __TBB_ASSERT( is_poisoned(child->m_body) && is_poisoned(child->m_incoming), nullptr ); - child->prepare_for_execution(body, incoming, stuff_last); - return child; - } else { - body.finish_construction(this, range, stuff_last); - return &body; - } - } - - sum_node* finalize(const execution_data& ed) { - sum_node* next_task = release_parent(); - m_allocator.delete_object(this, ed); - return next_task; - } - -public: - void prepare_for_execution(final_sum_type& body, final_sum_type* incoming, Body *stuff_last) { - this->m_body = &body; - this->m_incoming = incoming; - this->m_stuff_last = stuff_last; - } - task* execute(execution_data& ed) override { - if( m_body ) { - if( m_incoming ) - m_left_sum->reverse_join( *m_incoming ); - task* right_child = this->create_child(Range(m_range,split()), *m_left_sum, m_right, m_left_sum, m_stuff_last); - task* left_child = m_left_is_final ? nullptr : this->create_child(m_range, *m_body, m_left, m_incoming, nullptr); - ref_count = (left_child != nullptr) + (right_child != nullptr); - m_body = nullptr; - if( left_child ) { - spawn(*right_child, *ed.context); - return left_child; - } else { - return right_child; - } - } else { - return finalize(ed); - } - } - task* cancel(execution_data& ed) override { - return finalize(ed); - } - void self_destroy(const execution_data& ed) { - m_allocator.delete_object(this, ed); - } - template - friend struct start_scan; - - template - friend struct finish_scan; -}; - -//! Combine partial results -/** @ingroup algorithms */ -template -struct finish_scan : public task { -private: - using sum_node_type = sum_node; - using final_sum_type = final_sum; - final_sum_type** const m_sum_slot; - sum_node_type*& m_return_slot; - small_object_allocator m_allocator; -public: - std::atomic m_right_zombie; - sum_node_type& m_result; - std::atomic ref_count{2}; - finish_scan* m_parent; - wait_context& m_wait_context; - task* execute(execution_data& ed) override { - __TBB_ASSERT( m_result.ref_count.load() == static_cast((m_result.m_left!=nullptr)+(m_result.m_right!=nullptr)), nullptr ); - if( m_result.m_left ) - m_result.m_left_is_final = false; - final_sum_type* right_zombie = m_right_zombie.load(std::memory_order_acquire); - if( right_zombie && m_sum_slot ) - (*m_sum_slot)->reverse_join(*m_result.m_left_sum); - __TBB_ASSERT( !m_return_slot, nullptr ); - if( right_zombie || m_result.m_right ) { - m_return_slot = &m_result; - } else { - m_result.self_destroy(ed); - } - if( right_zombie && !m_sum_slot && !m_result.m_right ) { - right_zombie->self_destroy(ed); - m_right_zombie.store(nullptr, std::memory_order_relaxed); - } - return finalize(ed); - } - task* cancel(execution_data& ed) override { - return finalize(ed); - } - finish_scan(sum_node_type*& return_slot, final_sum_type** sum, sum_node_type& result_, finish_scan* parent, wait_context& w_o, small_object_allocator& alloc) : - m_sum_slot(sum), - m_return_slot(return_slot), - m_allocator(alloc), - m_right_zombie(nullptr), - m_result(result_), - m_parent(parent), - m_wait_context(w_o) - { - __TBB_ASSERT( !m_return_slot, nullptr ); - } -private: - finish_scan* release_parent() { - call_itt_task_notify(releasing, m_parent); - if (m_parent) { - auto parent = m_parent; - m_parent = nullptr; - if (parent->ref_count.fetch_sub(1) == 1) { - return parent; - } - } - else - m_wait_context.release(); - return nullptr; - } - finish_scan* finalize(const execution_data& ed) { - finish_scan* next_task = release_parent(); - m_allocator.delete_object(this, ed); - return next_task; - } -}; - -//! Initial task to split the work -/** @ingroup algorithms */ -template -struct start_scan : public task { -private: - using sum_node_type = sum_node; - using final_sum_type = final_sum; - using finish_pass1_type = finish_scan; - std::reference_wrapper m_return_slot; - Range m_range; - std::reference_wrapper m_body; - typename Partitioner::partition_type m_partition; - /** Non-null if caller is requesting total. */ - final_sum_type** m_sum_slot; - bool m_is_final; - bool m_is_right_child; - - finish_pass1_type* m_parent; - small_object_allocator m_allocator; - wait_context& m_wait_context; - - finish_pass1_type* release_parent() { - call_itt_task_notify(releasing, m_parent); - if (m_parent) { - auto parent = m_parent; - m_parent = nullptr; - if (parent->ref_count.fetch_sub(1) == 1) { - return parent; - } - } - else - m_wait_context.release(); - return nullptr; - } - - finish_pass1_type* finalize( const execution_data& ed ) { - finish_pass1_type* next_task = release_parent(); - m_allocator.delete_object(this, ed); - return next_task; - } - -public: - task* execute( execution_data& ) override; - task* cancel( execution_data& ed ) override { - return finalize(ed); - } - start_scan( sum_node_type*& return_slot, start_scan& parent, small_object_allocator& alloc ) : - m_return_slot(return_slot), - m_range(parent.m_range,split()), - m_body(parent.m_body), - m_partition(parent.m_partition,split()), - m_sum_slot(parent.m_sum_slot), - m_is_final(parent.m_is_final), - m_is_right_child(true), - m_parent(parent.m_parent), - m_allocator(alloc), - m_wait_context(parent.m_wait_context) - { - __TBB_ASSERT( !m_return_slot, nullptr ); - parent.m_is_right_child = false; - } - - start_scan( sum_node_type*& return_slot, const Range& range, final_sum_type& body, const Partitioner& partitioner, wait_context& w_o, small_object_allocator& alloc ) : - m_return_slot(return_slot), - m_range(range), - m_body(body), - m_partition(partitioner), - m_sum_slot(nullptr), - m_is_final(true), - m_is_right_child(false), - m_parent(nullptr), - m_allocator(alloc), - m_wait_context(w_o) - { - __TBB_ASSERT( !m_return_slot, nullptr ); - } - - static void run( const Range& range, Body& body, const Partitioner& partitioner ) { - if( !range.empty() ) { - task_group_context context(PARALLEL_SCAN); - - using start_pass1_type = start_scan; - sum_node_type* root = nullptr; - wait_context w_ctx{1}; - small_object_allocator alloc{}; - - auto& temp_body = *alloc.new_object(body, w_ctx, alloc); - temp_body.reverse_join(body); - - auto& pass1 = *alloc.new_object(/*m_return_slot=*/root, range, temp_body, partitioner, w_ctx, alloc); - - execute_and_wait(pass1, context, w_ctx, context); - if( root ) { - root->prepare_for_execution(temp_body, nullptr, &body); - w_ctx.reserve(); - execute_and_wait(*root, context, w_ctx, context); - } else { - temp_body.assign_to(body); - temp_body.finish_construction(nullptr, range, nullptr); - alloc.delete_object(&temp_body); - } - } - } -}; - -template -task* start_scan::execute( execution_data& ed ) { - // Inspecting m_parent->result.left_sum would ordinarily be a race condition. - // But we inspect it only if we are not a stolen task, in which case we - // know that task assigning to m_parent->result.left_sum has completed. - __TBB_ASSERT(!m_is_right_child || m_parent, "right child is never an orphan"); - bool treat_as_stolen = m_is_right_child && (is_stolen(ed) || &m_body.get()!=m_parent->m_result.m_left_sum); - if( treat_as_stolen ) { - // Invocation is for right child that has been really stolen or needs to be virtually stolen - small_object_allocator alloc{}; - final_sum_type* right_zombie = alloc.new_object(m_body, alloc); - m_parent->m_right_zombie.store(right_zombie, std::memory_order_release); - m_body = *right_zombie; - m_is_final = false; - } - task* next_task = nullptr; - if( (m_is_right_child && !treat_as_stolen) || !m_range.is_divisible() || m_partition.should_execute_range(ed) ) { - if( m_is_final ) - m_body(m_range, final_scan_tag()); - else if( m_sum_slot ) - m_body(m_range, pre_scan_tag()); - if( m_sum_slot ) - *m_sum_slot = &m_body.get(); - __TBB_ASSERT( !m_return_slot, nullptr ); - - next_task = finalize(ed); - } else { - small_object_allocator alloc{}; - auto result = alloc.new_object(m_range,/*m_left_is_final=*/m_is_final, m_parent? &m_parent->m_result: nullptr, m_wait_context, alloc); - - auto new_parent = alloc.new_object(m_return_slot, m_sum_slot, *result, m_parent, m_wait_context, alloc); - m_parent = new_parent; - - // Split off right child - auto& right_child = *alloc.new_object(/*m_return_slot=*/result->m_right, *this, alloc); - - spawn(right_child, *ed.context); - - m_sum_slot = &result->m_left_sum; - m_return_slot = result->m_left; - - __TBB_ASSERT( !m_return_slot, nullptr ); - next_task = this; - } - return next_task; -} - -template -class lambda_scan_body { - Value m_sum_slot; - const Value& identity_element; - const Scan& m_scan; - const ReverseJoin& m_reverse_join; -public: - void operator=(const lambda_scan_body&) = delete; - lambda_scan_body(const lambda_scan_body&) = default; - - lambda_scan_body( const Value& identity, const Scan& scan, const ReverseJoin& rev_join ) - : m_sum_slot(identity) - , identity_element(identity) - , m_scan(scan) - , m_reverse_join(rev_join) {} - - lambda_scan_body( lambda_scan_body& b, split ) - : m_sum_slot(b.identity_element) - , identity_element(b.identity_element) - , m_scan(b.m_scan) - , m_reverse_join(b.m_reverse_join) {} - - template - void operator()( const Range& r, Tag tag ) { - m_sum_slot = tbb::detail::invoke(m_scan, r, m_sum_slot, tag); - } - - void reverse_join( lambda_scan_body& a ) { - m_sum_slot = tbb::detail::invoke(m_reverse_join, a.m_sum_slot, m_sum_slot); - } - - void assign( lambda_scan_body& b ) { - m_sum_slot = b.m_sum_slot; - } - - Value result() const { - return m_sum_slot; - } -}; - -// Requirements on Range concept are documented in blocked_range.h - -/** \page parallel_scan_body_req Requirements on parallel_scan body - Class \c Body implementing the concept of parallel_scan body must define: - - \code Body::Body( Body&, split ); \endcode Splitting constructor. - Split \c b so that \c this and \c b can accumulate separately - - \code Body::~Body(); \endcode Destructor - - \code void Body::operator()( const Range& r, pre_scan_tag ); \endcode - Preprocess iterations for range \c r - - \code void Body::operator()( const Range& r, final_scan_tag ); \endcode - Do final processing for iterations of range \c r - - \code void Body::reverse_join( Body& a ); \endcode - Merge preprocessing state of \c a into \c this, where \c a was - created earlier from \c b by b's splitting constructor -**/ - -/** \name parallel_scan - See also requirements on \ref range_req "Range" and \ref parallel_scan_body_req "parallel_scan Body". **/ -//@{ - -//! Parallel prefix with default partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_scan_body) -void parallel_scan( const Range& range, Body& body ) { - start_scan::run(range,body,__TBB_DEFAULT_PARTITIONER()); -} - -//! Parallel prefix with simple_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_scan_body) -void parallel_scan( const Range& range, Body& body, const simple_partitioner& partitioner ) { - start_scan::run(range, body, partitioner); -} - -//! Parallel prefix with auto_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_scan_body) -void parallel_scan( const Range& range, Body& body, const auto_partitioner& partitioner ) { - start_scan::run(range, body, partitioner); -} - -//! Parallel prefix with default partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_scan_function && - parallel_scan_combine) -Value parallel_scan( const Range& range, const Value& identity, const Scan& scan, const ReverseJoin& reverse_join ) { - lambda_scan_body body(identity, scan, reverse_join); - parallel_scan(range, body, __TBB_DEFAULT_PARTITIONER()); - return body.result(); -} - -//! Parallel prefix with simple_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_scan_function && - parallel_scan_combine) -Value parallel_scan( const Range& range, const Value& identity, const Scan& scan, const ReverseJoin& reverse_join, - const simple_partitioner& partitioner ) { - lambda_scan_body body(identity, scan, reverse_join); - parallel_scan(range, body, partitioner); - return body.result(); -} - -//! Parallel prefix with auto_partitioner -/** @ingroup algorithms **/ -template - __TBB_requires(tbb_range && parallel_scan_function && - parallel_scan_combine) -Value parallel_scan( const Range& range, const Value& identity, const Scan& scan, const ReverseJoin& reverse_join, - const auto_partitioner& partitioner ) { - lambda_scan_body body(identity, scan, reverse_join); - parallel_scan(range, body, partitioner); - return body.result(); -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { - using detail::d1::parallel_scan; - using detail::d1::pre_scan_tag; - using detail::d1::final_scan_tag; -} // namespace v1 - -} // namespace tbb - -#endif /* __TBB_parallel_scan_H */ diff --git a/include/oneapi/tbb/parallel_sort.h b/include/oneapi/tbb/parallel_sort.h deleted file mode 100644 index 4c0578e..0000000 --- a/include/oneapi/tbb/parallel_sort.h +++ /dev/null @@ -1,288 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_parallel_sort_H -#define __TBB_parallel_sort_H - -#include "detail/_namespace_injection.h" -#include "parallel_for.h" -#include "blocked_range.h" -#include "profiling.h" - -#include -#include -#include -#include - -namespace tbb { -namespace detail { -#if __TBB_CPP20_CONCEPTS_PRESENT -inline namespace d0 { - -// TODO: consider using std::strict_weak_order concept -template -concept compare = requires( const std::remove_reference_t& comp, typename std::iterator_traits::reference value ) { - // Forward via iterator_traits::reference - { comp(typename std::iterator_traits::reference(value), - typename std::iterator_traits::reference(value)) } -> std::convertible_to; -}; - -// Inspired by std::__PartiallyOrderedWith exposition only concept -template -concept less_than_comparable = requires( const std::remove_reference_t& lhs, - const std::remove_reference_t& rhs ) { - { lhs < rhs } -> boolean_testable; -}; - -} // namespace d0 -#endif // __TBB_CPP20_CONCEPTS_PRESENT -namespace d1 { - -//! Range used in quicksort to split elements into subranges based on a value. -/** The split operation selects a splitter and places all elements less than or equal - to the value in the first range and the remaining elements in the second range. - @ingroup algorithms */ -template -class quick_sort_range { - std::size_t median_of_three( const RandomAccessIterator& array, std::size_t l, std::size_t m, std::size_t r ) const { - return comp(array[l], array[m]) ? ( comp(array[m], array[r]) ? m : ( comp(array[l], array[r]) ? r : l ) ) - : ( comp(array[r], array[m]) ? m : ( comp(array[r], array[l]) ? r : l ) ); - } - - std::size_t pseudo_median_of_nine( const RandomAccessIterator& array, const quick_sort_range& range ) const { - std::size_t offset = range.size / 8u; - return median_of_three(array, - median_of_three(array, 0 , offset, offset * 2), - median_of_three(array, offset * 3, offset * 4, offset * 5), - median_of_three(array, offset * 6, offset * 7, range.size - 1)); - - } - - std::size_t split_range( quick_sort_range& range ) { - RandomAccessIterator array = range.begin; - RandomAccessIterator first_element = range.begin; - std::size_t m = pseudo_median_of_nine(array, range); - if( m != 0 ) std::iter_swap(array, array + m); - - std::size_t i = 0; - std::size_t j = range.size; - // Partition interval [i + 1,j - 1] with key *first_element. - for(;;) { - __TBB_ASSERT( i < j, nullptr ); - // Loop must terminate since array[l] == *first_element. - do { - --j; - __TBB_ASSERT( i <= j, "bad ordering relation?" ); - } while( comp(*first_element, array[j]) ); - do { - __TBB_ASSERT( i <= j, nullptr ); - if( i == j ) goto partition; - ++i; - } while( comp(array[i], *first_element) ); - if( i == j ) goto partition; - std::iter_swap(array + i, array + j); - } -partition: - // Put the partition key were it belongs - std::iter_swap(array + j, first_element); - // array[l..j) is less or equal to key. - // array(j..r) is greater or equal to key. - // array[j] is equal to key - i = j + 1; - std::size_t new_range_size = range.size - i; - range.size = j; - return new_range_size; - } - -public: - quick_sort_range() = default; - quick_sort_range( const quick_sort_range& ) = default; - void operator=( const quick_sort_range& ) = delete; - - static constexpr std::size_t grainsize = 500; - const Compare& comp; - std::size_t size; - RandomAccessIterator begin; - - quick_sort_range( RandomAccessIterator begin_, std::size_t size_, const Compare& comp_ ) : - comp(comp_), size(size_), begin(begin_) {} - - bool empty() const { return size == 0; } - bool is_divisible() const { return size >= grainsize; } - - quick_sort_range( quick_sort_range& range, split ) - : comp(range.comp) - , size(split_range(range)) - // +1 accounts for the pivot element, which is at its correct place - // already and, therefore, is not included into subranges. - , begin(range.begin + range.size + 1) {} -}; - -//! Body class used to test if elements in a range are presorted -/** @ingroup algorithms */ -template -class quick_sort_pretest_body { - const Compare& comp; - task_group_context& context; - -public: - quick_sort_pretest_body() = default; - quick_sort_pretest_body( const quick_sort_pretest_body& ) = default; - void operator=( const quick_sort_pretest_body& ) = delete; - - quick_sort_pretest_body( const Compare& _comp, task_group_context& _context ) : comp(_comp), context(_context) {} - - void operator()( const blocked_range& range ) const { - RandomAccessIterator my_end = range.end(); - - int i = 0; - //TODO: consider using std::is_sorted() for each 64 iterations (requires performance measurements) - for( RandomAccessIterator k = range.begin(); k != my_end; ++k, ++i ) { - if( i % 64 == 0 && context.is_group_execution_cancelled() ) break; - - // The k - 1 is never out-of-range because the first chunk starts at begin+serial_cutoff+1 - if( comp(*(k), *(k - 1)) ) { - context.cancel_group_execution(); - break; - } - } - } -}; - -//! Body class used to sort elements in a range that is smaller than the grainsize. -/** @ingroup algorithms */ -template -struct quick_sort_body { - void operator()( const quick_sort_range& range ) const { - std::sort(range.begin, range.begin + range.size, range.comp); - } -}; - -//! Method to perform parallel_for based quick sort. -/** @ingroup algorithms */ -template -void do_parallel_quick_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp ) { - parallel_for(quick_sort_range(begin, end - begin, comp), - quick_sort_body(), - auto_partitioner()); -} - -//! Wrapper method to initiate the sort by calling parallel_for. -/** @ingroup algorithms */ -template -void parallel_quick_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp ) { - task_group_context my_context(PARALLEL_SORT); - constexpr int serial_cutoff = 9; - - __TBB_ASSERT( begin + serial_cutoff < end, "min_parallel_size is smaller than serial cutoff?" ); - RandomAccessIterator k = begin; - for( ; k != begin + serial_cutoff; ++k ) { - if( comp(*(k + 1), *k) ) { - do_parallel_quick_sort(begin, end, comp); - return; - } - } - - // Check is input range already sorted - parallel_for(blocked_range(k + 1, end), - quick_sort_pretest_body(comp, my_context), - auto_partitioner(), - my_context); - - if( my_context.is_group_execution_cancelled() ) - do_parallel_quick_sort(begin, end, comp); -} - -/** \page parallel_sort_iter_req Requirements on iterators for parallel_sort - Requirements on the iterator type \c It and its value type \c T for \c parallel_sort: - - - \code void iter_swap( It a, It b ) \endcode Swaps the values of the elements the given - iterators \c a and \c b are pointing to. \c It should be a random access iterator. - - - \code bool Compare::operator()( const T& x, const T& y ) \endcode True if x comes before y; -**/ - -/** \name parallel_sort - See also requirements on \ref parallel_sort_iter_req "iterators for parallel_sort". **/ -//@{ - -#if __TBB_CPP20_CONCEPTS_PRESENT -template -using iter_value_type = typename std::iterator_traits::value_type; - -template -using range_value_type = typename std::iterator_traits>::value_type; -#endif - -//! Sorts the data in [begin,end) using the given comparator -/** The compare function object is used for all comparisons between elements during sorting. - The compare object must define a bool operator() function. - @ingroup algorithms **/ -template - __TBB_requires(std::random_access_iterator && - compare && - std::movable>) -void parallel_sort( RandomAccessIterator begin, RandomAccessIterator end, const Compare& comp ) { - constexpr int min_parallel_size = 500; - if( end > begin ) { - if( end - begin < min_parallel_size ) { - std::sort(begin, end, comp); - } else { - parallel_quick_sort(begin, end, comp); - } - } -} - -//! Sorts the data in [begin,end) with a default comparator \c std::less -/** @ingroup algorithms **/ -template - __TBB_requires(std::random_access_iterator && - less_than_comparable> && - std::movable>) -void parallel_sort( RandomAccessIterator begin, RandomAccessIterator end ) { - parallel_sort(begin, end, std::less::value_type>()); -} - -//! Sorts the data in rng using the given comparator -/** @ingroup algorithms **/ -template - __TBB_requires(container_based_sequence && - compare> && - std::movable>) -void parallel_sort( Range&& rng, const Compare& comp ) { - parallel_sort(std::begin(rng), std::end(rng), comp); -} - -//! Sorts the data in rng with a default comparator \c std::less -/** @ingroup algorithms **/ -template - __TBB_requires(container_based_sequence && - less_than_comparable> && - std::movable>) -void parallel_sort( Range&& rng ) { - parallel_sort(std::begin(rng), std::end(rng)); -} -//@} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { - using detail::d1::parallel_sort; -} // namespace v1 -} // namespace tbb - -#endif /*__TBB_parallel_sort_H*/ diff --git a/include/oneapi/tbb/partitioner.h b/include/oneapi/tbb/partitioner.h deleted file mode 100644 index 98de0d4..0000000 --- a/include/oneapi/tbb/partitioner.h +++ /dev/null @@ -1,681 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_partitioner_H -#define __TBB_partitioner_H - -#ifndef __TBB_INITIAL_CHUNKS -// initial task divisions per thread -#define __TBB_INITIAL_CHUNKS 2 -#endif -#ifndef __TBB_RANGE_POOL_CAPACITY -// maximum number of elements in range pool -#define __TBB_RANGE_POOL_CAPACITY 8 -#endif -#ifndef __TBB_INIT_DEPTH -// initial value for depth of range pool -#define __TBB_INIT_DEPTH 5 -#endif -#ifndef __TBB_DEMAND_DEPTH_ADD -// when imbalance is found range splits this value times more -#define __TBB_DEMAND_DEPTH_ADD 1 -#endif - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_aligned_space.h" -#include "detail/_utils.h" -#include "detail/_template_helpers.h" -#include "detail/_range_common.h" -#include "detail/_task.h" -#include "detail/_small_object_pool.h" - -#include "cache_aligned_allocator.h" -#include "task_group.h" // task_group_context -#include "task_arena.h" - -#include -#include -#include - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) - // Workaround for overzealous compiler warnings - #pragma warning (push) - #pragma warning (disable: 4244) -#endif - -namespace tbb { -namespace detail { - -namespace d1 { -class auto_partitioner; -class simple_partitioner; -class static_partitioner; -class affinity_partitioner; -class affinity_partition_type; -class affinity_partitioner_base; - -inline std::size_t get_initial_auto_partitioner_divisor() { - const std::size_t factor = 4; - return factor * static_cast(max_concurrency()); -} - -//! Defines entry point for affinity partitioner into oneTBB run-time library. -class affinity_partitioner_base: no_copy { - friend class affinity_partitioner; - friend class affinity_partition_type; - //! Array that remembers affinities of tree positions to affinity_id. - /** nullptr if my_size==0. */ - slot_id* my_array; - //! Number of elements in my_array. - std::size_t my_size; - //! Zeros the fields. - affinity_partitioner_base() : my_array(nullptr), my_size(0) {} - //! Deallocates my_array. - ~affinity_partitioner_base() { resize(0); } - //! Resize my_array. - /** Retains values if resulting size is the same. */ - void resize(unsigned factor) { - // Check factor to avoid asking for number of workers while there might be no arena. - unsigned max_threads_in_arena = static_cast(max_concurrency()); - std::size_t new_size = factor ? factor * max_threads_in_arena : 0; - if (new_size != my_size) { - if (my_array) { - r1::cache_aligned_deallocate(my_array); - // Following two assignments must be done here for sake of exception safety. - my_array = nullptr; - my_size = 0; - } - if (new_size) { - my_array = static_cast(r1::cache_aligned_allocate(new_size * sizeof(slot_id))); - std::fill_n(my_array, new_size, no_slot); - my_size = new_size; - } - } - } -}; - -template struct start_for; -template struct start_scan; -template struct start_reduce; -template struct start_deterministic_reduce; - -struct node { - node* my_parent{}; - std::atomic m_ref_count{}; - - node() = default; - node(node* parent, int ref_count) : - my_parent{parent}, m_ref_count{ref_count} { - __TBB_ASSERT(ref_count > 0, "The ref count must be positive"); - } -}; - -struct wait_node : node { - wait_node() : node{ nullptr, 1 } {} - wait_context m_wait{1}; -}; - -//! Join task node that contains shared flag for stealing feedback -struct tree_node : public node { - small_object_allocator m_allocator; - std::atomic m_child_stolen{false}; - - tree_node(node* parent, int ref_count, small_object_allocator& alloc) - : node{parent, ref_count} - , m_allocator{alloc} {} - - void join(task_group_context*) {/*dummy, required only for reduction algorithms*/}; - - template - static void mark_task_stolen(Task &t) { - std::atomic &flag = static_cast(t.my_parent)->m_child_stolen; -#if TBB_USE_PROFILING_TOOLS - // Threading tools respect lock prefix but report false-positive data-race via plain store - flag.exchange(true); -#else - flag.store(true, std::memory_order_relaxed); -#endif // TBB_USE_PROFILING_TOOLS - } - template - static bool is_peer_stolen(Task &t) { - return static_cast(t.my_parent)->m_child_stolen.load(std::memory_order_relaxed); - } -}; - -// Context used to check cancellation state during reduction join process -template -void fold_tree(node* n, const execution_data& ed) { - for (;;) { - __TBB_ASSERT(n, nullptr); - __TBB_ASSERT(n->m_ref_count.load(std::memory_order_relaxed) > 0, "The refcount must be positive."); - call_itt_task_notify(releasing, n); - if (--n->m_ref_count > 0) { - return; - } - node* parent = n->my_parent; - if (!parent) { - break; - }; - - call_itt_task_notify(acquired, n); - TreeNodeType* self = static_cast(n); - self->join(ed.context); - self->m_allocator.delete_object(self, ed); - n = parent; - } - // Finish parallel for execution when the root (last node) is reached - static_cast(n)->m_wait.release(); -} - -//! Depth is a relative depth of recursive division inside a range pool. Relative depth allows -//! infinite absolute depth of the recursion for heavily unbalanced workloads with range represented -//! by a number that cannot fit into machine word. -typedef unsigned char depth_t; - -//! Range pool stores ranges of type T in a circular buffer with MaxCapacity -template -class range_vector { - depth_t my_head; - depth_t my_tail; - depth_t my_size; - depth_t my_depth[MaxCapacity]; // relative depths of stored ranges - tbb::detail::aligned_space my_pool; - -public: - //! initialize via first range in pool - range_vector(const T& elem) : my_head(0), my_tail(0), my_size(1) { - my_depth[0] = 0; - new( static_cast(my_pool.begin()) ) T(elem);//TODO: std::move? - } - ~range_vector() { - while( !empty() ) pop_back(); - } - bool empty() const { return my_size == 0; } - depth_t size() const { return my_size; } - //! Populates range pool via ranges up to max depth or while divisible - //! max_depth starts from 0, e.g. value 2 makes 3 ranges in the pool up to two 1/4 pieces - void split_to_fill(depth_t max_depth) { - while( my_size < MaxCapacity && is_divisible(max_depth) ) { - depth_t prev = my_head; - my_head = (my_head + 1) % MaxCapacity; - new(my_pool.begin()+my_head) T(my_pool.begin()[prev]); // copy TODO: std::move? - my_pool.begin()[prev].~T(); // instead of assignment - new(my_pool.begin()+prev) T(my_pool.begin()[my_head], detail::split()); // do 'inverse' split - my_depth[my_head] = ++my_depth[prev]; - my_size++; - } - } - void pop_back() { - __TBB_ASSERT(my_size > 0, "range_vector::pop_back() with empty size"); - my_pool.begin()[my_head].~T(); - my_size--; - my_head = (my_head + MaxCapacity - 1) % MaxCapacity; - } - void pop_front() { - __TBB_ASSERT(my_size > 0, "range_vector::pop_front() with empty size"); - my_pool.begin()[my_tail].~T(); - my_size--; - my_tail = (my_tail + 1) % MaxCapacity; - } - T& back() { - __TBB_ASSERT(my_size > 0, "range_vector::back() with empty size"); - return my_pool.begin()[my_head]; - } - T& front() { - __TBB_ASSERT(my_size > 0, "range_vector::front() with empty size"); - return my_pool.begin()[my_tail]; - } - //! similarly to front(), returns depth of the first range in the pool - depth_t front_depth() { - __TBB_ASSERT(my_size > 0, "range_vector::front_depth() with empty size"); - return my_depth[my_tail]; - } - depth_t back_depth() { - __TBB_ASSERT(my_size > 0, "range_vector::back_depth() with empty size"); - return my_depth[my_head]; - } - bool is_divisible(depth_t max_depth) { - return back_depth() < max_depth && back().is_divisible(); - } -}; - -//! Provides default methods for partition objects and common algorithm blocks. -template -struct partition_type_base { - typedef detail::split split_type; - // decision makers - void note_affinity( slot_id ) {} - template - bool check_being_stolen(Task&, const execution_data&) { return false; } // part of old should_execute_range() - template split_type get_split() { return split(); } - Partition& self() { return *static_cast(this); } // CRTP helper - - template - void work_balance(StartType &start, Range &range, const execution_data&) { - start.run_body( range ); // static partitioner goes here - } - - template - void execute(StartType &start, Range &range, execution_data& ed) { - // The algorithm in a few words ([]-denotes calls to decision methods of partitioner): - // [If this task is stolen, adjust depth and divisions if necessary, set flag]. - // If range is divisible { - // Spread the work while [initial divisions left]; - // Create trap task [if necessary]; - // } - // If not divisible or [max depth is reached], execute, else do the range pool part - if ( range.is_divisible() ) { - if ( self().is_divisible() ) { - do { // split until is divisible - typename Partition::split_type split_obj = self().template get_split(); - start.offer_work( split_obj, ed ); - } while ( range.is_divisible() && self().is_divisible() ); - } - } - self().work_balance(start, range, ed); - } -}; - -//! Provides default splitting strategy for partition objects. -template -struct adaptive_mode : partition_type_base { - typedef Partition my_partition; - std::size_t my_divisor; - // For affinity_partitioner, my_divisor indicates the number of affinity array indices the task reserves. - // A task which has only one index must produce the right split without reserved index in order to avoid - // it to be overwritten in note_affinity() of the created (right) task. - // I.e. a task created deeper than the affinity array can remember must not save its affinity (LIFO order) - static const unsigned factor = 1; - adaptive_mode() : my_divisor(get_initial_auto_partitioner_divisor() / 4 * my_partition::factor) {} - adaptive_mode(adaptive_mode &src, split) : my_divisor(do_split(src, split())) {} - adaptive_mode(adaptive_mode&, const proportional_split&) : my_divisor(0) - { - // left blank as my_divisor gets overridden in the successors' constructors - } - /*! Override do_split methods in order to specify splitting strategy */ - std::size_t do_split(adaptive_mode &src, split) { - return src.my_divisor /= 2u; - } -}; - - -//! Provides proportional splitting strategy for partition objects -template -struct proportional_mode : adaptive_mode { - typedef Partition my_partition; - using partition_type_base::self; // CRTP helper to get access to derived classes - - proportional_mode() : adaptive_mode() {} - proportional_mode(proportional_mode &src, split) : adaptive_mode(src, split()) {} - proportional_mode(proportional_mode &src, const proportional_split& split_obj) - : adaptive_mode(src, split_obj) - { - self().my_divisor = do_split(src, split_obj); - } - std::size_t do_split(proportional_mode &src, const proportional_split& split_obj) { - std::size_t portion = split_obj.right() * my_partition::factor; - portion = (portion + my_partition::factor/2) & (0ul - my_partition::factor); - src.my_divisor -= portion; - return portion; - } - bool is_divisible() { // part of old should_execute_range() - return self().my_divisor > my_partition::factor; - } - template - proportional_split get_split() { - // Create the proportion from partitioner internal resources (threads) that would be used: - // - into proportional_mode constructor to split the partitioner - // - if Range supports the proportional_split constructor it would use proposed proportion, - // otherwise, the tbb::proportional_split object will be implicitly (for Range implementor) - // casted to tbb::split - - std::size_t n = self().my_divisor / my_partition::factor; - std::size_t right = n / 2; - std::size_t left = n - right; - return proportional_split(left, right); - } -}; - -static std::size_t get_initial_partition_head() { - int current_index = tbb::this_task_arena::current_thread_index(); - if (current_index == tbb::task_arena::not_initialized) - current_index = 0; - return size_t(current_index); -} - -//! Provides default linear indexing of partitioner's sequence -template -struct linear_affinity_mode : proportional_mode { - std::size_t my_head; - std::size_t my_max_affinity; - using proportional_mode::self; - linear_affinity_mode() : proportional_mode(), my_head(get_initial_partition_head()), - my_max_affinity(self().my_divisor) {} - linear_affinity_mode(linear_affinity_mode &src, split) : proportional_mode(src, split()) - , my_head((src.my_head + src.my_divisor) % src.my_max_affinity), my_max_affinity(src.my_max_affinity) {} - linear_affinity_mode(linear_affinity_mode &src, const proportional_split& split_obj) : proportional_mode(src, split_obj) - , my_head((src.my_head + src.my_divisor) % src.my_max_affinity), my_max_affinity(src.my_max_affinity) {} - void spawn_task(task& t, task_group_context& ctx) { - if (self().my_divisor) { - spawn(t, ctx, slot_id(my_head)); - } else { - spawn(t, ctx); - } - } -}; - -static bool is_stolen_task(const execution_data& ed) { - return execution_slot(ed) != original_slot(ed); -} - -/*! Determine work-balance phase implementing splitting & stealing actions */ -template -struct dynamic_grainsize_mode : Mode { - using Mode::self; - enum { - begin = 0, - run, - pass - } my_delay; - depth_t my_max_depth; - static const unsigned range_pool_size = __TBB_RANGE_POOL_CAPACITY; - dynamic_grainsize_mode(): Mode() - , my_delay(begin) - , my_max_depth(__TBB_INIT_DEPTH) {} - dynamic_grainsize_mode(dynamic_grainsize_mode& p, split) - : Mode(p, split()) - , my_delay(pass) - , my_max_depth(p.my_max_depth) {} - dynamic_grainsize_mode(dynamic_grainsize_mode& p, const proportional_split& split_obj) - : Mode(p, split_obj) - , my_delay(begin) - , my_max_depth(p.my_max_depth) {} - template - bool check_being_stolen(Task &t, const execution_data& ed) { // part of old should_execute_range() - if( !(self().my_divisor / Mode::my_partition::factor) ) { // if not from the top P tasks of binary tree - self().my_divisor = 1; // TODO: replace by on-stack flag (partition_state's member)? - if( is_stolen_task(ed) && t.my_parent->m_ref_count >= 2 ) { // runs concurrently with the left task -#if __TBB_USE_OPTIONAL_RTTI - // RTTI is available, check whether the cast is valid - // TODO: TBB_REVAMP_TODO __TBB_ASSERT(dynamic_cast(t.m_parent), 0); - // correctness of the cast relies on avoiding the root task for which: - // - initial value of my_divisor != 0 (protected by separate assertion) - // - is_stolen_task() always returns false for the root task. -#endif - tree_node::mark_task_stolen(t); - if( !my_max_depth ) my_max_depth++; - my_max_depth += __TBB_DEMAND_DEPTH_ADD; - return true; - } - } - return false; - } - depth_t max_depth() { return my_max_depth; } - void align_depth(depth_t base) { - __TBB_ASSERT(base <= my_max_depth, nullptr); - my_max_depth -= base; - } - template - void work_balance(StartType &start, Range &range, execution_data& ed) { - if( !range.is_divisible() || !self().max_depth() ) { - start.run_body( range ); - } - else { // do range pool - range_vector range_pool(range); - do { - range_pool.split_to_fill(self().max_depth()); // fill range pool - if( self().check_for_demand( start ) ) { - if( range_pool.size() > 1 ) { - start.offer_work( range_pool.front(), range_pool.front_depth(), ed ); - range_pool.pop_front(); - continue; - } - if( range_pool.is_divisible(self().max_depth()) ) // was not enough depth to fork a task - continue; // note: next split_to_fill() should split range at least once - } - start.run_body( range_pool.back() ); - range_pool.pop_back(); - } while( !range_pool.empty() && !ed.context->is_group_execution_cancelled() ); - } - } - template - bool check_for_demand(Task& t) { - if ( pass == my_delay ) { - if ( self().my_divisor > 1 ) // produce affinitized tasks while they have slot in array - return true; // do not do my_max_depth++ here, but be sure range_pool is splittable once more - else if ( self().my_divisor && my_max_depth ) { // make balancing task - self().my_divisor = 0; // once for each task; depth will be decreased in align_depth() - return true; - } - else if ( tree_node::is_peer_stolen(t) ) { - my_max_depth += __TBB_DEMAND_DEPTH_ADD; - return true; - } - } else if( begin == my_delay ) { - my_delay = pass; - } - return false; - } -}; - -class auto_partition_type: public dynamic_grainsize_mode > { -public: - auto_partition_type( const auto_partitioner& ) { - my_divisor *= __TBB_INITIAL_CHUNKS; - } - auto_partition_type( auto_partition_type& src, split) - : dynamic_grainsize_mode >(src, split()) {} - bool is_divisible() { // part of old should_execute_range() - if( my_divisor > 1 ) return true; - if( my_divisor && my_max_depth ) { // can split the task. TODO: on-stack flag instead - // keep same fragmentation while splitting for the local task pool - my_max_depth--; - my_divisor = 0; // decrease max_depth once per task - return true; - } else return false; - } - template - bool check_for_demand(Task& t) { - if (tree_node::is_peer_stolen(t)) { - my_max_depth += __TBB_DEMAND_DEPTH_ADD; - return true; - } else return false; - } - void spawn_task(task& t, task_group_context& ctx) { - spawn(t, ctx); - } -}; - -class simple_partition_type: public partition_type_base { -public: - simple_partition_type( const simple_partitioner& ) {} - simple_partition_type( const simple_partition_type&, split ) {} - //! simplified algorithm - template - void execute(StartType &start, Range &range, execution_data& ed) { - split_type split_obj = split(); // start.offer_work accepts split_type as reference - while( range.is_divisible() ) - start.offer_work( split_obj, ed ); - start.run_body( range ); - } - void spawn_task(task& t, task_group_context& ctx) { - spawn(t, ctx); - } -}; - -class static_partition_type : public linear_affinity_mode { -public: - typedef detail::proportional_split split_type; - static_partition_type( const static_partitioner& ) {} - static_partition_type( static_partition_type& p, const proportional_split& split_obj ) - : linear_affinity_mode(p, split_obj) {} -}; - -class affinity_partition_type : public dynamic_grainsize_mode > { - static const unsigned factor_power = 4; // TODO: get a unified formula based on number of computing units - slot_id* my_array; -public: - static const unsigned factor = 1 << factor_power; // number of slots in affinity array per task - typedef detail::proportional_split split_type; - affinity_partition_type( affinity_partitioner_base& ap ) { - __TBB_ASSERT( (factor&(factor-1))==0, "factor must be power of two" ); - ap.resize(factor); - my_array = ap.my_array; - my_max_depth = factor_power + 1; - __TBB_ASSERT( my_max_depth < __TBB_RANGE_POOL_CAPACITY, nullptr ); - } - affinity_partition_type(affinity_partition_type& p, split) - : dynamic_grainsize_mode >(p, split()) - , my_array(p.my_array) {} - affinity_partition_type(affinity_partition_type& p, const proportional_split& split_obj) - : dynamic_grainsize_mode >(p, split_obj) - , my_array(p.my_array) {} - void note_affinity(slot_id id) { - if( my_divisor ) - my_array[my_head] = id; - } - void spawn_task(task& t, task_group_context& ctx) { - if (my_divisor) { - if (!my_array[my_head]) { - // TODO: consider new ideas with my_array for both affinity and static partitioner's, then code reuse - spawn(t, ctx, slot_id(my_head / factor)); - } else { - spawn(t, ctx, my_array[my_head]); - } - } else { - spawn(t, ctx); - } - } -}; - -//! A simple partitioner -/** Divides the range until the range is not divisible. - @ingroup algorithms */ -class simple_partitioner { -public: - simple_partitioner() {} -private: - template friend struct start_for; - template friend struct start_reduce; - template friend struct start_deterministic_reduce; - template friend struct start_scan; - // new implementation just extends existing interface - typedef simple_partition_type task_partition_type; - // TODO: consider to make split_type public - typedef simple_partition_type::split_type split_type; - - // for parallel_scan only - class partition_type { - public: - bool should_execute_range(const execution_data& ) {return false;} - partition_type( const simple_partitioner& ) {} - partition_type( const partition_type&, split ) {} - }; -}; - -//! An auto partitioner -/** The range is initial divided into several large chunks. - Chunks are further subdivided into smaller pieces if demand detected and they are divisible. - @ingroup algorithms */ -class auto_partitioner { -public: - auto_partitioner() {} - -private: - template friend struct start_for; - template friend struct start_reduce; - template friend struct start_deterministic_reduce; - template friend struct start_scan; - // new implementation just extends existing interface - typedef auto_partition_type task_partition_type; - // TODO: consider to make split_type public - typedef auto_partition_type::split_type split_type; - - //! Backward-compatible partition for auto and affinity partition objects. - class partition_type { - size_t num_chunks; - static const size_t VICTIM_CHUNKS = 4; - public: - bool should_execute_range(const execution_data& ed) { - if( num_chunks friend struct start_for; - template friend struct start_reduce; - template friend struct start_deterministic_reduce; - template friend struct start_scan; - // new implementation just extends existing interface - typedef static_partition_type task_partition_type; - // TODO: consider to make split_type public - typedef static_partition_type::split_type split_type; -}; - -//! An affinity partitioner -class affinity_partitioner : affinity_partitioner_base { -public: - affinity_partitioner() {} - -private: - template friend struct start_for; - template friend struct start_reduce; - template friend struct start_deterministic_reduce; - template friend struct start_scan; - // new implementation just extends existing interface - typedef affinity_partition_type task_partition_type; - // TODO: consider to make split_type public - typedef affinity_partition_type::split_type split_type; -}; - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -// Partitioners -using detail::d1::auto_partitioner; -using detail::d1::simple_partitioner; -using detail::d1::static_partitioner; -using detail::d1::affinity_partitioner; -// Split types -using detail::split; -using detail::proportional_split; -} // namespace v1 - -} // namespace tbb - -#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) - #pragma warning (pop) -#endif // warning 4244 is back - -#undef __TBB_INITIAL_CHUNKS -#undef __TBB_RANGE_POOL_CAPACITY -#undef __TBB_INIT_DEPTH - -#endif /* __TBB_partitioner_H */ diff --git a/include/oneapi/tbb/profiling.h b/include/oneapi/tbb/profiling.h deleted file mode 100644 index 412b5a3..0000000 --- a/include/oneapi/tbb/profiling.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_profiling_H -#define __TBB_profiling_H - -#include "detail/_config.h" -#include - -#include - -namespace tbb { -namespace detail { -inline namespace d0 { - // include list of index names - #define TBB_STRING_RESOURCE(index_name,str) index_name, - enum string_resource_index : std::uintptr_t { - #include "detail/_string_resource.h" - NUM_STRINGS - }; - #undef TBB_STRING_RESOURCE - - enum itt_relation - { - __itt_relation_is_unknown = 0, - __itt_relation_is_dependent_on, /**< "A is dependent on B" means that A cannot start until B completes */ - __itt_relation_is_sibling_of, /**< "A is sibling of B" means that A and B were created as a group */ - __itt_relation_is_parent_of, /**< "A is parent of B" means that A created B */ - __itt_relation_is_continuation_of, /**< "A is continuation of B" means that A assumes the dependencies of B */ - __itt_relation_is_child_of, /**< "A is child of B" means that A was created by B (inverse of is_parent_of) */ - __itt_relation_is_continued_by, /**< "A is continued by B" means that B assumes the dependencies of A (inverse of is_continuation_of) */ - __itt_relation_is_predecessor_to /**< "A is predecessor to B" means that B cannot start until A completes (inverse of is_dependent_on) */ - }; - -//! Unicode support -#if (_WIN32||_WIN64) - //! Unicode character type. Always wchar_t on Windows. - using tchar = wchar_t; -#else /* !WIN */ - using tchar = char; -#endif /* !WIN */ - -} // namespace d0 -} // namespace detail -} // namespace tbb - -#include -#if _WIN32||_WIN64 -#include /* mbstowcs_s */ -#endif -// Need these to work regardless of tools support -namespace tbb { -namespace detail { -namespace d1 { - enum notify_type {prepare=0, cancel, acquired, releasing, destroy}; - enum itt_domain_enum { ITT_DOMAIN_FLOW=0, ITT_DOMAIN_MAIN=1, ITT_DOMAIN_ALGO=2, ITT_NUM_DOMAINS }; -} // namespace d1 - -namespace r1 { - TBB_EXPORT void __TBB_EXPORTED_FUNC call_itt_notify(int t, void* ptr); - TBB_EXPORT void __TBB_EXPORTED_FUNC create_itt_sync(void* ptr, const tchar* objtype, const tchar* objname); - TBB_EXPORT void __TBB_EXPORTED_FUNC itt_make_task_group(d1::itt_domain_enum domain, void* group, unsigned long long group_extra, - void* parent, unsigned long long parent_extra, string_resource_index name_index); - TBB_EXPORT void __TBB_EXPORTED_FUNC itt_task_begin(d1::itt_domain_enum domain, void* task, unsigned long long task_extra, - void* parent, unsigned long long parent_extra, string_resource_index name_index); - TBB_EXPORT void __TBB_EXPORTED_FUNC itt_task_end(d1::itt_domain_enum domain); - TBB_EXPORT void __TBB_EXPORTED_FUNC itt_set_sync_name(void* obj, const tchar* name); - TBB_EXPORT void __TBB_EXPORTED_FUNC itt_metadata_str_add(d1::itt_domain_enum domain, void* addr, unsigned long long addr_extra, - string_resource_index key, const char* value); - TBB_EXPORT void __TBB_EXPORTED_FUNC itt_metadata_ptr_add(d1::itt_domain_enum domain, void* addr, unsigned long long addr_extra, - string_resource_index key, void* value); - TBB_EXPORT void __TBB_EXPORTED_FUNC itt_relation_add(d1::itt_domain_enum domain, void* addr0, unsigned long long addr0_extra, - itt_relation relation, void* addr1, unsigned long long addr1_extra); - TBB_EXPORT void __TBB_EXPORTED_FUNC itt_region_begin(d1::itt_domain_enum domain, void* region, unsigned long long region_extra, - void* parent, unsigned long long parent_extra, string_resource_index /* name_index */); - TBB_EXPORT void __TBB_EXPORTED_FUNC itt_region_end(d1::itt_domain_enum domain, void* region, unsigned long long region_extra); -} // namespace r1 - -namespace d1 { -#if TBB_USE_PROFILING_TOOLS && (_WIN32||_WIN64) - inline std::size_t multibyte_to_widechar(wchar_t* wcs, const char* mbs, std::size_t bufsize) { - std::size_t len; - mbstowcs_s(&len, wcs, bufsize, mbs, _TRUNCATE); - return len; // mbstowcs_s counts null terminator - } -#endif - -#if TBB_USE_PROFILING_TOOLS - inline void create_itt_sync(void *ptr, const char *objtype, const char *objname) { -#if (_WIN32||_WIN64) - std::size_t len_type = multibyte_to_widechar(nullptr, objtype, 0); - wchar_t *type = new wchar_t[len_type]; - multibyte_to_widechar(type, objtype, len_type); - std::size_t len_name = multibyte_to_widechar(nullptr, objname, 0); - wchar_t *name = new wchar_t[len_name]; - multibyte_to_widechar(name, objname, len_name); -#else // WIN - const char *type = objtype; - const char *name = objname; -#endif - r1::create_itt_sync(ptr, type, name); - -#if (_WIN32||_WIN64) - delete[] type; - delete[] name; -#endif // WIN - } - -// Distinguish notifications on task for reducing overheads -#if TBB_USE_PROFILING_TOOLS == 2 - inline void call_itt_task_notify(d1::notify_type t, void *ptr) { - r1::call_itt_notify(static_cast(t), ptr); - } -#else - inline void call_itt_task_notify(d1::notify_type, void *) {} -#endif // TBB_USE_PROFILING_TOOLS - - inline void call_itt_notify(d1::notify_type t, void *ptr) { - r1::call_itt_notify(static_cast(t), ptr); - } - -#if (_WIN32||_WIN64) && !__MINGW32__ - inline void itt_set_sync_name(void* obj, const wchar_t* name) { - r1::itt_set_sync_name(obj, name); - } - inline void itt_set_sync_name(void* obj, const char* name) { - std::size_t len_name = multibyte_to_widechar(nullptr, name, 0); - wchar_t *obj_name = new wchar_t[len_name]; - multibyte_to_widechar(obj_name, name, len_name); - r1::itt_set_sync_name(obj, obj_name); - delete[] obj_name; - } -#else - inline void itt_set_sync_name( void* obj, const char* name) { - r1::itt_set_sync_name(obj, name); - } -#endif //WIN - - inline void itt_make_task_group(itt_domain_enum domain, void* group, unsigned long long group_extra, - void* parent, unsigned long long parent_extra, string_resource_index name_index) { - r1::itt_make_task_group(domain, group, group_extra, parent, parent_extra, name_index); - } - - inline void itt_metadata_str_add( itt_domain_enum domain, void *addr, unsigned long long addr_extra, - string_resource_index key, const char *value ) { - r1::itt_metadata_str_add( domain, addr, addr_extra, key, value ); - } - - inline void register_node_addr(itt_domain_enum domain, void *addr, unsigned long long addr_extra, - string_resource_index key, void *value) { - r1::itt_metadata_ptr_add(domain, addr, addr_extra, key, value); - } - - inline void itt_relation_add( itt_domain_enum domain, void *addr0, unsigned long long addr0_extra, - itt_relation relation, void *addr1, unsigned long long addr1_extra ) { - r1::itt_relation_add( domain, addr0, addr0_extra, relation, addr1, addr1_extra ); - } - - inline void itt_task_begin( itt_domain_enum domain, void *task, unsigned long long task_extra, - void *parent, unsigned long long parent_extra, string_resource_index name_index ) { - r1::itt_task_begin( domain, task, task_extra, parent, parent_extra, name_index ); - } - - inline void itt_task_end( itt_domain_enum domain ) { - r1::itt_task_end( domain ); - } - - inline void itt_region_begin( itt_domain_enum domain, void *region, unsigned long long region_extra, - void *parent, unsigned long long parent_extra, string_resource_index name_index ) { - r1::itt_region_begin( domain, region, region_extra, parent, parent_extra, name_index ); - } - - inline void itt_region_end( itt_domain_enum domain, void *region, unsigned long long region_extra ) { - r1::itt_region_end( domain, region, region_extra ); - } -#else - inline void create_itt_sync(void* /*ptr*/, const char* /*objtype*/, const char* /*objname*/) {} - - inline void call_itt_notify(notify_type /*t*/, void* /*ptr*/) {} - - inline void call_itt_task_notify(notify_type /*t*/, void* /*ptr*/) {} -#endif // TBB_USE_PROFILING_TOOLS - -#if TBB_USE_PROFILING_TOOLS && !(TBB_USE_PROFILING_TOOLS == 2) -class event { -/** This class supports user event traces through itt. - Common use-case is tagging data flow graph tasks (data-id) - and visualization by Intel Advisor Flow Graph Analyzer (FGA) **/ -// TODO: Replace implementation by itt user event api. - - const std::string my_name; - - static void emit_trace(const std::string &input) { - itt_metadata_str_add( ITT_DOMAIN_FLOW, nullptr, FLOW_NULL, USER_EVENT, ( "FGA::DATAID::" + input ).c_str() ); - } - -public: - event(const std::string &input) - : my_name( input ) - { } - - void emit() { - emit_trace(my_name); - } - - static void emit(const std::string &description) { - emit_trace(description); - } - -}; -#else // TBB_USE_PROFILING_TOOLS && !(TBB_USE_PROFILING_TOOLS == 2) -// Using empty struct if user event tracing is disabled: -struct event { - event(const std::string &) { } - - void emit() { } - - static void emit(const std::string &) { } -}; -#endif // TBB_USE_PROFILING_TOOLS && !(TBB_USE_PROFILING_TOOLS == 2) -} // namespace d1 -} // namespace detail - -namespace profiling { - using detail::d1::event; -} -} // namespace tbb - - -#endif /* __TBB_profiling_H */ diff --git a/include/oneapi/tbb/queuing_mutex.h b/include/oneapi/tbb/queuing_mutex.h deleted file mode 100644 index f7ee5a9..0000000 --- a/include/oneapi/tbb/queuing_mutex.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_queuing_mutex_H -#define __TBB_queuing_mutex_H - -#include "detail/_namespace_injection.h" -#include "detail/_assert.h" -#include "detail/_utils.h" -#include "detail/_mutex_common.h" - -#include "profiling.h" - -#include - -namespace tbb { -namespace detail { -namespace d1 { - -//! Queuing mutex with local-only spinning. -/** @ingroup synchronization */ -class queuing_mutex { -public: - //! Construct unacquired mutex. - queuing_mutex() noexcept { - create_itt_sync(this, "tbb::queuing_mutex", ""); - }; - - queuing_mutex(const queuing_mutex&) = delete; - queuing_mutex& operator=(const queuing_mutex&) = delete; - - //! The scoped locking pattern - /** It helps to avoid the common problem of forgetting to release lock. - It also nicely provides the "node" for queuing locks. */ - class scoped_lock { - //! Reset fields to mean "no lock held". - void reset() { - m_mutex = nullptr; - } - - public: - //! Construct lock that has not acquired a mutex. - /** Equivalent to zero-initialization of *this. */ - scoped_lock() = default; - - //! Acquire lock on given mutex. - scoped_lock(queuing_mutex& m) { - acquire(m); - } - - //! Release lock (if lock is held). - ~scoped_lock() { - if (m_mutex) release(); - } - - //! No Copy - scoped_lock( const scoped_lock& ) = delete; - scoped_lock& operator=( const scoped_lock& ) = delete; - - //! Acquire lock on given mutex. - void acquire( queuing_mutex& m ) { - __TBB_ASSERT(!m_mutex, "scoped_lock is already holding a mutex"); - - // Must set all fields before the exchange, because once the - // exchange executes, *this becomes accessible to other threads. - m_mutex = &m; - m_next.store(nullptr, std::memory_order_relaxed); - m_going.store(0U, std::memory_order_relaxed); - - // x86 compare exchange operation always has a strong fence - // "sending" the fields initialized above to other processors. - scoped_lock* pred = m.q_tail.exchange(this); - if (pred) { - call_itt_notify(prepare, &m); - __TBB_ASSERT(pred->m_next.load(std::memory_order_relaxed) == nullptr, "the predecessor has another successor!"); - - pred->m_next.store(this, std::memory_order_release); - spin_wait_while_eq(m_going, 0U); - } - call_itt_notify(acquired, &m); - - } - - //! Acquire lock on given mutex if free (i.e. non-blocking) - bool try_acquire( queuing_mutex& m ) { - __TBB_ASSERT(!m_mutex, "scoped_lock is already holding a mutex"); - - // Must set all fields before the compare_exchange_strong, because once the - // compare_exchange_strong executes, *this becomes accessible to other threads. - m_next.store(nullptr, std::memory_order_relaxed); - m_going.store(0U, std::memory_order_relaxed); - - scoped_lock* expected = nullptr; - // The compare_exchange_strong must have release semantics, because we are - // "sending" the fields initialized above to other processors. - // x86 compare exchange operation always has a strong fence - if (!m.q_tail.compare_exchange_strong(expected, this, std::memory_order_acq_rel)) - return false; - - m_mutex = &m; - - call_itt_notify(acquired, &m); - return true; - } - - //! Release lock. - void release() - { - __TBB_ASSERT(this->m_mutex, "no lock acquired"); - - call_itt_notify(releasing, this->m_mutex); - - if (m_next.load(std::memory_order_relaxed) == nullptr) { - scoped_lock* expected = this; - if (m_mutex->q_tail.compare_exchange_strong(expected, nullptr)) { - // this was the only item in the queue, and the queue is now empty. - reset(); - return; - } - // Someone in the queue - spin_wait_while_eq(m_next, nullptr); - } - m_next.load(std::memory_order_acquire)->m_going.store(1U, std::memory_order_release); - - reset(); - } - - private: - //! The pointer to the mutex owned, or nullptr if not holding a mutex. - queuing_mutex* m_mutex{nullptr}; - - //! The pointer to the next competitor for a mutex - std::atomic m_next{nullptr}; - - //! The local spin-wait variable - /** Inverted (0 - blocked, 1 - acquired the mutex) for the sake of - zero-initialization. Defining it as an entire word instead of - a byte seems to help performance slightly. */ - std::atomic m_going{0U}; - }; - - // Mutex traits - static constexpr bool is_rw_mutex = false; - static constexpr bool is_recursive_mutex = false; - static constexpr bool is_fair_mutex = true; - -private: - //! The last competitor requesting the lock - std::atomic q_tail{nullptr}; - -}; - -#if TBB_USE_PROFILING_TOOLS -inline void set_name(queuing_mutex& obj, const char* name) { - itt_set_sync_name(&obj, name); -} -#if (_WIN32||_WIN64) -inline void set_name(queuing_mutex& obj, const wchar_t* name) { - itt_set_sync_name(&obj, name); -} -#endif //WIN -#else -inline void set_name(queuing_mutex&, const char*) {} -#if (_WIN32||_WIN64) -inline void set_name(queuing_mutex&, const wchar_t*) {} -#endif //WIN -#endif -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::queuing_mutex; -} // namespace v1 -namespace profiling { - using detail::d1::set_name; -} -} // namespace tbb - -#endif /* __TBB_queuing_mutex_H */ diff --git a/include/oneapi/tbb/queuing_rw_mutex.h b/include/oneapi/tbb/queuing_rw_mutex.h deleted file mode 100644 index f8325df..0000000 --- a/include/oneapi/tbb/queuing_rw_mutex.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_queuing_rw_mutex_H -#define __TBB_queuing_rw_mutex_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_assert.h" -#include "detail/_mutex_common.h" - -#include "profiling.h" - -#include -#include - -namespace tbb { -namespace detail { -namespace r1 { -struct queuing_rw_mutex_impl; -} -namespace d1 { - -//! Queuing reader-writer mutex with local-only spinning. -/** Adapted from Krieger, Stumm, et al. pseudocode at - https://www.researchgate.net/publication/221083709_A_Fair_Fast_Scalable_Reader-Writer_Lock - @ingroup synchronization */ -class queuing_rw_mutex { - friend r1::queuing_rw_mutex_impl; -public: - //! Construct unacquired mutex. - queuing_rw_mutex() noexcept { - create_itt_sync(this, "tbb::queuing_rw_mutex", ""); - } - - //! Destructor asserts if the mutex is acquired, i.e. q_tail is non-null - ~queuing_rw_mutex() { - __TBB_ASSERT(q_tail.load(std::memory_order_relaxed) == nullptr, "destruction of an acquired mutex"); - } - - //! No Copy - queuing_rw_mutex(const queuing_rw_mutex&) = delete; - queuing_rw_mutex& operator=(const queuing_rw_mutex&) = delete; - - //! The scoped locking pattern - /** It helps to avoid the common problem of forgetting to release lock. - It also nicely provides the "node" for queuing locks. */ - class scoped_lock { - friend r1::queuing_rw_mutex_impl; - //! Initialize fields to mean "no lock held". - void initialize() { - my_mutex = nullptr; - my_internal_lock.store(0, std::memory_order_relaxed); - my_going.store(0, std::memory_order_relaxed); -#if TBB_USE_ASSERT - my_state = 0xFF; // Set to invalid state - my_next.store(reinterpret_cast(reinterpret_cast(-1)), std::memory_order_relaxed); - my_prev.store(reinterpret_cast(reinterpret_cast(-1)), std::memory_order_relaxed); -#endif /* TBB_USE_ASSERT */ - } - - public: - //! Construct lock that has not acquired a mutex. - /** Equivalent to zero-initialization of *this. */ - scoped_lock() {initialize();} - - //! Acquire lock on given mutex. - scoped_lock( queuing_rw_mutex& m, bool write=true ) { - initialize(); - acquire(m,write); - } - - //! Release lock (if lock is held). - ~scoped_lock() { - if( my_mutex ) release(); - } - - //! No Copy - scoped_lock(const scoped_lock&) = delete; - scoped_lock& operator=(const scoped_lock&) = delete; - - //! Acquire lock on given mutex. - void acquire( queuing_rw_mutex& m, bool write=true ); - - //! Acquire lock on given mutex if free (i.e. non-blocking) - bool try_acquire( queuing_rw_mutex& m, bool write=true ); - - //! Release lock. - void release(); - - //! Upgrade reader to become a writer. - /** Returns whether the upgrade happened without releasing and re-acquiring the lock */ - bool upgrade_to_writer(); - - //! Downgrade writer to become a reader. - bool downgrade_to_reader(); - - bool is_writer() const; - - private: - //! The pointer to the mutex owned, or nullptr if not holding a mutex. - queuing_rw_mutex* my_mutex; - - //! The 'pointer' to the previous and next competitors for a mutex - std::atomic my_prev; - std::atomic my_next; - - using state_t = unsigned char ; - - //! State of the request: reader, writer, active reader, other service states - std::atomic my_state; - - //! The local spin-wait variable - /** Corresponds to "spin" in the pseudocode but inverted for the sake of zero-initialization */ - std::atomic my_going; - - //! A tiny internal lock - std::atomic my_internal_lock; - }; - - // Mutex traits - static constexpr bool is_rw_mutex = true; - static constexpr bool is_recursive_mutex = false; - static constexpr bool is_fair_mutex = true; - -private: - //! The last competitor requesting the lock - std::atomic q_tail{nullptr}; -}; -#if TBB_USE_PROFILING_TOOLS -inline void set_name(queuing_rw_mutex& obj, const char* name) { - itt_set_sync_name(&obj, name); -} -#if (_WIN32||_WIN64) -inline void set_name(queuing_rw_mutex& obj, const wchar_t* name) { - itt_set_sync_name(&obj, name); -} -#endif //WIN -#else -inline void set_name(queuing_rw_mutex&, const char*) {} -#if (_WIN32||_WIN64) -inline void set_name(queuing_rw_mutex&, const wchar_t*) {} -#endif //WIN -#endif -} // namespace d1 - -namespace r1 { -TBB_EXPORT void acquire(d1::queuing_rw_mutex&, d1::queuing_rw_mutex::scoped_lock&, bool); -TBB_EXPORT bool try_acquire(d1::queuing_rw_mutex&, d1::queuing_rw_mutex::scoped_lock&, bool); -TBB_EXPORT void release(d1::queuing_rw_mutex::scoped_lock&); -TBB_EXPORT bool upgrade_to_writer(d1::queuing_rw_mutex::scoped_lock&); -TBB_EXPORT bool downgrade_to_reader(d1::queuing_rw_mutex::scoped_lock&); -TBB_EXPORT bool is_writer(const d1::queuing_rw_mutex::scoped_lock&); -} // namespace r1 - -namespace d1 { - - -inline void queuing_rw_mutex::scoped_lock::acquire(queuing_rw_mutex& m,bool write) { - r1::acquire(m, *this, write); -} - -inline bool queuing_rw_mutex::scoped_lock::try_acquire(queuing_rw_mutex& m, bool write) { - return r1::try_acquire(m, *this, write); -} - -inline void queuing_rw_mutex::scoped_lock::release() { - r1::release(*this); -} - -inline bool queuing_rw_mutex::scoped_lock::upgrade_to_writer() { - return r1::upgrade_to_writer(*this); -} - -inline bool queuing_rw_mutex::scoped_lock::downgrade_to_reader() { - return r1::downgrade_to_reader(*this); -} - -inline bool queuing_rw_mutex::scoped_lock::is_writer() const { - return r1::is_writer(*this); -} -} // namespace d1 - -} // namespace detail - -inline namespace v1 { -using detail::d1::queuing_rw_mutex; -} // namespace v1 -namespace profiling { - using detail::d1::set_name; -} -} // namespace tbb - -#endif /* __TBB_queuing_rw_mutex_H */ diff --git a/include/oneapi/tbb/rw_mutex.h b/include/oneapi/tbb/rw_mutex.h deleted file mode 100644 index c3fbaf6..0000000 --- a/include/oneapi/tbb/rw_mutex.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - Copyright (c) 2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_rw_mutex_H -#define __TBB_rw_mutex_H - -#include "detail/_namespace_injection.h" -#include "detail/_utils.h" -#include "detail/_waitable_atomic.h" -#include "detail/_scoped_lock.h" -#include "detail/_mutex_common.h" -#include "profiling.h" - -namespace tbb { -namespace detail { -namespace d1 { - -class rw_mutex { -public: - //! Constructors - rw_mutex() noexcept : m_state(0) { - create_itt_sync(this, "tbb::rw_mutex", ""); - } - - //! Destructor - ~rw_mutex() { - __TBB_ASSERT(!m_state.load(std::memory_order_relaxed), "destruction of an acquired mutex"); - } - - //! No Copy - rw_mutex(const rw_mutex&) = delete; - rw_mutex& operator=(const rw_mutex&) = delete; - - using scoped_lock = rw_scoped_lock; - - //! Mutex traits - static constexpr bool is_rw_mutex = true; - static constexpr bool is_recursive_mutex = false; - static constexpr bool is_fair_mutex = false; - - //! Acquire lock - void lock() { - call_itt_notify(prepare, this); - while (!try_lock()) { - if (!(m_state.load(std::memory_order_relaxed) & WRITER_PENDING)) { // no pending writers - m_state |= WRITER_PENDING; - } - - auto wakeup_condition = [&] { return !(m_state.load(std::memory_order_relaxed) & BUSY); }; - adaptive_wait_on_address(this, wakeup_condition, WRITER_CONTEXT); - } - - call_itt_notify(acquired, this); - } - - //! Try acquiring lock (non-blocking) - /** Return true if lock acquired; false otherwise. */ - bool try_lock() { - // for a writer: only possible to acquire if no active readers or writers - // Use relaxed memory fence is OK here because - // Acquire memory fence guaranteed by compare_exchange_strong() - state_type s = m_state.load(std::memory_order_relaxed); - if (!(s & BUSY)) { // no readers, no writers; mask is 1..1101 - if (m_state.compare_exchange_strong(s, WRITER)) { - call_itt_notify(acquired, this); - return true; // successfully stored writer flag - } - } - return false; - } - - //! Release lock - void unlock() { - call_itt_notify(releasing, this); - state_type curr_state = (m_state &= READERS | WRITER_PENDING); // Returns current state - - if (curr_state & WRITER_PENDING) { - r1::notify_by_address(this, WRITER_CONTEXT); - } else { - // It's possible that WRITER sleeps without WRITER_PENDING, - // because other thread might clear this bit at upgrade() - r1::notify_by_address_all(this); - } - } - - //! Lock shared ownership mutex - void lock_shared() { - call_itt_notify(prepare, this); - while (!try_lock_shared()) { - state_type has_writer = WRITER | WRITER_PENDING; - auto wakeup_condition = [&] { return !(m_state.load(std::memory_order_relaxed) & has_writer); }; - adaptive_wait_on_address(this, wakeup_condition, READER_CONTEXT); - } - __TBB_ASSERT(m_state.load(std::memory_order_relaxed) & READERS, "invalid state of a read lock: no readers"); - } - - //! Try lock shared ownership mutex - bool try_lock_shared() { - // for a reader: acquire if no active or waiting writers - // Use relaxed memory fence is OK here because - // Acquire memory fence guaranteed by fetch_add() - state_type has_writer = WRITER | WRITER_PENDING; - if (!(m_state.load(std::memory_order_relaxed) & has_writer)) { - if (m_state.fetch_add(ONE_READER) & has_writer) { - m_state -= ONE_READER; - r1::notify_by_address(this, WRITER_CONTEXT); - } else { - call_itt_notify(acquired, this); - return true; // successfully stored increased number of readers - } - } - return false; - } - - //! Unlock shared ownership mutex - void unlock_shared() { - __TBB_ASSERT(m_state.load(std::memory_order_relaxed) & READERS, "invalid state of a read lock: no readers"); - call_itt_notify(releasing, this); - - state_type curr_state = (m_state -= ONE_READER); // Returns current state - - if (curr_state & (WRITER_PENDING)) { - r1::notify_by_address(this, WRITER_CONTEXT); - } else { - // It's possible that WRITER sleeps without WRITER_PENDING, - // because other thread might clear this bit at upgrade() - r1::notify_by_address_all(this); - } - } - -private: - /** Internal non ISO C++ standard API **/ - //! This API is used through the scoped_lock class - - //! Upgrade reader to become a writer. - /** Returns whether the upgrade happened without releasing and re-acquiring the lock */ - bool upgrade() { - state_type s = m_state.load(std::memory_order_relaxed); - __TBB_ASSERT(s & READERS, "invalid state before upgrade: no readers "); - // Check and set writer-pending flag. - // Required conditions: either no pending writers, or we are the only reader - // (with multiple readers and pending writer, another upgrade could have been requested) - while ((s & READERS) == ONE_READER || !(s & WRITER_PENDING)) { - if (m_state.compare_exchange_strong(s, s | WRITER | WRITER_PENDING)) { - auto wakeup_condition = [&] { return (m_state.load(std::memory_order_relaxed) & READERS) == ONE_READER; }; - while ((m_state.load(std::memory_order_relaxed) & READERS) != ONE_READER) { - adaptive_wait_on_address(this, wakeup_condition, WRITER_CONTEXT); - } - - __TBB_ASSERT((m_state.load(std::memory_order_relaxed) & (WRITER_PENDING|WRITER)) == (WRITER_PENDING | WRITER), - "invalid state when upgrading to writer"); - // Both new readers and writers are blocked at this time - m_state -= (ONE_READER + WRITER_PENDING); - return true; // successfully upgraded - } - } - // Slow reacquire - unlock_shared(); - lock(); - return false; - } - - //! Downgrade writer to a reader - void downgrade() { - __TBB_ASSERT(m_state.load(std::memory_order_relaxed) & WRITER, nullptr), - call_itt_notify(releasing, this); - m_state += (ONE_READER - WRITER); - - if (!(m_state & WRITER_PENDING)) { - r1::notify_by_address(this, READER_CONTEXT); - } - - __TBB_ASSERT(m_state.load(std::memory_order_relaxed) & READERS, "invalid state after downgrade: no readers"); - } - - using state_type = std::intptr_t; - static constexpr state_type WRITER = 1; - static constexpr state_type WRITER_PENDING = 2; - static constexpr state_type READERS = ~(WRITER | WRITER_PENDING); - static constexpr state_type ONE_READER = 4; - static constexpr state_type BUSY = WRITER | READERS; - - using context_type = std::uintptr_t; - static constexpr context_type WRITER_CONTEXT = 0; - static constexpr context_type READER_CONTEXT = 1; - friend scoped_lock; - //! State of lock - /** Bit 0 = writer is holding lock - Bit 1 = request by a writer to acquire lock (hint to readers to wait) - Bit 2..N = number of readers holding lock */ - std::atomic m_state; -}; // class rw_mutex - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::rw_mutex; -} // namespace v1 - -} // namespace tbb - -#endif // __TBB_rw_mutex_H diff --git a/include/oneapi/tbb/scalable_allocator.h b/include/oneapi/tbb/scalable_allocator.h deleted file mode 100644 index 36a9da4..0000000 --- a/include/oneapi/tbb/scalable_allocator.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_scalable_allocator_H -#define __TBB_scalable_allocator_H - -#ifdef __cplusplus -#include "oneapi/tbb/detail/_config.h" -#include "oneapi/tbb/detail/_utils.h" -#include -#include -#include /* std::bad_alloc() */ -#else -#include "oneapi/tbb/detail/_export.h" -#include /* Need ptrdiff_t and size_t from here. */ -#if !defined(_MSC_VER) || defined(__clang__) -#include /* Need intptr_t from here. */ -#endif -#endif - -#if __TBB_CPP17_MEMORY_RESOURCE_PRESENT -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#if _MSC_VER - #define __TBB_EXPORTED_FUNC __cdecl -#else - #define __TBB_EXPORTED_FUNC -#endif - -/** The "malloc" analogue to allocate block of memory of size bytes. - * @ingroup memory_allocation */ -TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_malloc(size_t size); - -/** The "free" analogue to discard a previously allocated piece of memory. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT void __TBB_EXPORTED_FUNC scalable_free(void* ptr); - -/** The "realloc" analogue complementing scalable_malloc. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_realloc(void* ptr, size_t size); - -/** The "calloc" analogue complementing scalable_malloc. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_calloc(size_t nobj, size_t size); - -/** The "posix_memalign" analogue. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT int __TBB_EXPORTED_FUNC scalable_posix_memalign(void** memptr, size_t alignment, size_t size); - -/** The "_aligned_malloc" analogue. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_aligned_malloc(size_t size, size_t alignment); - -/** The "_aligned_realloc" analogue. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT void* __TBB_EXPORTED_FUNC scalable_aligned_realloc(void* ptr, size_t size, size_t alignment); - -/** The "_aligned_free" analogue. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT void __TBB_EXPORTED_FUNC scalable_aligned_free(void* ptr); - -/** The analogue of _msize/malloc_size/malloc_usable_size. - Returns the usable size of a memory block previously allocated by scalable_*, - or 0 (zero) if ptr does not point to such a block. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT size_t __TBB_EXPORTED_FUNC scalable_msize(void* ptr); - -/* Results for scalable_allocation_* functions */ -typedef enum { - TBBMALLOC_OK, - TBBMALLOC_INVALID_PARAM, - TBBMALLOC_UNSUPPORTED, - TBBMALLOC_NO_MEMORY, - TBBMALLOC_NO_EFFECT -} ScalableAllocationResult; - -/* Setting TBB_MALLOC_USE_HUGE_PAGES environment variable to 1 enables huge pages. - scalable_allocation_mode call has priority over environment variable. */ -typedef enum { - TBBMALLOC_USE_HUGE_PAGES, /* value turns using huge pages on and off */ - /* deprecated, kept for backward compatibility only */ - USE_HUGE_PAGES = TBBMALLOC_USE_HUGE_PAGES, - /* try to limit memory consumption value (Bytes), clean internal buffers - if limit is exceeded, but not prevents from requesting memory from OS */ - TBBMALLOC_SET_SOFT_HEAP_LIMIT, - /* Lower bound for the size (Bytes), that is interpreted as huge - * and not released during regular cleanup operations. */ - TBBMALLOC_SET_HUGE_SIZE_THRESHOLD -} AllocationModeParam; - -/** Set TBB allocator-specific allocation modes. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT int __TBB_EXPORTED_FUNC scalable_allocation_mode(int param, intptr_t value); - -typedef enum { - /* Clean internal allocator buffers for all threads. - Returns TBBMALLOC_NO_EFFECT if no buffers cleaned, - TBBMALLOC_OK if some memory released from buffers. */ - TBBMALLOC_CLEAN_ALL_BUFFERS, - /* Clean internal allocator buffer for current thread only. - Return values same as for TBBMALLOC_CLEAN_ALL_BUFFERS. */ - TBBMALLOC_CLEAN_THREAD_BUFFERS -} ScalableAllocationCmd; - -/** Call TBB allocator-specific commands. - @ingroup memory_allocation */ -TBBMALLOC_EXPORT int __TBB_EXPORTED_FUNC scalable_allocation_command(int cmd, void *param); - -#ifdef __cplusplus -} /* extern "C" */ -#endif /* __cplusplus */ - -#ifdef __cplusplus - -//! The namespace rml contains components of low-level memory pool interface. -namespace rml { -class MemoryPool; - -typedef void *(*rawAllocType)(std::intptr_t pool_id, std::size_t &bytes); -// returns non-zero in case of error -typedef int (*rawFreeType)(std::intptr_t pool_id, void* raw_ptr, std::size_t raw_bytes); - -struct MemPoolPolicy { - enum { - TBBMALLOC_POOL_VERSION = 1 - }; - - rawAllocType pAlloc; - rawFreeType pFree; - // granularity of pAlloc allocations. 0 means default used. - std::size_t granularity; - int version; - // all memory consumed at 1st pAlloc call and never returned, - // no more pAlloc calls after 1st - unsigned fixedPool : 1, - // memory consumed but returned only at pool termination - keepAllMemory : 1, - reserved : 30; - - MemPoolPolicy(rawAllocType pAlloc_, rawFreeType pFree_, - std::size_t granularity_ = 0, bool fixedPool_ = false, - bool keepAllMemory_ = false) : - pAlloc(pAlloc_), pFree(pFree_), granularity(granularity_), version(TBBMALLOC_POOL_VERSION), - fixedPool(fixedPool_), keepAllMemory(keepAllMemory_), - reserved(0) {} -}; - -// enums have same values as appropriate enums from ScalableAllocationResult -// TODO: use ScalableAllocationResult in pool_create directly -enum MemPoolError { - // pool created successfully - POOL_OK = TBBMALLOC_OK, - // invalid policy parameters found - INVALID_POLICY = TBBMALLOC_INVALID_PARAM, - // requested pool policy is not supported by allocator library - UNSUPPORTED_POLICY = TBBMALLOC_UNSUPPORTED, - // lack of memory during pool creation - NO_MEMORY = TBBMALLOC_NO_MEMORY, - // action takes no effect - NO_EFFECT = TBBMALLOC_NO_EFFECT -}; - -TBBMALLOC_EXPORT MemPoolError pool_create_v1(std::intptr_t pool_id, const MemPoolPolicy *policy, - rml::MemoryPool **pool); - -TBBMALLOC_EXPORT bool pool_destroy(MemoryPool* memPool); -TBBMALLOC_EXPORT void *pool_malloc(MemoryPool* memPool, std::size_t size); -TBBMALLOC_EXPORT void *pool_realloc(MemoryPool* memPool, void *object, std::size_t size); -TBBMALLOC_EXPORT void *pool_aligned_malloc(MemoryPool* mPool, std::size_t size, std::size_t alignment); -TBBMALLOC_EXPORT void *pool_aligned_realloc(MemoryPool* mPool, void *ptr, std::size_t size, std::size_t alignment); -TBBMALLOC_EXPORT bool pool_reset(MemoryPool* memPool); -TBBMALLOC_EXPORT bool pool_free(MemoryPool *memPool, void *object); -TBBMALLOC_EXPORT MemoryPool *pool_identify(void *object); -TBBMALLOC_EXPORT std::size_t pool_msize(MemoryPool *memPool, void *object); - -} // namespace rml - -namespace tbb { -namespace detail { -namespace d1 { - -// keep throw in a separate function to prevent code bloat -template -void throw_exception(const E &e) { -#if TBB_USE_EXCEPTIONS - throw e; -#else - suppress_unused_warning(e); -#endif -} - -template -class scalable_allocator { -public: - using value_type = T; - using propagate_on_container_move_assignment = std::true_type; - - //! Always defined for TBB containers - using is_always_equal = std::true_type; - - scalable_allocator() = default; - template scalable_allocator(const scalable_allocator&) noexcept {} - - //! Allocate space for n objects. - __TBB_nodiscard T* allocate(std::size_t n) { - T* p = static_cast(scalable_malloc(n * sizeof(value_type))); - if (!p) { - throw_exception(std::bad_alloc()); - } - return p; - } - - //! Free previously allocated block of memory - void deallocate(T* p, std::size_t) { - scalable_free(p); - } - -#if TBB_ALLOCATOR_TRAITS_BROKEN - using pointer = value_type*; - using const_pointer = const value_type*; - using reference = value_type&; - using const_reference = const value_type&; - using difference_type = std::ptrdiff_t; - using size_type = std::size_t; - template struct rebind { - using other = scalable_allocator; - }; - //! Largest value for which method allocate might succeed. - size_type max_size() const noexcept { - size_type absolutemax = static_cast(-1) / sizeof (value_type); - return (absolutemax > 0 ? absolutemax : 1); - } - template - void construct(U *p, Args&&... args) - { ::new((void *)p) U(std::forward(args)...); } - void destroy(pointer p) { p->~value_type(); } - pointer address(reference x) const { return &x; } - const_pointer address(const_reference x) const { return &x; } -#endif // TBB_ALLOCATOR_TRAITS_BROKEN - -}; - -#if TBB_ALLOCATOR_TRAITS_BROKEN - template<> - class scalable_allocator { - public: - using pointer = void*; - using const_pointer = const void*; - using value_type = void; - template struct rebind { - using other = scalable_allocator; - }; - }; -#endif - -template -inline bool operator==(const scalable_allocator&, const scalable_allocator&) noexcept { return true; } - -#if !__TBB_CPP20_COMPARISONS_PRESENT -template -inline bool operator!=(const scalable_allocator&, const scalable_allocator&) noexcept { return false; } -#endif - -#if __TBB_CPP17_MEMORY_RESOURCE_PRESENT - -//! C++17 memory resource implementation for scalable allocator -//! ISO C++ Section 23.12.2 -class scalable_resource_impl : public std::pmr::memory_resource { -private: - void* do_allocate(std::size_t bytes, std::size_t alignment) override { - void* p = scalable_aligned_malloc(bytes, alignment); - if (!p) { - throw_exception(std::bad_alloc()); - } - return p; - } - - void do_deallocate(void* ptr, std::size_t /*bytes*/, std::size_t /*alignment*/) override { - scalable_free(ptr); - } - - //! Memory allocated by one instance of scalable_resource_impl could be deallocated by any - //! other instance of this class - bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { - return this == &other || -#if __TBB_USE_OPTIONAL_RTTI - dynamic_cast(&other) != nullptr; -#else - false; -#endif - } -}; - -//! Global scalable allocator memory resource provider -inline std::pmr::memory_resource* scalable_memory_resource() noexcept { - static tbb::detail::d1::scalable_resource_impl scalable_res; - return &scalable_res; -} - -#endif // __TBB_CPP17_MEMORY_RESOURCE_PRESENT - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::scalable_allocator; -#if __TBB_CPP17_MEMORY_RESOURCE_PRESENT -using detail::d1::scalable_memory_resource; -#endif -} // namespace v1 - -} // namespace tbb - -#endif /* __cplusplus */ - -#endif /* __TBB_scalable_allocator_H */ diff --git a/include/oneapi/tbb/spin_mutex.h b/include/oneapi/tbb/spin_mutex.h deleted file mode 100644 index e38c47c..0000000 --- a/include/oneapi/tbb/spin_mutex.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_spin_mutex_H -#define __TBB_spin_mutex_H - -#include "detail/_namespace_injection.h" -#include "detail/_mutex_common.h" - -#include "profiling.h" - -#include "detail/_assert.h" -#include "detail/_utils.h" -#include "detail/_scoped_lock.h" - -#include - -namespace tbb { -namespace detail { -namespace d1 { - -#if __TBB_TSX_INTRINSICS_PRESENT -class rtm_mutex; -#endif - -/** A spin_mutex is a low-level synchronization primitive. - While locked, it causes the waiting threads to spin in a loop until the lock is released. - It should be used only for locking short critical sections - (typically less than 20 instructions) when fairness is not an issue. - If zero-initialized, the mutex is considered unheld. - @ingroup synchronization */ -class spin_mutex { -public: - //! Constructors - spin_mutex() noexcept : m_flag(false) { - create_itt_sync(this, "tbb::spin_mutex", ""); - }; - - //! Destructor - ~spin_mutex() = default; - - //! No Copy - spin_mutex(const spin_mutex&) = delete; - spin_mutex& operator=(const spin_mutex&) = delete; - - using scoped_lock = unique_scoped_lock; - - //! Mutex traits - static constexpr bool is_rw_mutex = false; - static constexpr bool is_recursive_mutex = false; - static constexpr bool is_fair_mutex = false; - - //! Acquire lock - /** Spin if the lock is taken */ - void lock() { - atomic_backoff backoff; - call_itt_notify(prepare, this); - while (m_flag.exchange(true)) backoff.pause(); - call_itt_notify(acquired, this); - } - - //! Try acquiring lock (non-blocking) - /** Return true if lock acquired; false otherwise. */ - bool try_lock() { - bool result = !m_flag.exchange(true); - if (result) { - call_itt_notify(acquired, this); - } - return result; - } - - //! Release lock - void unlock() { - call_itt_notify(releasing, this); - m_flag.store(false, std::memory_order_release); - } - -protected: - std::atomic m_flag; -}; // class spin_mutex - -#if TBB_USE_PROFILING_TOOLS -inline void set_name(spin_mutex& obj, const char* name) { - itt_set_sync_name(&obj, name); -} -#if (_WIN32||_WIN64) -inline void set_name(spin_mutex& obj, const wchar_t* name) { - itt_set_sync_name(&obj, name); -} -#endif //WIN -#else -inline void set_name(spin_mutex&, const char*) {} -#if (_WIN32||_WIN64) -inline void set_name(spin_mutex&, const wchar_t*) {} -#endif // WIN -#endif -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::spin_mutex; -} // namespace v1 -namespace profiling { - using detail::d1::set_name; -} -} // namespace tbb - -#include "detail/_rtm_mutex.h" - -namespace tbb { -inline namespace v1 { -#if __TBB_TSX_INTRINSICS_PRESENT - using speculative_spin_mutex = detail::d1::rtm_mutex; -#else - using speculative_spin_mutex = detail::d1::spin_mutex; -#endif -} -} - -#endif /* __TBB_spin_mutex_H */ - diff --git a/include/oneapi/tbb/spin_rw_mutex.h b/include/oneapi/tbb/spin_rw_mutex.h deleted file mode 100644 index 3fdae35..0000000 --- a/include/oneapi/tbb/spin_rw_mutex.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_spin_rw_mutex_H -#define __TBB_spin_rw_mutex_H - -#include "detail/_namespace_injection.h" -#include "detail/_mutex_common.h" - -#include "profiling.h" - -#include "detail/_assert.h" -#include "detail/_utils.h" -#include "detail/_scoped_lock.h" - -#include - -namespace tbb { -namespace detail { -namespace d1 { - -#if __TBB_TSX_INTRINSICS_PRESENT -class rtm_rw_mutex; -#endif - -//! Fast, unfair, spinning reader-writer lock with backoff and writer-preference -/** @ingroup synchronization */ -class spin_rw_mutex { -public: - //! Constructors - spin_rw_mutex() noexcept : m_state(0) { - create_itt_sync(this, "tbb::spin_rw_mutex", ""); - } - - //! Destructor - ~spin_rw_mutex() { - __TBB_ASSERT(!m_state, "destruction of an acquired mutex"); - } - - //! No Copy - spin_rw_mutex(const spin_rw_mutex&) = delete; - spin_rw_mutex& operator=(const spin_rw_mutex&) = delete; - - using scoped_lock = rw_scoped_lock; - - //! Mutex traits - static constexpr bool is_rw_mutex = true; - static constexpr bool is_recursive_mutex = false; - static constexpr bool is_fair_mutex = false; - - //! Acquire lock - void lock() { - call_itt_notify(prepare, this); - for (atomic_backoff backoff; ; backoff.pause()) { - state_type s = m_state.load(std::memory_order_relaxed); - if (!(s & BUSY)) { // no readers, no writers - if (m_state.compare_exchange_strong(s, WRITER)) - break; // successfully stored writer flag - backoff.reset(); // we could be very close to complete op. - } else if (!(s & WRITER_PENDING)) { // no pending writers - m_state |= WRITER_PENDING; - } - } - call_itt_notify(acquired, this); - } - - //! Try acquiring lock (non-blocking) - /** Return true if lock acquired; false otherwise. */ - bool try_lock() { - // for a writer: only possible to acquire if no active readers or writers - state_type s = m_state.load(std::memory_order_relaxed); - if (!(s & BUSY)) { // no readers, no writers; mask is 1..1101 - if (m_state.compare_exchange_strong(s, WRITER)) { - call_itt_notify(acquired, this); - return true; // successfully stored writer flag - } - } - return false; - } - - //! Release lock - void unlock() { - call_itt_notify(releasing, this); - m_state &= READERS; - } - - //! Lock shared ownership mutex - void lock_shared() { - call_itt_notify(prepare, this); - for (atomic_backoff b; ; b.pause()) { - state_type s = m_state.load(std::memory_order_relaxed); - if (!(s & (WRITER | WRITER_PENDING))) { // no writer or write requests - state_type prev_state = m_state.fetch_add(ONE_READER); - if (!(prev_state & WRITER)) { - break; // successfully stored increased number of readers - } - // writer got there first, undo the increment - m_state -= ONE_READER; - } - } - call_itt_notify(acquired, this); - __TBB_ASSERT(m_state & READERS, "invalid state of a read lock: no readers"); - } - - //! Try lock shared ownership mutex - bool try_lock_shared() { - // for a reader: acquire if no active or waiting writers - state_type s = m_state.load(std::memory_order_relaxed); - if (!(s & (WRITER | WRITER_PENDING))) { // no writers - state_type prev_state = m_state.fetch_add(ONE_READER); - if (!(prev_state & WRITER)) { // got the lock - call_itt_notify(acquired, this); - return true; // successfully stored increased number of readers - } - // writer got there first, undo the increment - m_state -= ONE_READER; - } - return false; - } - - //! Unlock shared ownership mutex - void unlock_shared() { - __TBB_ASSERT(m_state & READERS, "invalid state of a read lock: no readers"); - call_itt_notify(releasing, this); - m_state -= ONE_READER; - } - -protected: - /** Internal non ISO C++ standard API **/ - //! This API is used through the scoped_lock class - - //! Upgrade reader to become a writer. - /** Returns whether the upgrade happened without releasing and re-acquiring the lock */ - bool upgrade() { - state_type s = m_state.load(std::memory_order_relaxed); - __TBB_ASSERT(s & READERS, "invalid state before upgrade: no readers "); - // Check and set writer-pending flag. - // Required conditions: either no pending writers, or we are the only reader - // (with multiple readers and pending writer, another upgrade could have been requested) - while ((s & READERS) == ONE_READER || !(s & WRITER_PENDING)) { - if (m_state.compare_exchange_strong(s, s | WRITER | WRITER_PENDING)) { - atomic_backoff backoff; - while ((m_state.load(std::memory_order_relaxed) & READERS) != ONE_READER) backoff.pause(); - __TBB_ASSERT((m_state & (WRITER_PENDING|WRITER)) == (WRITER_PENDING | WRITER), "invalid state when upgrading to writer"); - // Both new readers and writers are blocked at this time - m_state -= (ONE_READER + WRITER_PENDING); - return true; // successfully upgraded - } - } - // Slow reacquire - unlock_shared(); - lock(); - return false; - } - - //! Downgrade writer to a reader - void downgrade() { - call_itt_notify(releasing, this); - m_state += (ONE_READER - WRITER); - __TBB_ASSERT(m_state & READERS, "invalid state after downgrade: no readers"); - } - - using state_type = std::intptr_t; - static constexpr state_type WRITER = 1; - static constexpr state_type WRITER_PENDING = 2; - static constexpr state_type READERS = ~(WRITER | WRITER_PENDING); - static constexpr state_type ONE_READER = 4; - static constexpr state_type BUSY = WRITER | READERS; - friend scoped_lock; - //! State of lock - /** Bit 0 = writer is holding lock - Bit 1 = request by a writer to acquire lock (hint to readers to wait) - Bit 2..N = number of readers holding lock */ - std::atomic m_state; -}; // class spin_rw_mutex - -#if TBB_USE_PROFILING_TOOLS -inline void set_name(spin_rw_mutex& obj, const char* name) { - itt_set_sync_name(&obj, name); -} -#if (_WIN32||_WIN64) -inline void set_name(spin_rw_mutex& obj, const wchar_t* name) { - itt_set_sync_name(&obj, name); -} -#endif // WIN -#else -inline void set_name(spin_rw_mutex&, const char*) {} -#if (_WIN32||_WIN64) -inline void set_name(spin_rw_mutex&, const wchar_t*) {} -#endif // WIN -#endif -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::spin_rw_mutex; -} // namespace v1 -namespace profiling { - using detail::d1::set_name; -} -} // namespace tbb - -#include "detail/_rtm_rw_mutex.h" - -namespace tbb { -inline namespace v1 { -#if __TBB_TSX_INTRINSICS_PRESENT - using speculative_spin_rw_mutex = detail::d1::rtm_rw_mutex; -#else - using speculative_spin_rw_mutex = detail::d1::spin_rw_mutex; -#endif -} -} - -#endif /* __TBB_spin_rw_mutex_H */ - diff --git a/include/oneapi/tbb/task.h b/include/oneapi/tbb/task.h deleted file mode 100644 index 82ce1df..0000000 --- a/include/oneapi/tbb/task.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_task_H -#define __TBB_task_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_task.h" - -namespace tbb { -inline namespace v1 { -namespace task { -#if __TBB_RESUMABLE_TASKS - using detail::d1::suspend_point; - using detail::d1::resume; - using detail::d1::suspend; -#endif /* __TBB_RESUMABLE_TASKS */ - using detail::d1::current_context; -} // namespace task -} // namespace v1 -} // namespace tbb - -#endif /* __TBB_task_H */ diff --git a/include/oneapi/tbb/task_arena.h b/include/oneapi/tbb/task_arena.h deleted file mode 100644 index 0de49ae..0000000 --- a/include/oneapi/tbb/task_arena.h +++ /dev/null @@ -1,498 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_task_arena_H -#define __TBB_task_arena_H - -#include "detail/_config.h" - -#include "detail/_aligned_space.h" -#include "detail/_attach.h" -#include "detail/_exception.h" -#include "detail/_namespace_injection.h" -#include "detail/_small_object_pool.h" -#include "detail/_task.h" - -#include "detail/_task_handle.h" - -#if __TBB_ARENA_BINDING -#include "info.h" -#endif /*__TBB_ARENA_BINDING*/ - -namespace tbb { -namespace detail { - -namespace d1 { - -template -class task_arena_function : public delegate_base { - F &my_func; - aligned_space my_return_storage; - bool my_constructed{false}; - // The function should be called only once. - bool operator()() const override { - new (my_return_storage.begin()) R(my_func()); - return true; - } -public: - task_arena_function(F& f) : my_func(f) {} - // The function can be called only after operator() and only once. - R consume_result() { - my_constructed = true; - return std::move(*(my_return_storage.begin())); - } - ~task_arena_function() override { - if (my_constructed) { - my_return_storage.begin()->~R(); - } - } -}; - -template -class task_arena_function : public delegate_base { - F &my_func; - bool operator()() const override { - my_func(); - return true; - } -public: - task_arena_function(F& f) : my_func(f) {} - void consume_result() const {} - - friend class task_arena_base; -}; - -class task_arena_base; -class task_scheduler_observer; -} // namespace d1 - -namespace r1 { -class arena; -struct task_arena_impl; - -TBB_EXPORT void __TBB_EXPORTED_FUNC observe(d1::task_scheduler_observer&, bool); -TBB_EXPORT void __TBB_EXPORTED_FUNC initialize(d1::task_arena_base&); -TBB_EXPORT void __TBB_EXPORTED_FUNC terminate(d1::task_arena_base&); -TBB_EXPORT bool __TBB_EXPORTED_FUNC attach(d1::task_arena_base&); -TBB_EXPORT void __TBB_EXPORTED_FUNC execute(d1::task_arena_base&, d1::delegate_base&); -TBB_EXPORT void __TBB_EXPORTED_FUNC wait(d1::task_arena_base&); -TBB_EXPORT int __TBB_EXPORTED_FUNC max_concurrency(const d1::task_arena_base*); -TBB_EXPORT void __TBB_EXPORTED_FUNC isolate_within_arena(d1::delegate_base& d, std::intptr_t); - -TBB_EXPORT void __TBB_EXPORTED_FUNC enqueue(d1::task&, d1::task_arena_base*); -TBB_EXPORT void __TBB_EXPORTED_FUNC enqueue(d1::task&, d1::task_group_context&, d1::task_arena_base*); -TBB_EXPORT void __TBB_EXPORTED_FUNC submit(d1::task&, d1::task_group_context&, arena*, std::uintptr_t); -} // namespace r1 - -namespace d2 { -inline void enqueue_impl(task_handle&& th, d1::task_arena_base* ta) { - __TBB_ASSERT(th != nullptr, "Attempt to schedule empty task_handle"); - - auto& ctx = task_handle_accessor::ctx_of(th); - - // Do not access th after release - r1::enqueue(*task_handle_accessor::release(th), ctx, ta); -} -} //namespace d2 - -namespace d1 { - -static constexpr int priority_stride = INT_MAX / 4; - -class task_arena_base { - friend struct r1::task_arena_impl; - friend void r1::observe(d1::task_scheduler_observer&, bool); -public: - enum class priority : int { - low = 1 * priority_stride, - normal = 2 * priority_stride, - high = 3 * priority_stride - }; -#if __TBB_ARENA_BINDING - using constraints = tbb::detail::d1::constraints; -#endif /*__TBB_ARENA_BINDING*/ -protected: - //! Special settings - intptr_t my_version_and_traits; - - std::atomic my_initialization_state; - - //! nullptr if not currently initialized. - std::atomic my_arena; - static_assert(sizeof(std::atomic) == sizeof(r1::arena*), - "To preserve backward compatibility we need the equal size of an atomic pointer and a pointer"); - - //! Concurrency level for deferred initialization - int my_max_concurrency; - - //! Reserved slots for external threads - unsigned my_num_reserved_slots; - - //! Arena priority - priority my_priority; - - //! The NUMA node index to which the arena will be attached - numa_node_id my_numa_id; - - //! The core type index to which arena will be attached - core_type_id my_core_type; - - //! Number of threads per core - int my_max_threads_per_core; - - // Backward compatibility checks. - core_type_id core_type() const { - return (my_version_and_traits & core_type_support_flag) == core_type_support_flag ? my_core_type : automatic; - } - int max_threads_per_core() const { - return (my_version_and_traits & core_type_support_flag) == core_type_support_flag ? my_max_threads_per_core : automatic; - } - - enum { - default_flags = 0 - , core_type_support_flag = 1 - }; - - task_arena_base(int max_concurrency, unsigned reserved_for_masters, priority a_priority) - : my_version_and_traits(default_flags | core_type_support_flag) - , my_initialization_state(do_once_state::uninitialized) - , my_arena(nullptr) - , my_max_concurrency(max_concurrency) - , my_num_reserved_slots(reserved_for_masters) - , my_priority(a_priority) - , my_numa_id(automatic) - , my_core_type(automatic) - , my_max_threads_per_core(automatic) - {} - -#if __TBB_ARENA_BINDING - task_arena_base(const constraints& constraints_, unsigned reserved_for_masters, priority a_priority) - : my_version_and_traits(default_flags | core_type_support_flag) - , my_initialization_state(do_once_state::uninitialized) - , my_arena(nullptr) - , my_max_concurrency(constraints_.max_concurrency) - , my_num_reserved_slots(reserved_for_masters) - , my_priority(a_priority) - , my_numa_id(constraints_.numa_id) - , my_core_type(constraints_.core_type) - , my_max_threads_per_core(constraints_.max_threads_per_core) - {} -#endif /*__TBB_ARENA_BINDING*/ -public: - //! Typedef for number of threads that is automatic. - static const int automatic = -1; - static const int not_initialized = -2; -}; - -template -R isolate_impl(F& f) { - task_arena_function func(f); - r1::isolate_within_arena(func, /*isolation*/ 0); - return func.consume_result(); -} - -template -class enqueue_task : public task { - small_object_allocator m_allocator; - const F m_func; - - void finalize(const execution_data& ed) { - m_allocator.delete_object(this, ed); - } - task* execute(execution_data& ed) override { - m_func(); - finalize(ed); - return nullptr; - } - task* cancel(execution_data&) override { - __TBB_ASSERT_RELEASE(false, "Unhandled exception from enqueue task is caught"); - return nullptr; - } -public: - enqueue_task(const F& f, small_object_allocator& alloc) : m_allocator(alloc), m_func(f) {} - enqueue_task(F&& f, small_object_allocator& alloc) : m_allocator(alloc), m_func(std::move(f)) {} -}; - -template -void enqueue_impl(F&& f, task_arena_base* ta) { - small_object_allocator alloc{}; - r1::enqueue(*alloc.new_object::type>>(std::forward(f), alloc), ta); -} -/** 1-to-1 proxy representation class of scheduler's arena - * Constructors set up settings only, real construction is deferred till the first method invocation - * Destructor only removes one of the references to the inner arena representation. - * Final destruction happens when all the references (and the work) are gone. - */ -class task_arena : public task_arena_base { - - void mark_initialized() { - __TBB_ASSERT( my_arena.load(std::memory_order_relaxed), "task_arena initialization is incomplete" ); - my_initialization_state.store(do_once_state::initialized, std::memory_order_release); - } - - template - R execute_impl(F& f) { - initialize(); - task_arena_function func(f); - r1::execute(*this, func); - return func.consume_result(); - } -public: - //! Creates task_arena with certain concurrency limits - /** Sets up settings only, real construction is deferred till the first method invocation - * @arg max_concurrency specifies total number of slots in arena where threads work - * @arg reserved_for_masters specifies number of slots to be used by external threads only. - * Value of 1 is default and reflects behavior of implicit arenas. - **/ - task_arena(int max_concurrency_ = automatic, unsigned reserved_for_masters = 1, - priority a_priority = priority::normal) - : task_arena_base(max_concurrency_, reserved_for_masters, a_priority) - {} - -#if __TBB_ARENA_BINDING - //! Creates task arena pinned to certain NUMA node - task_arena(const constraints& constraints_, unsigned reserved_for_masters = 1, - priority a_priority = priority::normal) - : task_arena_base(constraints_, reserved_for_masters, a_priority) - {} - - //! Copies settings from another task_arena - task_arena(const task_arena &s) // copy settings but not the reference or instance - : task_arena_base( - constraints{} - .set_numa_id(s.my_numa_id) - .set_max_concurrency(s.my_max_concurrency) - .set_core_type(s.my_core_type) - .set_max_threads_per_core(s.my_max_threads_per_core) - , s.my_num_reserved_slots, s.my_priority) - {} -#else - //! Copies settings from another task_arena - task_arena(const task_arena& a) // copy settings but not the reference or instance - : task_arena_base(a.my_max_concurrency, a.my_num_reserved_slots, a.my_priority) - {} -#endif /*__TBB_ARENA_BINDING*/ - - //! Tag class used to indicate the "attaching" constructor - struct attach {}; - - //! Creates an instance of task_arena attached to the current arena of the thread - explicit task_arena( attach ) - : task_arena_base(automatic, 1, priority::normal) // use default settings if attach fails - { - if (r1::attach(*this)) { - mark_initialized(); - } - } - - //! Creates an instance of task_arena attached to the current arena of the thread - explicit task_arena(d1::attach) - : task_arena(attach{}) - {} - - //! Forces allocation of the resources for the task_arena as specified in constructor arguments - void initialize() { - atomic_do_once([this]{ r1::initialize(*this); }, my_initialization_state); - } - - //! Overrides concurrency level and forces initialization of internal representation - void initialize(int max_concurrency_, unsigned reserved_for_masters = 1, - priority a_priority = priority::normal) - { - __TBB_ASSERT(!my_arena.load(std::memory_order_relaxed), "Impossible to modify settings of an already initialized task_arena"); - if( !is_active() ) { - my_max_concurrency = max_concurrency_; - my_num_reserved_slots = reserved_for_masters; - my_priority = a_priority; - r1::initialize(*this); - mark_initialized(); - } - } - -#if __TBB_ARENA_BINDING - void initialize(constraints constraints_, unsigned reserved_for_masters = 1, - priority a_priority = priority::normal) - { - __TBB_ASSERT(!my_arena.load(std::memory_order_relaxed), "Impossible to modify settings of an already initialized task_arena"); - if( !is_active() ) { - my_numa_id = constraints_.numa_id; - my_max_concurrency = constraints_.max_concurrency; - my_core_type = constraints_.core_type; - my_max_threads_per_core = constraints_.max_threads_per_core; - my_num_reserved_slots = reserved_for_masters; - my_priority = a_priority; - r1::initialize(*this); - mark_initialized(); - } - } -#endif /*__TBB_ARENA_BINDING*/ - - //! Attaches this instance to the current arena of the thread - void initialize(attach) { - // TODO: decide if this call must be thread-safe - __TBB_ASSERT(!my_arena.load(std::memory_order_relaxed), "Impossible to modify settings of an already initialized task_arena"); - if( !is_active() ) { - if ( !r1::attach(*this) ) { - r1::initialize(*this); - } - mark_initialized(); - } - } - - //! Attaches this instance to the current arena of the thread - void initialize(d1::attach) { - initialize(attach{}); - } - - //! Removes the reference to the internal arena representation. - //! Not thread safe wrt concurrent invocations of other methods. - void terminate() { - if( is_active() ) { - r1::terminate(*this); - my_initialization_state.store(do_once_state::uninitialized, std::memory_order_relaxed); - } - } - - //! Removes the reference to the internal arena representation, and destroys the external object. - //! Not thread safe wrt concurrent invocations of other methods. - ~task_arena() { - terminate(); - } - - //! Returns true if the arena is active (initialized); false otherwise. - //! The name was chosen to match a task_scheduler_init method with the same semantics. - bool is_active() const { - return my_initialization_state.load(std::memory_order_acquire) == do_once_state::initialized; - } - - //! Enqueues a task into the arena to process a functor, and immediately returns. - //! Does not require the calling thread to join the arena - - template - void enqueue(F&& f) { - initialize(); - enqueue_impl(std::forward(f), this); - } - - //! Enqueues a task into the arena to process a functor wrapped in task_handle, and immediately returns. - //! Does not require the calling thread to join the arena - void enqueue(d2::task_handle&& th) { - initialize(); - d2::enqueue_impl(std::move(th), this); - } - - //! Joins the arena and executes a mutable functor, then returns - //! If not possible to join, wraps the functor into a task, enqueues it and waits for task completion - //! Can decrement the arena demand for workers, causing a worker to leave and free a slot to the calling thread - //! Since C++11, the method returns the value returned by functor (prior to C++11 it returns void). - template - auto execute(F&& f) -> decltype(f()) { - return execute_impl(f); - } - -#if __TBB_EXTRA_DEBUG - //! Returns my_num_reserved_slots - int debug_reserved_slots() const { - // Handle special cases inside the library - return my_num_reserved_slots; - } - - //! Returns my_max_concurrency - int debug_max_concurrency() const { - // Handle special cases inside the library - return my_max_concurrency; - } - - //! Wait for all work in the arena to be completed - //! Even submitted by other application threads - //! Joins arena if/when possible (in the same way as execute()) - void debug_wait_until_empty() { - initialize(); - r1::wait(*this); - } -#endif //__TBB_EXTRA_DEBUG - - //! Returns the maximal number of threads that can work inside the arena - int max_concurrency() const { - // Handle special cases inside the library - return (my_max_concurrency > 1) ? my_max_concurrency : r1::max_concurrency(this); - } - - friend void submit(task& t, task_arena& ta, task_group_context& ctx, bool as_critical) { - __TBB_ASSERT(ta.is_active(), nullptr); - call_itt_task_notify(releasing, &t); - r1::submit(t, ctx, ta.my_arena.load(std::memory_order_relaxed), as_critical ? 1 : 0); - } -}; - -//! Executes a mutable functor in isolation within the current task arena. -//! Since C++11, the method returns the value returned by functor (prior to C++11 it returns void). -template -inline auto isolate(F&& f) -> decltype(f()) { - return isolate_impl(f); -} - -//! Returns the index, aka slot number, of the calling thread in its current arena -inline int current_thread_index() { - slot_id idx = r1::execution_slot(nullptr); - return idx == slot_id(-1) ? task_arena_base::not_initialized : int(idx); -} - -#if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS -inline bool is_inside_task() { - return nullptr != current_context(); -} -#endif //__TBB_PREVIEW_TASK_GROUP_EXTENSIONS - -//! Returns the maximal number of threads that can work inside the arena -inline int max_concurrency() { - return r1::max_concurrency(nullptr); -} - -inline void enqueue(d2::task_handle&& th) { - d2::enqueue_impl(std::move(th), nullptr); -} - -template -inline void enqueue(F&& f) { - enqueue_impl(std::forward(f), nullptr); -} - -using r1::submit; - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::task_arena; -using detail::d1::attach; - -#if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS -using detail::d1::is_inside_task; -#endif - -namespace this_task_arena { -using detail::d1::current_thread_index; -using detail::d1::max_concurrency; -using detail::d1::isolate; - -using detail::d1::enqueue; -} // namespace this_task_arena - -} // inline namespace v1 - -} // namespace tbb -#endif /* __TBB_task_arena_H */ diff --git a/include/oneapi/tbb/task_group.h b/include/oneapi/tbb/task_group.h deleted file mode 100644 index 2bbacd5..0000000 --- a/include/oneapi/tbb/task_group.h +++ /dev/null @@ -1,746 +0,0 @@ -/* - Copyright (c) 2005-2022 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_task_group_H -#define __TBB_task_group_H - -#include "detail/_config.h" -#include "detail/_namespace_injection.h" -#include "detail/_assert.h" -#include "detail/_utils.h" -#include "detail/_template_helpers.h" -#include "detail/_exception.h" -#include "detail/_task.h" -#include "detail/_small_object_pool.h" -#include "detail/_intrusive_list_node.h" -#include "detail/_task_handle.h" - -#include "profiling.h" - -#include - -#if _MSC_VER && !defined(__INTEL_COMPILER) - // Suppress warning: structure was padded due to alignment specifier - #pragma warning(push) - #pragma warning(disable:4324) -#endif - -namespace tbb { -namespace detail { - -namespace d1 { -class delegate_base; -class task_arena_base; -class task_group_context; -class task_group_base; -} - -namespace r1 { -// Forward declarations -class tbb_exception_ptr; -class market; -class thread_data; -class task_dispatcher; -template -class context_guard_helper; -struct task_arena_impl; -class context_list; - -TBB_EXPORT void __TBB_EXPORTED_FUNC execute(d1::task_arena_base&, d1::delegate_base&); -TBB_EXPORT void __TBB_EXPORTED_FUNC isolate_within_arena(d1::delegate_base&, std::intptr_t); - -TBB_EXPORT void __TBB_EXPORTED_FUNC initialize(d1::task_group_context&); -TBB_EXPORT void __TBB_EXPORTED_FUNC destroy(d1::task_group_context&); -TBB_EXPORT void __TBB_EXPORTED_FUNC reset(d1::task_group_context&); -TBB_EXPORT bool __TBB_EXPORTED_FUNC cancel_group_execution(d1::task_group_context&); -TBB_EXPORT bool __TBB_EXPORTED_FUNC is_group_execution_cancelled(d1::task_group_context&); -TBB_EXPORT void __TBB_EXPORTED_FUNC capture_fp_settings(d1::task_group_context&); - -struct task_group_context_impl; -} - -namespace d2 { - -namespace { -template -d1::task* task_ptr_or_nullptr(F&& f); -} - -template -class function_task : public task_handle_task { - //TODO: apply empty base optimization here - const F m_func; - -private: - d1::task* execute(d1::execution_data& ed) override { - __TBB_ASSERT(ed.context == &this->ctx(), "The task group context should be used for all tasks"); - task* res = task_ptr_or_nullptr(m_func); - finalize(&ed); - return res; - } - d1::task* cancel(d1::execution_data& ed) override { - finalize(&ed); - return nullptr; - } -public: - template - function_task(FF&& f, d1::wait_context& wo, d1::task_group_context& ctx, d1::small_object_allocator& alloc) - : task_handle_task{wo, ctx, alloc}, - m_func(std::forward(f)) {} -}; - -#if __TBB_PREVIEW_TASK_GROUP_EXTENSIONS -namespace { - template - d1::task* task_ptr_or_nullptr_impl(std::false_type, F&& f){ - task_handle th = std::forward(f)(); - return task_handle_accessor::release(th); - } - - template - d1::task* task_ptr_or_nullptr_impl(std::true_type, F&& f){ - std::forward(f)(); - return nullptr; - } - - template - d1::task* task_ptr_or_nullptr(F&& f){ - using is_void_t = std::is_void< - decltype(std::forward(f)()) - >; - - return task_ptr_or_nullptr_impl(is_void_t{}, std::forward(f)); - } -} -#else -namespace { - template - d1::task* task_ptr_or_nullptr(F&& f){ - std::forward(f)(); - return nullptr; - } -} // namespace -#endif // __TBB_PREVIEW_TASK_GROUP_EXTENSIONS -} // namespace d2 - -namespace d1 { - -// This structure is left here for backward compatibility check -struct context_list_node { - std::atomic prev{}; - std::atomic next{}; -}; - -//! Used to form groups of tasks -/** @ingroup task_scheduling - The context services explicit cancellation requests from user code, and unhandled - exceptions intercepted during tasks execution. Intercepting an exception results - in generating internal cancellation requests (which is processed in exactly the - same way as external ones). - - The context is associated with one or more root tasks and defines the cancellation - group that includes all the descendants of the corresponding root task(s). Association - is established when a context object is passed as an argument to the task::allocate_root() - method. See task_group_context::task_group_context for more details. - - The context can be bound to another one, and other contexts can be bound to it, - forming a tree-like structure: parent -> this -> children. Arrows here designate - cancellation propagation direction. If a task in a cancellation group is cancelled - all the other tasks in this group and groups bound to it (as children) get cancelled too. -**/ -class task_group_context : no_copy { -public: - enum traits_type { - fp_settings = 1 << 1, - concurrent_wait = 1 << 2, - default_traits = 0 - }; - enum kind_type { - isolated, - bound - }; -private: - //! Space for platform-specific FPU settings. - /** Must only be accessed inside TBB binaries, and never directly in user - code or inline methods. */ - std::uint64_t my_cpu_ctl_env; - - //! Specifies whether cancellation was requested for this task group. - std::atomic my_cancellation_requested; - - //! Versioning for run-time checks and behavioral traits of the context. - enum class task_group_context_version : std::uint8_t { - unused = 1 // ensure that new versions, if any, will not clash with previously used ones - }; - task_group_context_version my_version; - - //! The context traits. - struct context_traits { - bool fp_settings : 1; - bool concurrent_wait : 1; - bool bound : 1; - bool reserved1 : 1; - bool reserved2 : 1; - bool reserved3 : 1; - bool reserved4 : 1; - bool reserved5 : 1; - } my_traits; - - static_assert(sizeof(context_traits) == 1, "Traits shall fit into one byte."); - - static constexpr std::uint8_t may_have_children = 1; - //! The context internal state (currently only may_have_children). - std::atomic my_may_have_children; - - enum class state : std::uint8_t { - created, - locked, - isolated, - bound, - dead, - proxy = std::uint8_t(-1) //the context is not the real one, but proxy to other one - }; - - //! The synchronization machine state to manage lifetime. - std::atomic my_state; - - union { - //! Pointer to the context of the parent cancellation group. nullptr for isolated contexts. - task_group_context* my_parent; - - //! Pointer to the actual context 'this' context represents a proxy of. - task_group_context* my_actual_context; - }; - - //! Thread data instance that registered this context in its list. - r1::context_list* my_context_list; - static_assert(sizeof(std::atomic) == sizeof(r1::context_list*), "To preserve backward compatibility these types should have the same size"); - - //! Used to form the thread specific list of contexts without additional memory allocation. - /** 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 thread only. **/ - intrusive_list_node my_node; - static_assert(sizeof(intrusive_list_node) == sizeof(context_list_node), "To preserve backward compatibility these types should have the same size"); - - //! Pointer to the container storing exception being propagated across this task group. - std::atomic my_exception; - static_assert(sizeof(std::atomic) == sizeof(r1::tbb_exception_ptr*), - "backward compatibility check"); - - //! Used to set and maintain stack stitching point for Intel Performance Tools. - void* my_itt_caller; - - //! Description of algorithm for scheduler based instrumentation. - string_resource_index my_name; - - char padding[max_nfs_size - - sizeof(std::uint64_t) // my_cpu_ctl_env - - sizeof(std::atomic) // my_cancellation_requested - - sizeof(std::uint8_t) // my_version - - sizeof(context_traits) // my_traits - - sizeof(std::atomic) // my_state - - sizeof(std::atomic) // my_state - - sizeof(task_group_context*) // my_parent - - sizeof(r1::context_list*) // my_context_list - - sizeof(intrusive_list_node) // my_node - - sizeof(std::atomic) // my_exception - - sizeof(void*) // my_itt_caller - - sizeof(string_resource_index) // my_name - ]; - - task_group_context(context_traits t, string_resource_index name) - : my_version{task_group_context_version::unused}, my_name{name} - { - my_traits = t; // GCC4.8 issues warning list initialization for bitset (missing-field-initializers) - r1::initialize(*this); - } - - task_group_context(task_group_context* actual_context) - : my_version{task_group_context_version::unused} - , my_state{state::proxy} - , my_actual_context{actual_context} - { - __TBB_ASSERT(my_actual_context, "Passed pointer value points to nothing."); - my_name = actual_context->my_name; - - // no need to initialize 'this' context as it acts as a proxy for my_actual_context, which - // initialization is a user-side responsibility. - } - - static context_traits make_traits(kind_type relation_with_parent, std::uintptr_t user_traits) { - context_traits ct; - ct.fp_settings = (user_traits & fp_settings) == fp_settings; - ct.concurrent_wait = (user_traits & concurrent_wait) == concurrent_wait; - ct.bound = relation_with_parent == bound; - ct.reserved1 = ct.reserved2 = ct.reserved3 = ct.reserved4 = ct.reserved5 = false; - return ct; - } - - bool is_proxy() const { - return my_state.load(std::memory_order_relaxed) == state::proxy; - } - - task_group_context& actual_context() noexcept { - if (is_proxy()) { - __TBB_ASSERT(my_actual_context, "Actual task_group_context is not set."); - return *my_actual_context; - } - return *this; - } - - const task_group_context& actual_context() const noexcept { - if (is_proxy()) { - __TBB_ASSERT(my_actual_context, "Actual task_group_context is not set."); - return *my_actual_context; - } - return *this; - } - -public: - //! Default & binding constructor. - /** By default a bound context is created. That is this context will be bound - (as child) to the context of the currently executing task . Cancellation - requests passed to the parent context are propagated to all the contexts - bound to it. Similarly priority change is propagated from the parent context - to its children. - - If task_group_context::isolated is used as the argument, then the tasks associated - with this context will never be affected by events in any other context. - - Creating isolated contexts involve much less overhead, but they have limited - utility. Normally when an exception occurs in an algorithm that has nested - ones running, it is desirably to have all the nested algorithms cancelled - as well. Such a behavior requires nested algorithms to use bound contexts. - - There is one good place where using isolated algorithms is beneficial. It is - an external thread. That is if a particular algorithm is invoked directly from - the external thread (not from a TBB task), supplying it with explicitly - created isolated context will result in a faster algorithm startup. - - VERSIONING NOTE: - Implementation(s) of task_group_context constructor(s) cannot be made - entirely out-of-line because the run-time version must be set by the user - code. This will become critically important for binary compatibility, if - we ever have to change the size of the context object. **/ - - task_group_context(kind_type relation_with_parent = bound, - std::uintptr_t t = default_traits) - : task_group_context(make_traits(relation_with_parent, t), CUSTOM_CTX) {} - - // Custom constructor for instrumentation of oneTBB algorithm - task_group_context(string_resource_index name ) - : task_group_context(make_traits(bound, default_traits), name) {} - - // Do not introduce any logic on user side since it might break state propagation assumptions - ~task_group_context() { - // When 'this' serves as a proxy, the initialization does not happen - nor should the - // destruction. - if (!is_proxy()) - { - r1::destroy(*this); - } - } - - //! Forcefully reinitializes the context after the task tree it was associated with is completed. - /** Because the method assumes that all the tasks that used to be associated with - this context have already finished, calling it while the context is still - in use somewhere in the task hierarchy leads to undefined behavior. - - IMPORTANT: This method is not thread safe! - - The method does not change the context's parent if it is set. **/ - void reset() { - r1::reset(actual_context()); - } - - //! Initiates cancellation of all tasks in this cancellation group and its subordinate groups. - /** \return false if cancellation has already been requested, true otherwise. - - Note that canceling never fails. When false is returned, it just means that - another thread (or this one) has already sent cancellation request to this - context or to one of its ancestors (if this context is bound). It is guaranteed - that when this method is concurrently called on the same not yet cancelled - context, true will be returned by one and only one invocation. **/ - bool cancel_group_execution() { - return r1::cancel_group_execution(actual_context()); - } - - //! Returns true if the context received cancellation request. - bool is_group_execution_cancelled() { - return r1::is_group_execution_cancelled(actual_context()); - } - -#if __TBB_FP_CONTEXT - //! Captures the current FPU control settings to the context. - /** Because the method assumes that all the tasks that used to be associated with - this context have already finished, calling it while the context is still - in use somewhere in the task hierarchy leads to undefined behavior. - - IMPORTANT: This method is not thread safe! - - The method does not change the FPU control settings of the context's parent. **/ - void capture_fp_settings() { - r1::capture_fp_settings(actual_context()); - } -#endif - - //! Returns the user visible context trait - std::uintptr_t traits() const { - std::uintptr_t t{}; - const task_group_context& ctx = actual_context(); - t |= ctx.my_traits.fp_settings ? fp_settings : 0; - t |= ctx.my_traits.concurrent_wait ? concurrent_wait : 0; - return t; - } -private: - //// TODO: cleanup friends - friend class r1::market; - friend class r1::thread_data; - friend class r1::task_dispatcher; - template - friend class r1::context_guard_helper; - friend struct r1::task_arena_impl; - friend struct r1::task_group_context_impl; - friend class task_group_base; -}; // class task_group_context - -static_assert(sizeof(task_group_context) == 128, "Wrong size of task_group_context"); - -enum task_group_status { - not_complete, - complete, - canceled -}; - -class task_group; -class structured_task_group; -#if TBB_PREVIEW_ISOLATED_TASK_GROUP -class isolated_task_group; -#endif - -template -class function_task : public task { - const F m_func; - wait_context& m_wait_ctx; - small_object_allocator m_allocator; - - void finalize(const execution_data& ed) { - // Make a local reference not to access this after destruction. - wait_context& wo = m_wait_ctx; - // Copy allocator to the stack - auto allocator = m_allocator; - // Destroy user functor before release wait. - this->~function_task(); - wo.release(); - - allocator.deallocate(this, ed); - } - task* execute(execution_data& ed) override { - task* res = d2::task_ptr_or_nullptr(m_func); - finalize(ed); - return res; - } - task* cancel(execution_data& ed) override { - finalize(ed); - return nullptr; - } -public: - function_task(const F& f, wait_context& wo, small_object_allocator& alloc) - : m_func(f) - , m_wait_ctx(wo) - , m_allocator(alloc) {} - - function_task(F&& f, wait_context& wo, small_object_allocator& alloc) - : m_func(std::move(f)) - , m_wait_ctx(wo) - , m_allocator(alloc) {} -}; - -template -class function_stack_task : public task { - const F& m_func; - wait_context& m_wait_ctx; - - void finalize() { - m_wait_ctx.release(); - } - task* execute(execution_data&) override { - task* res = d2::task_ptr_or_nullptr(m_func); - finalize(); - return res; - } - task* cancel(execution_data&) override { - finalize(); - return nullptr; - } -public: - function_stack_task(const F& f, wait_context& wo) : m_func(f), m_wait_ctx(wo) {} -}; - -class task_group_base : no_copy { -protected: - wait_context m_wait_ctx; - task_group_context m_context; - - template - task_group_status internal_run_and_wait(const F& f) { - function_stack_task t{ f, m_wait_ctx }; - m_wait_ctx.reserve(); - bool cancellation_status = false; - try_call([&] { - execute_and_wait(t, context(), m_wait_ctx, context()); - }).on_completion([&] { - // TODO: the reset method is not thread-safe. Ensure the correct behavior. - cancellation_status = context().is_group_execution_cancelled(); - context().reset(); - }); - return cancellation_status ? canceled : complete; - } - - task_group_status internal_run_and_wait(d2::task_handle&& h) { - __TBB_ASSERT(h != nullptr, "Attempt to schedule empty task_handle"); - - using acs = d2::task_handle_accessor; - __TBB_ASSERT(&acs::ctx_of(h) == &context(), "Attempt to schedule task_handle into different task_group"); - - bool cancellation_status = false; - try_call([&] { - execute_and_wait(*acs::release(h), context(), m_wait_ctx, context()); - }).on_completion([&] { - // TODO: the reset method is not thread-safe. Ensure the correct behavior. - cancellation_status = context().is_group_execution_cancelled(); - context().reset(); - }); - return cancellation_status ? canceled : complete; - } - - template - task* prepare_task(F&& f) { - m_wait_ctx.reserve(); - small_object_allocator alloc{}; - return alloc.new_object::type>>(std::forward(f), m_wait_ctx, alloc); - } - - task_group_context& context() noexcept { - return m_context.actual_context(); - } - - template - d2::task_handle prepare_task_handle(F&& f) { - m_wait_ctx.reserve(); - small_object_allocator alloc{}; - using function_task_t = d2::function_task::type>; - d2::task_handle_task* function_task_p = alloc.new_object(std::forward(f), m_wait_ctx, context(), alloc); - - return d2::task_handle_accessor::construct(function_task_p); - } - -public: - task_group_base(uintptr_t traits = 0) - : m_wait_ctx(0) - , m_context(task_group_context::bound, task_group_context::default_traits | traits) - {} - - task_group_base(task_group_context& ctx) - : m_wait_ctx(0) - , m_context(&ctx) - {} - - ~task_group_base() noexcept(false) { - if (m_wait_ctx.continue_execution()) { -#if __TBB_CPP17_UNCAUGHT_EXCEPTIONS_PRESENT - bool stack_unwinding_in_progress = std::uncaught_exceptions() > 0; -#else - bool stack_unwinding_in_progress = std::uncaught_exception(); -#endif - // Always attempt to do proper cleanup to avoid inevitable memory corruption - // in case of missing wait (for the sake of better testability & debuggability) - if (!context().is_group_execution_cancelled()) - cancel(); - d1::wait(m_wait_ctx, context()); - if (!stack_unwinding_in_progress) - throw_exception(exception_id::missing_wait); - } - } - - task_group_status wait() { - bool cancellation_status = false; - try_call([&] { - d1::wait(m_wait_ctx, context()); - }).on_completion([&] { - // TODO: the reset method is not thread-safe. Ensure the correct behavior. - cancellation_status = m_context.is_group_execution_cancelled(); - context().reset(); - }); - return cancellation_status ? canceled : complete; - } - - void cancel() { - context().cancel_group_execution(); - } -}; // class task_group_base - -class task_group : public task_group_base { -public: - task_group() : task_group_base(task_group_context::concurrent_wait) {} - task_group(task_group_context& ctx) : task_group_base(ctx) {} - - template - void run(F&& f) { - spawn(*prepare_task(std::forward(f)), context()); - } - - void run(d2::task_handle&& h) { - __TBB_ASSERT(h != nullptr, "Attempt to schedule empty task_handle"); - - using acs = d2::task_handle_accessor; - __TBB_ASSERT(&acs::ctx_of(h) == &context(), "Attempt to schedule task_handle into different task_group"); - - spawn(*acs::release(h), context()); - } - - template - d2::task_handle defer(F&& f) { - return prepare_task_handle(std::forward(f)); - - } - - template - task_group_status run_and_wait(const F& f) { - return internal_run_and_wait(f); - } - - task_group_status run_and_wait(d2::task_handle&& h) { - return internal_run_and_wait(std::move(h)); - } -}; // class task_group - -#if TBB_PREVIEW_ISOLATED_TASK_GROUP -class spawn_delegate : public delegate_base { - task* task_to_spawn; - task_group_context& context; - bool operator()() const override { - spawn(*task_to_spawn, context); - return true; - } -public: - spawn_delegate(task* a_task, task_group_context& ctx) - : task_to_spawn(a_task), context(ctx) - {} -}; - -class wait_delegate : public delegate_base { - bool operator()() const override { - status = tg.wait(); - return true; - } -protected: - task_group& tg; - task_group_status& status; -public: - wait_delegate(task_group& a_group, task_group_status& tgs) - : tg(a_group), status(tgs) {} -}; - -template -class run_wait_delegate : public wait_delegate { - F& func; - bool operator()() const override { - status = tg.run_and_wait(func); - return true; - } -public: - run_wait_delegate(task_group& a_group, F& a_func, task_group_status& tgs) - : wait_delegate(a_group, tgs), func(a_func) {} -}; - -class isolated_task_group : public task_group { - intptr_t this_isolation() { - return reinterpret_cast(this); - } -public: - isolated_task_group() : task_group() {} - - isolated_task_group(task_group_context& ctx) : task_group(ctx) {} - - template - void run(F&& f) { - spawn_delegate sd(prepare_task(std::forward(f)), context()); - r1::isolate_within_arena(sd, this_isolation()); - } - - void run(d2::task_handle&& h) { - __TBB_ASSERT(h != nullptr, "Attempt to schedule empty task_handle"); - - using acs = d2::task_handle_accessor; - __TBB_ASSERT(&acs::ctx_of(h) == &context(), "Attempt to schedule task_handle into different task_group"); - - spawn_delegate sd(acs::release(h), context()); - r1::isolate_within_arena(sd, this_isolation()); - } - - template - task_group_status run_and_wait( const F& f ) { - task_group_status result = not_complete; - run_wait_delegate rwd(*this, f, result); - r1::isolate_within_arena(rwd, this_isolation()); - __TBB_ASSERT(result != not_complete, "premature exit from wait?"); - return result; - } - - task_group_status wait() { - task_group_status result = not_complete; - wait_delegate wd(*this, result); - r1::isolate_within_arena(wd, this_isolation()); - __TBB_ASSERT(result != not_complete, "premature exit from wait?"); - return result; - } -}; // class isolated_task_group -#endif // TBB_PREVIEW_ISOLATED_TASK_GROUP - -inline bool is_current_task_group_canceling() { - task_group_context* ctx = current_context(); - return ctx ? ctx->is_group_execution_cancelled() : false; -} - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::task_group_context; -using detail::d1::task_group; -#if TBB_PREVIEW_ISOLATED_TASK_GROUP -using detail::d1::isolated_task_group; -#endif - -using detail::d1::task_group_status; -using detail::d1::not_complete; -using detail::d1::complete; -using detail::d1::canceled; - -using detail::d1::is_current_task_group_canceling; -using detail::r1::missing_wait; - -using detail::d2::task_handle; -} - -} // namespace tbb - -#if _MSC_VER && !defined(__INTEL_COMPILER) - #pragma warning(pop) // 4324 warning -#endif - -#endif // __TBB_task_group_H diff --git a/include/oneapi/tbb/task_scheduler_observer.h b/include/oneapi/tbb/task_scheduler_observer.h deleted file mode 100644 index a228cfe..0000000 --- a/include/oneapi/tbb/task_scheduler_observer.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_task_scheduler_observer_H -#define __TBB_task_scheduler_observer_H - -#include "detail/_namespace_injection.h" -#include "task_arena.h" -#include - -namespace tbb { -namespace detail { - -namespace d1 { -class task_scheduler_observer; -} - -namespace r1 { -class observer_proxy; -class observer_list; - -//! Enable or disable observation -/** For local observers the method can be used only when the current thread -has the task scheduler initialized or is attached to an arena. -Repeated calls with the same state are no-ops. **/ -TBB_EXPORT void __TBB_EXPORTED_FUNC observe(d1::task_scheduler_observer&, bool state = true); -} - -namespace d1 { -class task_scheduler_observer { - friend class r1::observer_proxy; - friend class r1::observer_list; - friend void r1::observe(d1::task_scheduler_observer&, bool); - - //! Pointer to the proxy holding this observer. - /** Observers are proxied by the scheduler to maintain persistent lists of them. **/ - std::atomic my_proxy{ nullptr }; - - //! Counter preventing the observer from being destroyed while in use by the scheduler. - /** Valid only when observation is on. **/ - std::atomic my_busy_count{ 0 }; - - //! Contains task_arena pointer - task_arena* my_task_arena{ nullptr }; -public: - //! Returns true if observation is enabled, false otherwise. - bool is_observing() const { return my_proxy.load(std::memory_order_relaxed) != nullptr; } - - //! Entry notification - /** Invoked from inside observe(true) call and whenever a worker enters the arena - this observer is associated with. If a thread is already in the arena when - the observer is activated, the entry notification is called before it - executes the first stolen task. **/ - virtual void on_scheduler_entry( bool /*is_worker*/ ) {} - - //! Exit notification - /** Invoked from inside observe(false) call and whenever a worker leaves the - arena this observer is associated with. **/ - virtual void on_scheduler_exit( bool /*is_worker*/ ) {} - - //! Construct local or global observer in inactive state (observation disabled). - /** For a local observer entry/exit notifications are invoked whenever a worker - thread joins/leaves the arena of the observer's owner thread. If a thread is - already in the arena when the observer is activated, the entry notification is - called before it executes the first stolen task. **/ - explicit task_scheduler_observer() = default; - - //! Construct local observer for a given arena in inactive state (observation disabled). - /** entry/exit notifications are invoked whenever a thread joins/leaves arena. - If a thread is already in the arena when the observer is activated, the entry notification - is called before it executes the first stolen task. **/ - explicit task_scheduler_observer(task_arena& a) : my_task_arena(&a) {} - - /** Destructor protects instance of the observer from concurrent notification. - It is recommended to disable observation before destructor of a derived class starts, - otherwise it can lead to concurrent notification callback on partly destroyed object **/ - virtual ~task_scheduler_observer() { - if (my_proxy.load(std::memory_order_acquire)) { - observe(false); - } - } - - //! Enable or disable observation - /** Warning: concurrent invocations of this method are not safe. - Repeated calls with the same state are no-ops. **/ - void observe(bool state = true) { - if( state && !my_proxy.load(std::memory_order_relaxed) ) { - __TBB_ASSERT( my_busy_count.load(std::memory_order_relaxed) == 0, "Inconsistent state of task_scheduler_observer instance"); - } - r1::observe(*this, state); - } -}; - -} // namespace d1 -} // namespace detail - -inline namespace v1 { - using detail::d1::task_scheduler_observer; -} -} // namespace tbb - - -#endif /* __TBB_task_scheduler_observer_H */ diff --git a/include/oneapi/tbb/tbb_allocator.h b/include/oneapi/tbb/tbb_allocator.h deleted file mode 100644 index 5394b81..0000000 --- a/include/oneapi/tbb/tbb_allocator.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_tbb_allocator_H -#define __TBB_tbb_allocator_H - -#include "oneapi/tbb/detail/_utils.h" -#include "detail/_namespace_injection.h" -#include -#include - -#if __TBB_CPP17_MEMORY_RESOURCE_PRESENT -#include -#endif - -namespace tbb { -namespace detail { - -namespace r1 { -TBB_EXPORT void* __TBB_EXPORTED_FUNC allocate_memory(std::size_t size); -TBB_EXPORT void __TBB_EXPORTED_FUNC deallocate_memory(void* p); -TBB_EXPORT bool __TBB_EXPORTED_FUNC is_tbbmalloc_used(); -} - -namespace d1 { - -template -class tbb_allocator { -public: - using value_type = T; - using propagate_on_container_move_assignment = std::true_type; - - //! Always defined for TBB containers (supported since C++17 for std containers) - using is_always_equal = std::true_type; - - //! Specifies current allocator - enum malloc_type { - scalable, - standard - }; - - tbb_allocator() = default; - template tbb_allocator(const tbb_allocator&) noexcept {} - - //! Allocate space for n objects. - __TBB_nodiscard T* allocate(std::size_t n) { - return static_cast(r1::allocate_memory(n * sizeof(value_type))); - } - - //! Free previously allocated block of memory. - void deallocate(T* p, std::size_t) { - r1::deallocate_memory(p); - } - - //! Returns current allocator - static malloc_type allocator_type() { - return r1::is_tbbmalloc_used() ? standard : scalable; - } - -#if TBB_ALLOCATOR_TRAITS_BROKEN - using pointer = value_type*; - using const_pointer = const value_type*; - using reference = value_type&; - using const_reference = const value_type&; - using difference_type = std::ptrdiff_t; - using size_type = std::size_t; - template struct rebind { - using other = tbb_allocator; - }; - //! Largest value for which method allocate might succeed. - size_type max_size() const noexcept { - size_type max = ~(std::size_t(0)) / sizeof(value_type); - return (max > 0 ? max : 1); - } - template - void construct(U *p, Args&&... args) - { ::new (p) U(std::forward(args)...); } - void destroy( pointer p ) { p->~value_type(); } - pointer address(reference x) const { return &x; } - const_pointer address(const_reference x) const { return &x; } -#endif // TBB_ALLOCATOR_TRAITS_BROKEN -}; - -#if TBB_ALLOCATOR_TRAITS_BROKEN - template<> - class tbb_allocator { - public: - using pointer = void*; - using const_pointer = const void*; - using value_type = void; - template struct rebind { - using other = tbb_allocator; - }; - }; -#endif - -template -inline bool operator==(const tbb_allocator&, const tbb_allocator&) noexcept { return true; } - -#if !__TBB_CPP20_COMPARISONS_PRESENT -template -inline bool operator!=(const tbb_allocator&, const tbb_allocator&) noexcept { return false; } -#endif - -} // namespace d1 -} // namespace detail - -inline namespace v1 { -using detail::d1::tbb_allocator; -} // namespace v1 -} // namespace tbb - -#endif /* __TBB_tbb_allocator_H */ diff --git a/include/oneapi/tbb/tbbmalloc_proxy.h b/include/oneapi/tbb/tbbmalloc_proxy.h deleted file mode 100644 index 0ba38f2..0000000 --- a/include/oneapi/tbb/tbbmalloc_proxy.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/* -Replacing the standard memory allocation routines in Microsoft* C/C++ RTL -(malloc/free, global new/delete, etc.) with the TBB memory allocator. - -Include the following header to a source of any binary which is loaded during -application startup - -#include "oneapi/tbb/tbbmalloc_proxy.h" - -or add following parameters to the linker options for the binary which is -loaded during application startup. It can be either exe-file or dll. - -For win32 -tbbmalloc_proxy.lib /INCLUDE:"___TBB_malloc_proxy" -win64 -tbbmalloc_proxy.lib /INCLUDE:"__TBB_malloc_proxy" -*/ - -#ifndef __TBB_tbbmalloc_proxy_H -#define __TBB_tbbmalloc_proxy_H - -#if _MSC_VER - -#ifdef _DEBUG - #pragma comment(lib, "tbbmalloc_proxy_debug.lib") -#else - #pragma comment(lib, "tbbmalloc_proxy.lib") -#endif - -#if defined(_WIN64) - #pragma comment(linker, "/include:__TBB_malloc_proxy") -#else - #pragma comment(linker, "/include:___TBB_malloc_proxy") -#endif - -#else -/* Primarily to support MinGW */ - -extern "C" void __TBB_malloc_proxy(); -struct __TBB_malloc_proxy_caller { - __TBB_malloc_proxy_caller() { __TBB_malloc_proxy(); } -} volatile __TBB_malloc_proxy_helper_object; - -#endif // _MSC_VER - -/* Public Windows API */ -extern "C" int TBB_malloc_replacement_log(char *** function_replacement_log_ptr); - -#endif //__TBB_tbbmalloc_proxy_H diff --git a/include/oneapi/tbb/tick_count.h b/include/oneapi/tbb/tick_count.h deleted file mode 100644 index 2caa56b..0000000 --- a/include/oneapi/tbb/tick_count.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - Copyright (c) 2005-2021 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_tick_count_H -#define __TBB_tick_count_H - -#include - -#include "detail/_namespace_injection.h" - -namespace tbb { -namespace detail { -namespace d1 { - - -//! Absolute timestamp -/** @ingroup timing */ -class tick_count { -public: - using clock_type = typename std::conditional::type; - - //! Relative time interval. - class interval_t : public clock_type::duration { - public: - //! Construct a time interval representing zero time duration - interval_t() : clock_type::duration(clock_type::duration::zero()) {} - - //! Construct a time interval representing sec seconds time duration - explicit interval_t( double sec ) - : clock_type::duration(std::chrono::duration_cast(std::chrono::duration(sec))) {} - - //! Return the length of a time interval in seconds - double seconds() const { - return std::chrono::duration_cast>(*this).count(); - } - - //! Extract the intervals from the tick_counts and subtract them. - friend interval_t operator-( const tick_count& t1, const tick_count& t0 ); - - //! Add two intervals. - friend interval_t operator+( const interval_t& i, const interval_t& j ) { - return interval_t(std::chrono::operator+(i, j)); - } - - //! Subtract two intervals. - friend interval_t operator-( const interval_t& i, const interval_t& j ) { - return interval_t(std::chrono::operator-(i, j)); - } - - private: - explicit interval_t( clock_type::duration value_ ) : clock_type::duration(value_) {} - }; - - tick_count() = default; - - //! Return current time. - static tick_count now() { - return clock_type::now(); - } - - //! Subtract two timestamps to get the time interval between - friend interval_t operator-( const tick_count& t1, const tick_count& t0 ) { - return tick_count::interval_t(t1.my_time_point - t0.my_time_point); - } - - //! Return the resolution of the clock in seconds per tick. - static double resolution() { - return static_cast(interval_t::period::num) / interval_t::period::den; - } - -private: - clock_type::time_point my_time_point; - tick_count( clock_type::time_point tp ) : my_time_point(tp) {} -}; - -} // namespace d1 -} // namespace detail - -inline namespace v1 { - using detail::d1::tick_count; -} // namespace v1 - -} // namespace tbb - -#endif /* __TBB_tick_count_H */ diff --git a/include/oneapi/tbb/version.h b/include/oneapi/tbb/version.h deleted file mode 100644 index 965af12..0000000 --- a/include/oneapi/tbb/version.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - Copyright (c) 2005-2023 Intel Corporation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#ifndef __TBB_version_H -#define __TBB_version_H - -// Exclude all includes during .rc files compilation -#ifndef RC_INVOKED - #include "detail/_config.h" - #include "detail/_namespace_injection.h" -#else - #define __TBB_STRING_AUX(x) #x - #define __TBB_STRING(x) __TBB_STRING_AUX(x) -#endif - -// Product version -#define TBB_VERSION_MAJOR 2021 -// Update version -#define TBB_VERSION_MINOR 10 -// "Patch" version for custom releases -#define TBB_VERSION_PATCH 0 -// Suffix string -#define __TBB_VERSION_SUFFIX "" -// Full official version string -#define TBB_VERSION_STRING __TBB_STRING(TBB_VERSION_MAJOR) "." __TBB_STRING(TBB_VERSION_MINOR) __TBB_VERSION_SUFFIX - -// OneAPI oneTBB specification version -#define ONETBB_SPEC_VERSION "1.0" -// Full interface version -#define TBB_INTERFACE_VERSION 12100 -// Major interface version -#define TBB_INTERFACE_VERSION_MAJOR (TBB_INTERFACE_VERSION/1000) -// Minor interface version -#define TBB_INTERFACE_VERSION_MINOR (TBB_INTERFACE_VERSION%1000/10) - -// The binary compatibility version -// To be used in SONAME, manifests, etc. -#define __TBB_BINARY_VERSION 12 - -//! TBB_VERSION support -#ifndef ENDL -#define ENDL "\n" -#endif - -//TBB_REVAMP_TODO: consider enabling version_string.ver generation -//TBB_REVAMP_TODO: #include "version_string.ver" - -#define __TBB_ONETBB_SPEC_VERSION(N) #N ": SPECIFICATION VERSION\t" ONETBB_SPEC_VERSION ENDL -#define __TBB_VERSION_NUMBER(N) #N ": VERSION\t\t" TBB_VERSION_STRING ENDL -#define __TBB_INTERFACE_VERSION_NUMBER(N) #N ": INTERFACE VERSION\t" __TBB_STRING(TBB_INTERFACE_VERSION) ENDL - -#ifndef TBB_USE_DEBUG - #define __TBB_VERSION_USE_DEBUG(N) #N ": TBB_USE_DEBUG\tundefined" ENDL -#elif TBB_USE_DEBUG==0 - #define __TBB_VERSION_USE_DEBUG(N) #N ": TBB_USE_DEBUG\t0" ENDL -#elif TBB_USE_DEBUG==1 - #define __TBB_VERSION_USE_DEBUG(N) #N ": TBB_USE_DEBUG\t1" ENDL -#elif TBB_USE_DEBUG==2 - #define __TBB_VERSION_USE_DEBUG(N) #N ": TBB_USE_DEBUG\t2" ENDL -#else - #error Unexpected value for TBB_USE_DEBUG -#endif - -#ifndef TBB_USE_ASSERT - #define __TBB_VERSION_USE_ASSERT(N) #N ": TBB_USE_ASSERT\tundefined" ENDL -#elif TBB_USE_ASSERT==0 - #define __TBB_VERSION_USE_ASSERT(N) #N ": TBB_USE_ASSERT\t0" ENDL -#elif TBB_USE_ASSERT==1 - #define __TBB_VERSION_USE_ASSERT(N) #N ": TBB_USE_ASSERT\t1" ENDL -#elif TBB_USE_ASSERT==2 - #define __TBB_VERSION_USE_ASSERT(N) #N ": TBB_USE_ASSERT\t2" ENDL -#else - #error Unexpected value for TBB_USE_ASSERT -#endif - -#define TBB_VERSION_STRINGS_P(N) \ - __TBB_ONETBB_SPEC_VERSION(N) \ - __TBB_VERSION_NUMBER(N) \ - __TBB_INTERFACE_VERSION_NUMBER(N) \ - __TBB_VERSION_USE_DEBUG(N) \ - __TBB_VERSION_USE_ASSERT(N) - -#define TBB_VERSION_STRINGS TBB_VERSION_STRINGS_P(oneTBB) -#define TBBMALLOC_VERSION_STRINGS TBB_VERSION_STRINGS_P(TBBmalloc) - -//! The function returns the version string for the Intel(R) oneAPI Threading Building Blocks (oneTBB) -//! shared library being used. -/** - * The returned pointer is an address of a string in the shared library. - * It can be different than the TBB_VERSION_STRING obtained at compile time. - */ -extern "C" TBB_EXPORT const char* __TBB_EXPORTED_FUNC TBB_runtime_version(); - -//! The function returns the interface version of the oneTBB shared library being used. -/** - * The returned version is determined at runtime, not at compile/link time. - * It can be different than the value of TBB_INTERFACE_VERSION obtained at compile time. - */ -extern "C" TBB_EXPORT int __TBB_EXPORTED_FUNC TBB_runtime_interface_version(); - -#endif // __TBB_version_H diff --git a/libraries/tbb/tbb.def b/libraries/tbb/tbb.def deleted file mode 100644 index 306903c..0000000 --- a/libraries/tbb/tbb.def +++ /dev/null @@ -1,150 +0,0 @@ -; Copyright (c) 2005-2021 Intel Corporation -; -; Licensed under the Apache License, Version 2.0 (the "License"); -; you may not use this file except in compliance with the License. -; You may obtain a copy of the License at -; -; http://www.apache.org/licenses/LICENSE-2.0 -; -; Unless required by applicable law or agreed to in writing, software -; distributed under the License is distributed on an "AS IS" BASIS, -; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -; See the License for the specific language governing permissions and -; limitations under the License. - -; This file is organized with a section for each .cpp file. - -EXPORTS - -; Assertions (assert.cpp) -?assertion_failure@r1@detail@tbb@@YAXPEBDH00@Z - -; ITT (tbb_profiling.cpp) -?call_itt_notify@r1@detail@tbb@@YAXHPEAX@Z -?create_itt_sync@r1@detail@tbb@@YAXPEAXPEB_W1@Z -?itt_make_task_group@r1@detail@tbb@@YAXW4itt_domain_enum@d1@23@PEAX_K12W4string_resource_index@d0@23@@Z -?itt_task_begin@r1@detail@tbb@@YAXW4itt_domain_enum@d1@23@PEAX_K12W4string_resource_index@d0@23@@Z -?itt_task_end@r1@detail@tbb@@YAXW4itt_domain_enum@d1@23@@Z -?itt_set_sync_name@r1@detail@tbb@@YAXPEAXPEB_W@Z -?itt_metadata_str_add@r1@detail@tbb@@YAXW4itt_domain_enum@d1@23@PEAX_KW4string_resource_index@d0@23@PEBD@Z -?itt_relation_add@r1@detail@tbb@@YAXW4itt_domain_enum@d1@23@PEAX_KW4itt_relation@d0@23@12@Z -?itt_region_begin@r1@detail@tbb@@YAXW4itt_domain_enum@d1@23@PEAX_K12W4string_resource_index@d0@23@@Z -?itt_region_end@r1@detail@tbb@@YAXW4itt_domain_enum@d1@23@PEAX_K@Z -?itt_metadata_ptr_add@r1@detail@tbb@@YAXW4itt_domain_enum@d1@23@PEAX_KW4string_resource_index@d0@23@1@Z - -; Allocators (tbb_allocator.cpp) -?cache_aligned_allocate@r1@detail@tbb@@YAPEAX_K@Z -?cache_aligned_deallocate@r1@detail@tbb@@YAXPEAX@Z -?cache_line_size@r1@detail@tbb@@YA_KXZ -?allocate_memory@r1@detail@tbb@@YAPEAX_K@Z -?deallocate_memory@r1@detail@tbb@@YAXPEAX@Z -?is_tbbmalloc_used@r1@detail@tbb@@YA_NXZ - -; Small object pool (small_object_pool.cpp) -?allocate@r1@detail@tbb@@YAPEAXAEAPEAVsmall_object_pool@d1@23@_KAEBUexecution_data@523@@Z -?allocate@r1@detail@tbb@@YAPEAXAEAPEAVsmall_object_pool@d1@23@_K@Z -?deallocate@r1@detail@tbb@@YAXAEAVsmall_object_pool@d1@23@PEAX_KAEBUexecution_data@523@@Z -?deallocate@r1@detail@tbb@@YAXAEAVsmall_object_pool@d1@23@PEAX_K@Z - -; Error handling (exception.cpp) -?throw_exception@r1@detail@tbb@@YAXW4exception_id@d0@23@@Z -?what@bad_last_alloc@r1@detail@tbb@@UEBAPEBDXZ -?what@user_abort@r1@detail@tbb@@UEBAPEBDXZ -?what@missing_wait@r1@detail@tbb@@UEBAPEBDXZ - -; RTM Mutex (rtm_mutex.cpp) -?try_acquire@r1@detail@tbb@@YA_NAEAVrtm_mutex@d1@23@AEAVscoped_lock@4523@@Z -?acquire@r1@detail@tbb@@YAXAEAVrtm_mutex@d1@23@AEAVscoped_lock@4523@_N@Z -?release@r1@detail@tbb@@YAXAEAVscoped_lock@rtm_mutex@d1@23@@Z - -; RTM RW Mutex (rtm_rw_mutex.cpp) -?acquire_writer@r1@detail@tbb@@YAXAEAVrtm_rw_mutex@d1@23@AEAVscoped_lock@4523@_N@Z -?acquire_reader@r1@detail@tbb@@YAXAEAVrtm_rw_mutex@d1@23@AEAVscoped_lock@4523@_N@Z -?upgrade@r1@detail@tbb@@YA_NAEAVscoped_lock@rtm_rw_mutex@d1@23@@Z -?downgrade@r1@detail@tbb@@YA_NAEAVscoped_lock@rtm_rw_mutex@d1@23@@Z -?try_acquire_writer@r1@detail@tbb@@YA_NAEAVrtm_rw_mutex@d1@23@AEAVscoped_lock@4523@@Z -?try_acquire_reader@r1@detail@tbb@@YA_NAEAVrtm_rw_mutex@d1@23@AEAVscoped_lock@4523@@Z -?release@r1@detail@tbb@@YAXAEAVscoped_lock@rtm_rw_mutex@d1@23@@Z - -; Tasks and partitioners (task.cpp) -?suspend@r1@detail@tbb@@YAXP6AXPEAXPEAUsuspend_point_type@123@@Z0@Z -?resume@r1@detail@tbb@@YAXPEAUsuspend_point_type@123@@Z -?current_suspend_point@r1@detail@tbb@@YAPEAUsuspend_point_type@123@XZ -?notify_waiters@r1@detail@tbb@@YAX_K@Z - -; Task dispatcher (task_dispatcher.cpp) -?spawn@r1@detail@tbb@@YAXAEAVtask@d1@23@AEAVtask_group_context@523@@Z -?spawn@r1@detail@tbb@@YAXAEAVtask@d1@23@AEAVtask_group_context@523@G@Z -?execute_and_wait@r1@detail@tbb@@YAXAEAVtask@d1@23@AEAVtask_group_context@523@AEAVwait_context@523@1@Z -?execution_slot@r1@detail@tbb@@YAGPEBUexecution_data@d1@23@@Z -?wait@r1@detail@tbb@@YAXAEAVwait_context@d1@23@AEAVtask_group_context@523@@Z -?submit@r1@detail@tbb@@YAXAEAVtask@d1@23@AEAVtask_group_context@523@PEAVarena@123@_K@Z -?current_context@r1@detail@tbb@@YAPEAVtask_group_context@d1@23@XZ - -; Task group context (task_group_context.cpp) -?initialize@r1@detail@tbb@@YAXAEAVtask_group_context@d1@23@@Z -?destroy@r1@detail@tbb@@YAXAEAVtask_group_context@d1@23@@Z -?is_group_execution_cancelled@r1@detail@tbb@@YA_NAEAVtask_group_context@d1@23@@Z -?reset@r1@detail@tbb@@YAXAEAVtask_group_context@d1@23@@Z -?cancel_group_execution@r1@detail@tbb@@YA_NAEAVtask_group_context@d1@23@@Z -?capture_fp_settings@r1@detail@tbb@@YAXAEAVtask_group_context@d1@23@@Z - -; Task arena (arena.cpp) -?max_concurrency@r1@detail@tbb@@YAHPEBVtask_arena_base@d1@23@@Z -?initialize@r1@detail@tbb@@YAXAEAVtask_arena_base@d1@23@@Z -?terminate@r1@detail@tbb@@YAXAEAVtask_arena_base@d1@23@@Z -?execute@r1@detail@tbb@@YAXAEAVtask_arena_base@d1@23@AEAVdelegate_base@523@@Z -?wait@r1@detail@tbb@@YAXAEAVtask_arena_base@d1@23@@Z -?attach@r1@detail@tbb@@YA_NAEAVtask_arena_base@d1@23@@Z -?isolate_within_arena@r1@detail@tbb@@YAXAEAVdelegate_base@d1@23@_J@Z -?enqueue@r1@detail@tbb@@YAXAEAVtask@d1@23@PEAVtask_arena_base@523@@Z -?enqueue@r1@detail@tbb@@YAXAEAVtask@d1@23@AEAVtask_group_context@523@PEAVtask_arena_base@523@@Z - -; System topology parsing and threads pinning (governor.cpp) -?numa_node_count@r1@detail@tbb@@YAIXZ -?fill_numa_indices@r1@detail@tbb@@YAXPEAH@Z -?core_type_count@r1@detail@tbb@@YAI_J@Z -?fill_core_type_indices@r1@detail@tbb@@YAXPEAH_J@Z -?numa_default_concurrency@r1@detail@tbb@@YAHH@Z -?constraints_default_concurrency@r1@detail@tbb@@YAHAEBUconstraints@d1@23@_J@Z -?constraints_threads_per_core@r1@detail@tbb@@YAHAEBUconstraints@d1@23@_J@Z - -; Observer (observer_proxy.cpp) -?observe@r1@detail@tbb@@YAXAEAVtask_scheduler_observer@d1@23@_N@Z - -; Queuing RW Mutex (queuing_rw_mutex.cpp) -?construct@r1@detail@tbb@@YAXAEAVqueuing_rw_mutex@d1@23@@Z -?try_acquire@r1@detail@tbb@@YA_NAEAVqueuing_rw_mutex@d1@23@AEAVscoped_lock@4523@_N@Z -?acquire@r1@detail@tbb@@YAXAEAVqueuing_rw_mutex@d1@23@AEAVscoped_lock@4523@_N@Z -?release@r1@detail@tbb@@YAXAEAVscoped_lock@queuing_rw_mutex@d1@23@@Z -?upgrade_to_writer@r1@detail@tbb@@YA_NAEAVscoped_lock@queuing_rw_mutex@d1@23@@Z -?downgrade_to_reader@r1@detail@tbb@@YA_NAEAVscoped_lock@queuing_rw_mutex@d1@23@@Z -?is_writer@r1@detail@tbb@@YA_NAEBVscoped_lock@queuing_rw_mutex@d1@23@@Z - -; Global control (global_control.cpp) -?global_control_active_value@r1@detail@tbb@@YA_KH@Z -?create@r1@detail@tbb@@YAXAEAVglobal_control@d1@23@@Z -?destroy@r1@detail@tbb@@YAXAEAVglobal_control@d1@23@@Z -?get@r1@detail@tbb@@YAXAEAVtask_scheduler_handle@d1@23@@Z -?finalize@r1@detail@tbb@@YA_NAEAVtask_scheduler_handle@d1@23@_J@Z - -; Parallel pipeline (parallel_pipeline.cpp) -?set_end_of_input@r1@detail@tbb@@YAXAEAVbase_filter@d1@23@@Z -?parallel_pipeline@r1@detail@tbb@@YAXAEAVtask_group_context@d1@23@_KAEBVfilter_node@523@@Z - -; Concurrent bounded queue (concurrent_bounded_queue.cpp) -?allocate_bounded_queue_rep@r1@detail@tbb@@YAPEAE_K@Z -?deallocate_bounded_queue_rep@r1@detail@tbb@@YAXPEAE_K@Z -?wait_bounded_queue_monitor@r1@detail@tbb@@YAXPEAVconcurrent_monitor@123@_K_JAEAVdelegate_base@d1@23@@Z -?abort_bounded_queue_monitors@r1@detail@tbb@@YAXPEAVconcurrent_monitor@123@@Z -?notify_bounded_queue_monitor@r1@detail@tbb@@YAXPEAVconcurrent_monitor@123@_K1@Z - -; Concurrent monitor (address_waiter.cpp) -?wait_on_address@r1@detail@tbb@@YAXPEAXAEAVdelegate_base@d1@23@_K@Z -?notify_by_address@r1@detail@tbb@@YAXPEAX_K@Z -?notify_by_address_one@r1@detail@tbb@@YAXPEAX@Z -?notify_by_address_all@r1@detail@tbb@@YAXPEAX@Z - -;; Versioning (version.cpp) -TBB_runtime_interface_version -TBB_runtime_version diff --git a/libraries/tbb/tbb12.dll b/libraries/tbb/tbb12.dll deleted file mode 100644 index caf0e5551c6fc07aaf65c1b629b9a76a41fa8b0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 290944 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

#c0NAl5SjAG5TBW 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

aMzT#NZ{uP;m^rnYj15EO62k9 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

-aKDBr9^BC{xSe!xqL7QHmvIRztw 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

3B_xWvq=8d6hfeByz{)8F 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 L9xEMYZsh*~X-61> diff --git a/libraries/tbb/tbbbind.dll b/libraries/tbb/tbbbind.dll deleted file mode 100644 index efe59ac4567d192463c6d643cfaa3aeba61bc54b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47232 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 R49noRx8Dd%(|_z>_zx!pWMBXQ diff --git a/libraries/tbb/tbbbind_2_0.dll b/libraries/tbb/tbbbind_2_0.dll deleted file mode 100644 index abf0acae8df53ac342a6d28472a04b8b21665e51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47232 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åy2A*IYYS?T0YN=SlO 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 j&ghDnVNA z4Mzle8_mNUM;|gEl)Wc?pfynWicdamx22*5II6N5b$DiNdow&Nsh!jET90FR>LzOX)$tnCIoERO zx4JVDa*YaA=$EK3RK7x6_rs4i8l=xD+8GY(s@#%IN~5=r`Lw|9IMNgE?PWps~hxOmFChj(IDb6nb$ zd-AYR0?`)(Q)WgM@_orbrhhMcOu)cp-wo%lofvNbs-F_hNo?A%Q{;Cw1x*}C=cRd` zi2^r4^Z!5l_UvKD4GNyI%BhjiJ@dmQ?kd=bc*J(iB7}R08CkD7l3ViTfWna+EAT zYczNK9IzWFghI{(Tt8ZO(9vPq)18Az-I*k!r$#V?*~0Ed8a!t|{z4JbPym&!<}z1p zgmCT4Kp}<(+N4j)O5j7+f}*YWM+b6mE&hVy9L>7eMok&(8>zOD+6;tD@7j)%5wYzA z03m$AnaLN$qS~T9gzK#q5pW&4@PAY=`KYdYLwWqqNIm)|_=^@Xu*t zTHZ(^_EGC5eU}E3(UC<=(C*qZF>Ga|*oV^LO@_OpPmL3TCF0Dj3Cb)rih_qZVF92*F1) z2a|F8&SiZh&C1z6S`(^P&}vFE=)6{h-_%UnQl~y;5oK$V6bVI(A&T~6xK~+8sQrtX zQ!Y2?9X)^H$>Y4M1X0psPSrK-36E_!bEd=o^nkN{r#*$xVPy}dc+~Qla9-N?$%bPC zU2WXy77U}ar1>xXS6YL_kMyg&)N$9oB-=4gX=f8D(P!x1B5&9w=ztb!zXXhb3=c|S^#H!zz%*QyluYVs)Me*kt=*r@;j diff --git a/libraries/tbb/tbbbind_2_0_debug.dll b/libraries/tbb/tbbbind_2_0_debug.dll deleted file mode 100644 index ef8291f8c833edca7899444e30a3dad15f776aa7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125056 zcmeEv31C#!)&HB2fq;ZWG>ZFEgGL1!7By^k0*MZq=!h(_qDe>sNeM|zW*{gEF`&dD zm0H_sYc1BLYB#JKpshhD35bf?sIwaFXeb-TU-u7POb33hn zkEHK>`Xi(tNq_q359GP)>38J$?9*@IIbn!;w*5AF=i5lX*ZAzt8}PjF(49}eE6?Z5 zXZB8}-)k(A=Y@wpBl$C{$|_0AlFXN{Y30|aXg@BiEw}RaX(#E2Bn?Z~j)j2tsoWEf z#4}yuE0L?>Nt!kg(E-{Jc}l5{OeiyON|KhOl>k&#vL4ZCm9u-DOZyY5dj8w+R7|E)04Dacw|?(v>ZI+{nepd6Glyl-7d}Jjt8x2WBe73eiW}c z+GLrs(Oq& zWB?AbYDeI?CJN3orb@xxNnJn|?QlHrhyjKp1Qg6Nn) zDM&mV3$CKN7J%!B2f7!XJP`#q4W<7h|90`O^OOae*7@ym2&#XI;QQww_~<(be(^m7 zm6szZI|IRmix3=jJc9BcBdD!MaQH^W>z z|J;FK&7}w$&PPxKy>)ipiXizR1UJ2Uc zx=H6V=)RpP#PSxbxAV&x2qvNU8?TTbqK1jL9q031k+AH zP^cs57=hre>k*v8*1xn6K_2r~yoO-&4g@2qcH!^ps}XzYSOjn0gW%^BX3*sbt|JGR zQ_2o_*UlHWA-Lk}2yVL_!P`GWa01yqeFcL2ECl<=?ggarJ%TiiL@c55Nsv)zhrF2QUt#*LQu=L9KvefCBAhO|7jZQ_6`KO?FeqC9Pf=ma5Le4<44d) zy*$MBeKs9I=Wh_a{VIam8xXwD*vQQY*3*U#A-gY?A}Fmx@bgUwK3jv}FKp1uNzx#` zyD|~{_ErR&sLpbB(-rjycFsZY33b&*;$3XjvyMSGctw8Yc zp$LXijw+&`MRso@f8UslAfE*~g9`!(9=#SpQ!avCYY^P90KqiUcqSV`3MG1oy1$Ur zI?4Dfs_7yoPiMg+QxL4As83VCIi&IJrx1*you10}_%&PquhdJ&%Lrb)7(oq*?_e^M z@;>)R1YayhFqIwv_S`pmT zj$r3c5S+=DSx+Vpxf#LtDiJi1_*$}Y_IL#2wjr2GhnK^=G7>-jPz1MKkKh(+BSfxV zWvqghxaU_0j$t!?N(8wy{@2zcclM$Rg z0fBoef^2H-n41u6r@Ujy-eqZ$#sO|2CZ-|3|L z$oUBN(YP)n2cH%p7|W(TmdPUzM{x6n2>!JKK_fMJ@^2BmUVvcmWeB!0_<)|_n?FV{ zaxenjgW!Uf5gY*>$jI3r^lPAB1N|E4*Fe7p`ZbV%1`1jVGWCL%yvz|Tvoh1YNTtm6 zHt+R$ga6!wQCZta93TJ88!X5y@wVn=HhKZ87qk{+))gRG>200Gf`ZI!<3tSa-qwuy z*j4ZbJB(?kBiX#oSG?89hD9Kq1d_u9fL{*+b4zk2AE6GCX`Yg)T|1;bFEiN%$gUK| z5Yd{K8%F6=KZd~fL&cjn_2Ub1wDr{E=DAjaT$|Q>fR+S>SfzXBa3?GdYksvL6K|% zZY$Ysyf$;CWV^3j0c7L1d0SRxRw5^NBez+`zo6Rr>$}*_t1{OR77$)M3xaFS=PJRm zIW&MkG>b8xKzr>9jE!fqf7e6MAS@^w8XROQI`bod26rLn6YynR$(le+7QEwk8ISKn zx@j9(x0^iLbajfA0z$S};DXKP(E^9%k8gukb7;=lM%9fls$--{kj|Ot&GsjcZ|fSY z8Y$?>fj)JclCt7xdT7aiZW5R8yUv^!`_s>E0Xj6Q{N&zHbR+LT_`gzDKU?mwMEg7UhJ;GE!I zV+uH3-4;kUOCUb$c0H@oQJg7#M0kyuOnK33br6yMqHj($>3NI?BiZvGDTK(%L~nXv zkfgejyE?|VMXSYjDbgMZsK!CJdW~ukGL}Wgq-g@j=5Bmfj5cHck+A;O)NjKoLm7X< zCMY-nTR!9M8z@CVW)}M=lxH{c98;7mYBvjRg3My@Sy*pVLWUYYBB6B(TTf{CcBh>lNE1t%Xr!N&xlgQI8aCI;*(^2KVFKyS|7qJuck>0-!lZHEm^afz~L&J?D zz03%D>pJ7oi!zn&GD1(;MjG^#hnRyE>?jC$?{X?OYl zNWTW+YG93X7Tevf*ok3@w9vb{U|}#$G0b=0wGzGBT%!12WtX+^57=uZdc`l}Sz<&y zMy<^^deWh4Y|~cAKwsZx|;%s3T&o zPT1bd%@s-PwrpCSna!izdd#@ltVe>gt|REV;bP6aiuaJ0Btvd24;sm_J;CyI8v=>H zge_X)ZEpJ-2MCxBQxg$eDq~hQhj+!tht!^9_)F?qglYa<9L%agrqx|+#`XO1J90N- zXBoaY<62~z^W*Q_XU+J*xiy8#-9$i)yzj7fYevmjnW-F!R4AhgHO9AL2KQYFSCc&q zzhJg`&mNNXY~-}qyQoc=^l)l`Io{zeEBj0G?(uH^!u(?yk+7M4;UO+kVF#ItF@FPN zF-CImApC}A!8@9S^>KfsK91i*mQ#%DUsvLp`ZytuKCZVVeCLe_eZ0gZq>0eSV`gTF zp<+&b++)^v>LcZ=(?^s&Htk)>K>%Gq$J1Uct<7A=Yz#CdSj$2Lm0`RrdI9{}%v%Yd z-0PP8Nwfjo23urG0qZg=Hqd_n28uLuJ|QZOmI1f%?4L=4rJ**{n#3KLpy!*d0Hw@= zVL+c*#x%3R&=d1ichu2o*CeyLk9jgY6gf+i*U)x9H!D_>ll2h7SPt}<)(|-|iTo<| zYX@G`1X>_78OoA!%{y_Gs3CBUT{4U%fUUE0?uIV7O8`c>OKSED6Vb21OxF?{w$Xwu z2rA6dhfLTnME4X{@e(C=qm4&;H^?;>1a}mmPX%vL&1LcbE1+7}C7TzR(3#^R7&Ix} zz~-|OZ&Ufi0srT1g(jM|t%TMbPB(#Z@;?P~u=YJTj(rcH&UITSN77!AlV#`p-89j# zeQ#inY2S@K+jq;W3Z2Jc-xrxA&?=6G?noioc=p{v`+o6{3Rxw2TaT79o_VdieJi&V z#+%$>+LUPkC;SOG(B{$h?PdvB2;28d(tG0A_fC*@*!Ke#Y%Kd8YAPv)eJioU_Pv4% z>%qRafNT%;{Vx&El=TR~^qnyM6WA&K&GgfR{vyyxr?G~K zryUG&=);sB8dKVePKP%fY#@goo%4}c<#}tmZS4bS1T7uyje`gbxLdp!ZR0xM#Oe=- zTi?lF^WN-OQ)A48X;zb4F?B- zq^_%Mkng;#ct91Q+}eFSz6m&F9yiM|39S>lCY8mpATDu2Tp)=8ZkD9{SC4THcNrCp z>R)~AKYL9pmR9Bx#~4c8qw#y}KQm|~mhHHW6P{;#b(>S)!%QFw_n(Rv2xj)5n@!7( z>_2N*qmTXP0h3Uy{&UkG6cge8Gg1sAPX8GX@Sgk6a#ZiJ{~Sk(eds@XcPkFV{pU~8 z1LO9eV^Q2o|H&0aql4M~NAy}`cd$m_i0VIEoxNjDEQoa?o7uyZqxc`*f1Kkn+)+CH z)(AKRYeLX_x$M8Qs#J-d|MXlQ`#ig8uk1EpMw{uJ(W>!+jkumYb?h_Ba?pd;X5Pn~ z6U>|8Sn%v5|XQU8^u3)HACsLMzm3w0*2SE#P4JTMUFr`yFK+u>#@n5En( zZ`SvWgVEnV%$^t4U#FSl)SpIuIrMk0>1`Tg=x;rA-3R^UK;2IL-6f>D>F-AB?|N~t z|1tf=*$HR+!#CR-(_?5_&5;Mz-I_3V`y_~eH}%sq{+RpQF^OGUOr8;nW9X7o+I})cCHNMALge15>pQ1=-$L)b%F})9I(Q%eSqEn}TjVGE| z9i|03+s|vyO^}3(Qai4gQ{(y_(z?-hx0+dcd!%bM=vPnQ(?|L}j}N_%C*k#VoJ&5N zd5x30DKdsU9SSw?Px8-K$|^EG-x&(wM07ItwK>vgxtrV!)d>`a2CM~H&kM(%_9)Iy z{fFA_-)F4Db+E1@b2k#deF><@_#1j{*Nw{gkKT__Wrym+UBfqnOULh894q4)`W8Km*{i>G(fN&y!O$n*!S~H#~ROO z;QPE}NjHXIuY}k1&;w0l-T$R%si$sj+o{t&fkZHHeZiqDJ%&S!Jzt8dNrmd(7O%o1 z=7|)rnQZbVAg zO~z6a++Z^c%9dQ+C`B&V7Y5;W^AIQRdD!V#iKj{XVx2zhl_$(DPzTpl62rlVoBk__ z+5HLBV-5Al0|e9yCr_^Pnwg8{^tibnd2PvMBy^*m(s3E!b;_^%S*D#0hN<&NLgbwXg0X&mAZK?2}U9lqYlF@^6((LVWF8R7fU;g zUrGr&X|^%K;t!39g=B|u)@HM}_+W9;C4sTSxZ0|cW4!aYKyb@ORT;o4ol8S_r)P&% zhF-0D-@BV4@pTwwiloWqY*Q>eQ zNBODA){K)(`JoNSj~cP%e}#O6n}x-T+s1Z+i%wH21hW@9_U| zOiTQP2TMu#3U6>mI;TyD%#?$DPhn6agE$~U+7%p57XkKCjPG*XPo(7!LcW)2X}!r8 z@%|sUk8wv#@vH?kdeFe&_T)^?gSrMGLdTQ#XmSs?VIuIyY#FL9+o*+EfKX2J9v5~| z(>520OJhmFuW2xBv)*K3KS`8cF_hz^x51|O+Jk+e7wLa|RAAI+4?qXLm*dyhC@e-7 zc`c?yHb5bm_5O@_2-sP&i;gMB%9ht^vK_D0Og!9$+aP=)=-j>GPJE#mS5E9I%F~*$ z7@1}#zRn-B6B7_l?DwqQn(^nq@{u|asZd4{YB)RbB04clbUf*zEWSJgaWIg|Ei)M+ z@#2&>y?zK^pz?lgsbccZt}v7Ln{mbDD#c_6GEFA`+z^vV0)ok}DJF9ilclH@%6JGh zoJ=kgPiymrouuY}+sj8iIcBrWoOPkmO)GDU8O~ z{R&S}u~g|`5;4p%Qdlp?Souo@023t6pPB$Za~D~VwGc*%PnTGS@hC|HZIQ~pQfg^T z$gLmwmcpfPVNy2oVE}#wE4D=R@xE|j>f-}+M~6N>m)_Z7)SKeUtgOy>K%9hAA9cTo z&_|025TTD{gb{sQDkM9MD+w0X$0bs$n?5GK0exhs!Q-G;u(m$*Sfdu1%Ql{-tZ|%Q zy!{Ff(XpS}({1c9V{EDe#!6!vFha^~zUSy(x{UAEXQSIc`9nCxRZW={=&%E}g!XV- zw}c~_n1|0;1$A`dLvOO*>nzv_MRwjEqu>VUwX-=!!IeM9fx=?Au&te8?+|=X#g%vX z#En#I#!dj3-r>pGnBIYa@D5|&7x<%zcF^w8C;tTVVcMf#0K393Z17Ex(ooOLThSbDwKdw=pJHf&Gckc z+GmwSRER!rh^M5Iu#(cLmI3}_m3BsUjSrh>&i&9(5E=?+<j@4BE&x6MC5!UrmmDd zi2rMcBEXs0&T?qR-a@4@efiUH1~gfu1qO|4>-@f5L5JSBVk*J~VB-PA1x=yE#3ghX ze_{gdqN}ohvId}V@{*!*-e(Q~vQ%>aeF^}{!8XixsU=+EUXi-Vidhg@yo@X&hU1!) z4LmCT>NY0K7D!m18RmFmidtz(p3R#AP=J_;_p=Wbx9Tl7=uTfE-=Qh$Iy(lOZoWgT z^us}zGwcSlhMRb^`YLn6@(f^8gqv>sf2lmN&->ziU<{^GVoZmVWHXc)-=K`m_-Zt|i7~(;Bfn3%`5%j9vR=#w^<{F;Y#1iD{M?o9!eqt-8oIYto>W7P0B1wQx}K2i(t2xTlmje{GbEcsym5j$^?&jU$3 z?j>BOzK$ug>PoHdd;hHBYEE*8lHwfzm{Po25mO2RLW%;23#ki5nAGd>2xV+Q4O@!r z?mL7%54Gm}8%<%IFGkjnai1fdq9%hK$J81Y4`+=VRPy=3Y_ux6JO;ub(z7yC_N6^| zDDsk#go{rO43-OIU1{pHAPbPmEh+Mak%+^}7_9g6)AOyZ|2F-^^RM0g0j-4&p%G?+ z6I3~^#gLK5ol^6HP?0eTJ`uZPgSNwIxVqeYVEc`0w}fX#iS8$opKRmzKjr~{o4KjU zZH5Q$gsW_hziHYx{M!2N_#f#7zr9|LsX)=9aXh%T_f!6Mhw|+{O#_I;Ox_>04ToEH-8_MUQk&-&`E{@-sqxcci4vi>y(YA^JX zAN~l;!{}?`8I9fh9p_1L?4S5vrZB*Fd|&W&Hz@iH(168oh8`H=HIHk$0LV-^k4W*t zjJ!SBk;5K%&>cBBf?Q^Kyjx{p!eb1eemJg?Cy$XL>e3X06J359c$OR~r7%WQ=b*-= zZ|{@2MCe~neTL~@cPrKV715Xd*F*x zJmYn?Pkj4Ov(`Ai#HN3M^|}9P`MmBTz#6jo&p7vc*mvgaqV&6H8rh6q)HVXElrb-1 zwm+bQUt}voc~Fkad(f7{9=j8x-m@hzMw3c22sB5}V{CbQsP7VeA9($}5cgo{KX#D( zkG@6db1NZAmkG%)&K9>`4zT`S#s2}uK(CU z@;~}!@*myybUKN&|0v=A!0Z3w+n|r}?KM8*@L@T?MEJR#`T)MQeZl9WKaD!7=jryB z_6ah#w(2$yz6fxYm5nPn4@oiqgV$G`QnccmY(80V+>6_OYRi+1jP^c zDW_1!6qZN(kf~b9MDa>(@gRLAKfYdnSUAg*&Ry-g21`dN#uYRl(c4H9EX6p4CaJWP zVoW7~(j<0`TwS-A+HdsXhLs#izq=jUxmn!E!6*kxJ1G5xE|T`5ex9ZPUyXjwu~gj^ z)=$?uhklL|dlyTK&`%NAQTloGDd?wOb<=||9_Xh}^BbNQV}gS|q+SKW%k@3D8ZlYN z`6%k8qt!=*KP~`>#LH2A1g$3h2A89|*#5)A{@(pSobWzlxT#N{aT?VOn@(GGCp*2A zE;d_MSIL3W5|~?u4-I_Atu}_gnb27Z`jwlMS!Ao^s&BCwqc^qRZ&mp|o81b-?`pG2kuzXWL`(x?nA8RcPYfWfVKRXGH z%BFtynR@8H>3@t}|2Bv8!qPn~a_Z;QU^wI8^%KKCK+t$I@w>rH^Ti(X`KMR9gA;zovW z2G47J31m)LGLSSZNG{v+h4$_A`Y}E8tjD|nBy%2QOSkm*4j_Ry{7Od zuA6(`z)XbRb~Zl^Qqjw$+l)bP094lNhH1R}qR@Wq<#4tvhp|4bGX`I0zF~zWE^m>3 z-ejglFC|7N)4{cwD`9Ea^UDH|{8|&C@jde$!oJ)`-py!YUJ@MPF5oa!%tQ#UV1UQUfQd9>JRV>Nm^juCcaQ$9NKKP99GS`{H_BOTe*6HnfY)cCHgPju@`+v90B7$+KXVrXn|b=0nv1w`IoM33ovprL^IJj%MZLfb zGIi=LvjP5*(3Tf_AL3K%OxGaadB8C7Gqio@d*B*}&bmv!=(@TsZPg!;Ltg9PZz^qH znPsdr1xGJ}As9I;m54d&ow;ml>jcHzQosg5w(>~0=!exKa}lH%cOH#nuV-um=0|io zjA7tL<%$Ed^H-aWvQ9>XD%2)EADIA$TPnxDV0>Lb;IGUJSEiu6o`?4>SA;_Cn&_bU z-}82|-5wAd?Azof*T_O!~JkMfuX@$Op0#ZF!dbU?6iL&lbqD+sFf7=0`n|rt581;FTsg zxL4qa>^3r@z-KA&IxX#6*nrdsU1X$@6%I7WkuN}In;#4k#!`$SoDkCx)UQgVt-{Y_ zqAD^9f-5&l-2925EaT%0O#`YNq9XPzV+V3jc}mxC%)X|myvNNv2s$O?&jhNRP#b`9 z5UWFjIgRaasIf{%`$nCrcj;xuT9D;~eBF5?N+UH;!kl`TxsY ztfzhvuPokr6%xj9f>3d`aTy$kvwt*UK8^0b8IR3hQzrN<;)VxKnC&LBpX}g_5_PQ? z?W(5QA6>ESSIFZJOyjzDqOJwz>w2s3ve z={6@Yru8Ip!t2vH!-ZOR1&94-4{ZM+M3_GIQ~8$Uq`pg40m;Qp78e!!oa1absC zpANxf;6li%Soz3i(cft0-7mIlR~`_6FF{IulYm#24#1#e@1HbJot^^Kn&WfMX3=;R z9-C8@#;-x#__1*7J_?|5DM#u96QCGZdEr;HTub`+qO)nF_WUk3YUoE-s#X`hzkQM!@wjOTW{+I-6mf6b`f8=mI0Wst4jg4#Uygh7ZfJ3`hn`F#DM@(9maa9*};lMC&k!5RCzmComvt?_3|~b4&(lMaJ4i^7}tp(bTZ9n7AdPL zWgal77Hb~P!5$VR5*g*_0RHCvA^z0%cTpF|j-!>`E?tG=4vw^{X8?|U*;%GF{-VFDQn-H1rETpYI4l#NhZMm9e-watW{dgRY%|8|?Q`-?Ywm=m? zkcIDk-D)ug4tEd=QoR}bKU_c$j+>Z%mrv2=BjnJ>CZ zTlF)biqO{-Ynel}X9qnKfFy@gZy7=Atf}`7vumPJ0p<;^*$6~N#}IJDE@jS(>I!&u z0uTU8H=ilS>jEX2DaJ!c;DfwMRnI{bsA@kRn}4oUbvxq5_t*s{*cyHh-Vg&qNq658 z_EP_QVz&Q+X~dfFa>C#@+{W28qO$I@rD#1xj;C>m za{0dly#UrKB#c6Krsi0)-GuS|LY%&d@2?5sgIz@~{9$}P&Ubujj7Wx;id-uBjGxz% zao)*ky9>5;Ea@gbAB&WyH~KTHqv@xL;z++_J?P&K7W+UyV!n_aYfLkKLIKfj(Rf@F z7l58~IaH7L$I!{j9ziF2L(R5UAa0BWBj~C!nfJgAy(@gCDqYBi zz-xR^#r;W$oTW7I2Gg8zqH|&H72fm5H&CICb%{uB>ATlk3$ZNVRV+VNx0x1DiBSs{ z{5&?@H#aE@I1WZ_)QX~DGiul|S3Nn#VpJyIxV|K4%+cmh)e&xlJ#AjC(7XhcMi$Y; zbY?vnl3A9Jy@N%yr~*}RCRW~Iyns0#hC-PZn&p^ZS>-7PKVFS;=b{Wbs@&T1cX$3t zvg{RKC2UFce*8qK5K1w=gKDy`Cr?a)ye+vhOrsCr+|?}o2Z&Q^EKToPX1w7@rx?#W zQtA0>mWJxww==1n3Osg`J#xP^+28Qk+&~UnQ^!33T|cpyeF-(tWa(yA2aYk;id34< z%Wpg%Xu~AXb_9vJUNu%w+L)99-|D{MbNe@5`m|qTOMExSEl10V{$GG6JqM-iG_S?Lhq8FvG5p2q7)wEa29$(fk8&iLa4@VXjrXu{T5ou z>G|bb^oOv0fvAiI*+wNS1(ma6RR&ZFkA_$8@SpBo{epjFA@&hni*Iq~Rg=XhK2 zf_?BGX(xEJ1q;$nSOvF2k2NR7TQ+o3)AG~)$^INT6pNQh$@u(H)08Y`_~AckRKZ!3 zQVe$mY-OvrxjlXI@yoaIM7fbh`AJa3C1t@GK$N!Jz>AnMBte&!9%PC379=(uT3%O@(Pho4`TH;znCYnqZn z)Qanz^MkX-r5G2LgX`@xnzyG<4rt4_6a<0Bi@;l;eidV@Vt@6Y{EOG&@_KTp-3u%D z#EXo1O_PZ(a7@$Hhf8F5)72v+GPvpLV;S+Mi?7W?M-RSeWG!HaoIbQ^`3T`GvUG@) zUX~ns4!8sS6^^Xp%VM`9Q6I=3yz8mwXqt@_>B5LKy28q-!~Tw@4~)*;!~u1?JM^)p zx!~R44RmB^<6juR!^oUZQ95Y#onsk~Y5r)u9EFCJi4J(A{@bOo>csMIVgPjG7}Q`& z=1XEZMlGnyhXSZ5TcSv*8}EV=FOA}=$BC}yscoNu zLI03VgYYosz$V+>4fOhBag=4nsLWUnY!Ju0-!HU;^2ZU z{A}a+;&82#05KP6u+{J2??ZlE=4oebcy&#t=3b zzKb$VY{__1=?aizX>u?o8TG_!F+atGMWzMzFT0>6kQd5$c8W_2y8r$GcF5XV+k6U*)$OryZnRrND7=wwU@y3;iP!*bq*lV8frEcaCf$$LN|~f{xkfkLJc54 ziUv6oiZHPhxdQBC*;-({;Ni6%5*iMWh>Sd-jmW^3fbfbgNL~j7&hEy@@CZhdx{mBt zUJnH1LblN5G5}=7;qbP9m+6QJMYRVG3#WeiQ}7ZPB(}?!h}`cYgJzE<18yZ)BaFCe zJ(TeZZga#Q-OTr+oc;}qd3bf8z(_@p{EsihBlkEyMbw9Ob6f$pu>($^8?GP!t4CZE zIKqErnLor9=H|gmfkE`%nwl#@r>JDS5i}AXy|O7+9=el->9c#OzFsh?9xeMd9MG-b zh|73^l|@yDJD9sWL6mX%`vB)P7JoM-h)ndS91c##GU{y3eg|h^A)D4CvM@iKw_;L2 z8*feh(LGKL;l0QpUqVQS8S<8+^aEyM5s4E4M0Oh&PoP><59wi>IqG{psDcllq>c&` zOqbh)-3X?W7~Xj?e*r4)YR+yi)kHV5iJZ35t$ft@c$Yc3m!WF98gD4F067JQ#7uH7?;;_ zlQX*x!J5E*7wWboV`YVZ&D%!A9o_Br-;6aBltq^}Ofed>1wgt;HX*QeQX+EE?`C5j zQ^FxpccD)hj}6kamAs{v_T4s6?`bw08()r%FA4b2=A$?cWOH%hF)mFUJNB<;&zYM$ z$J?}bKZatA6x-gyWBfL6%c{&h$O%q;5gGZxeQFtv!_l-=E<&z;A#h|~>$K$H%eb`H z{!<9`7wq44z5`n=2%bOG+p4aCds}YL+z0p>&7GOv)^A|^+3Iaozk_Z(C|iQ(0ygz^ zAfTq}fb9JH5N|dBZvHqo@++}@dn5B(Qy&B9=7D4Z`OvUMhnr&XLnb1e=cL|=cpg{J zg!B+{H+vEkl>-ZSTp*>-x}TKzJj@eOs2e|p6DTmjb2nlOSPQ=F4L)ONkkA`c0(o)I zoeJ`f-({4|f@L&qqXu)YD8AzIxW*Lj_6Hnm{vvtl8Q!v|pr}Kqg+lqkTbf3!cWH&e zwOiV*aA~>CUxfTwdBLZ0gD-UYpx}aF>htTNF*l^hm3z`=;dm4#z$o`q;70wK`dk=E zHk1J)`SF_+T#Q6VPH^hI%m}{f4Som<@ds8;()^cV>&EycHPqZT+B;PP+|qoWBas`X zRHyG4Lmpuia0!xaJi(k+_hpfA_ew5+x&}e?8O?tiu;v7B^X~?DTUX$6;NM&U`PM9M@t`_4pMXmcgUb1>$&J&uCx1=L-JU!M zI^UL+Tbz4&?!3NkNMQ}0_GmhOzG&48fls|>Z1ILVBCN!7$?Ulz{T#^Ow0FZWD&GAq ze&10U#5&~UkN@k8G9y3uNn!Bwyx?EF!9N;L(c5Ep7#mB6c!N(H6EIX11o!1ZGg-Nt z=vxbe?FC5bn6kG#oOvJ6Z0h_MuPRLBee1FgZ`0>o5;lxSA2q9ZTUTYS!-W7;Wi9kw zv&ukLX?<7HFjM=V^wAen1W}|N|y8m{}4V3V9y@- z4#~qx-?-=^$iF&-O-{5uW=wAKL*eOYh(fBF*Jau}AEim57@lfXW*w}UiERF&x{tJm z&=a*zeG)j7@}&0o7fkWfz@l0EcB#F(P3jZb%0Y@?n-Sd3yN#aQ;I2?c(`fY}PZu_FRfDpN(mvQl z$50_n+h{x3aO9%BWxWBdyk{M?(?9JbgK%2+qlrFBW4i|8tX zQ}&}BuCx3#itlipwaf_qk$!V2cfscv%dpUd)12gRnhSmy;WWQ<8qL<}G;d%|tNX%8 zxGN>sa++1rSZgzLU{30nZG$fxqe#r*Ilo`X^zE5m0HJy4Ko81mQhw|UR&!I{pE-g) zGkZqxk282s#Ep5Q{$e@;eB!bo_*?;IGSKdJZ}1PS2OozEKX@OCd+eUoZS;0t@fF;? z_N95Zz|X}!=~7K9>toq;1!eRm+-l1oze5bxxQ4PeM;>axnSK6e$H<8Eht72K=gCXc zyk%S9JTAouOU@}-gG zq?ZADXxxdfqTPZ+SLU`HzB0IF>qa_F2PGx1JzpKaBb4zq{5G#Qh$(9*<5)yP8K)rJ zJUCf{DsdL3vlt^q6cR^ou<+$eJ^szQoDOSVlGg@{-PPPegX#wQ-8vm z?a40qN-dlpOdbA5A}&bQtxSA*C-sw8%uH0=gj;mRnDe9C+977bQTBg%nacR&Lc!Qg zdzfl+`H`j6we}QIR|gZbJp)X;J^KciEPu>Aq6K2HXGf{!<{UuW{(+(aQX$$5t z1tX6JT)qfKL#U!44nz1H01#{@vs>I-Va@IC`w=R^?m~=$VSC7{Ey0?e+u;FTNA)6C zvWG1h0-a$-f{~uu!z`u6-Hdup6)r+{FbKD|fM;dGnI>@Q#e$a1!&-IrPvL zoIkc^OhZN49-9$NZu7Pz@9f$M6)yh~r26o@uf}*{DBbE`jda2*!D}evQmk&gL5$Q$ zd4ntW^FfJu{I0Ik;+=|6#w3Cv-$PY-G=Hvr5KpBSu?X`Fu0j#ZvJoPo^EBT>S-oDU z%12l6cenV0#-4mZ4EC`frwrb~|7}DS%|9nD9>}6v!z1@#J!C!UnuVrlMRZoaP)0E^ zfKe|+^J@O-5u%AeB3fl^(QuB2c@jk{qG(3}8@{HV)U^iAuCD^h{SFWH2o-WqD+Ll@ zPYd(AtWd^45movz4J_ktnDFs)$*`*M(G~KajhRm*aLxC#kd3uv4{9;etv;#iQJLcZ zLm}D-$(`I`9-je{;LV9$Vd&nbGD>b7_mek9-*yI2Pjo-@A`XA>g1-9!M7*EvM%TR+ zv95gM%`ed_Xii=SJd(I^v~j$JK+1)o7I!5vNLvvgY1}Jn|Fat=M(YpG73USfwS|rQ>E?hX__jYEflr}6gO+98 z+$|us@$Mf1u$!h7PJ90mvZbdzimLn+{)-)!Ow^}wI*{N+g13*4MiSm%rY2e2Lrr2k zmqD3FUV%&x|8R4JKnR5FjmhB0I}FF*(yen%P0mP!1a2`s1uraA+nDywTU0-7t{ZUCCWfz(x1c zfMM0gjER1pKQ3A^CbZpNbd5OXN*>qNRYz_y=96t4t3ReqE&K*LEsJI|N2zUSW>{=f zoh_EunsR*kW{s1H3{x0<-W0_nm>NNphhWMgD6uS#+43>P*;N4sMrOl8O#^^@USlG9 z0G8!su)xUnz@lVt)@%+mfDB!!KFsW(P+IdoFMdIR`8ws+HUDrjrc?V_f#eM?=ziNc z>fUP^T5NW9iGDXtSKrY$UIQat!;L_`q{+`9V+{)9i^%#oHTUOQC)ljigGz+n``D?I z8xSJ$k;c7Wx6*@+7GUZDh18!86&WP}jOHtRpTzEYsFGv61d>oOy`$F^IFZ_tDFMcQ zjE(H<+!Lh^asGE42W(vILtcsDJw}YLM=umsuJBhS#QekHNPqq^9OcPC(KXE22Qk5e z+Zd@(#&SWd4}JauN;gIUO?T6>+>^5QLEGKaK)_|3O!#xpvDC;$ILgRIYbrG#0+bg6 zTMvpgMIin;T$NU)J?b`IQmdN{uFg)FZ1WSDEAi0$hnq2ct9p|g8HNvSVKgAX4TDJ* zSO9a9)Sm-|apcho4}MPTPJko(7tGee**7A)YlXLc3m0+5%M@IUTZ|ep@k~7KE7M^1%Rj)C9v1d^P!r2M;WsXlsl?zcYpc^C{v88vbe81 zg<%_T#w`C-MEWHb+Y8Q60zxdzn~bynVKo#RZYDvcB*(m~t|T$PJeOq{pR3w9Ot*a= z95Hkg^kj~NCRg%G2r%I{!n*k6&{WimBQTj>V-A1DJ<3>dt}h==1IDDTp`3B2?%;{j z4RM^_8eC7eaKM7@0W7Mf5=RcX0!K&i>P5D32MDOxV~9ofWW|uBKeS(J36L3YPIU?& z3XGx(ea3by$I-ViXmP3wh3V?+F*?+N&dl`KE?f=gEu+@dBZ%pS?p3m)J~-36|8%xB z;}*X;;I{)4I%BNKuX5C(0Dg-dc4r(5;$6pe$s6J_qNBmF*#q8gX05Ixz;z-cr-L5R z{tYewd&=?+c@bT1U+CymJfZ{--Dgwi45c7w8wdM;yL%k6_-AL^VzQjucl*%q)tY`I zSOvvHu)@&>Vy)Tn%xRmj{h=9uoCIE4SII3vwN}`T-7~Hg)=FL|V+Gc7Tp5ip_j`Ta z);$YZE}fTdyZe%^jzzNWX=Dt#GikB)xuom1?m5ww}!uGf*b z5&|fmZ$<3m2ijFuhNgKCU4=ZZ&B?okjdSf#M(byAllxr({v?e@U0d_W*ZV!Vu#{_K z28Sh1b}P9q`!?#*mz9Jvu0<3uc@R5K3mnGYlMCw}q7=JwA{5)SjZJV+dy#gd_924x zU(??8lpTvOtjxFstVB{R0kL{J2CN73GON{{%0to~!{tjhh%!VRPH4>1!9TI|%D;@K zb7)D;F*oL%dyNzW@HtfG?Vo~0(pB>#`6Kb(urttlv}@8k|Q?~3(s6GoAWz<&ceEAZd?gIM^J4iG;ap2H1V{&s{YMD9^y zK@r5i=9sr8EO;5uvyBJN?qj6KnFy{@1j(2wom*@f)AQL!In+kh5~? zhr1v_+G=bO;fnd&NVp9AcrLC^;PCJx6oJ8O!Jtu*CYk;#t@FZL@H>FW!U{PLMX;8+ zR*8JfV67Az*MSJiCSxuSC#vUe)CL3D@d{bmYSTZ1Q1kw|feap4c^(u<;|m_a!M)b8 zYU)#%i=r|-mGP!oFcJ2$x|)@VE~}-|B0dGPTHNs%E@-PcdWW9K}F!IL^`fYjF=I;S}#;rCN9Uz zA(&Z^l1FA8JlW1;QucFGs6H8XS~JE9>FppL%2>?I!Q;xkCkI(99+E_m8 zCTQ7w`p2mJ#saNCThNkMu$vguD#gT<84QgY2 z7&%^LHJy3toLY`Oj1S?W;I$UxDD>0Sn%3$Y%$YUsP8hGLqM0aqT8fgX-V$S*%AcgD zpNjme=71uVt+ z7S6hCz*e#Fh{#q^P$*-eu;s5fY^eeHDnAzn#Hv|VO6~0` z4|fYGLtQB25tRY6VMb9X<8EZ+2Df+7LEsh>YMFPKuDI;qdjGR;-sC?I?YRUqd>{Jl z)sI2V90)LR5DyO(;W+^hK6B*hUJWR8JKO|ViWrRmZ%^(>`w1Pgp4)1H0G2_hhDry4qv&`N_I|c*`hU!fpR@GF@IAn{JnWQpn{1w%Q zY~k(#*kH#{>y4pgh#L<>*Cb~~V$KctD=j1@=877BRTSp3+`>E?bGnT=-NKxXJ&_dd zxM+!rn4I=#awgpERk%2G#iOumPHOPupChrgo;I}&Hvs(n(O@%Of@>vyPdHu2G`~D( zmiq<{(ENHhUBYyFI9-QyAT8*g(uSEzhpWXs9uZ3w11t@g>K~eIl_Xgu(he;ix+-nh`pTtHc zXX3}6f|0O!oA1B>668U+6)}Iuf)CzeMDx_|J_OD9qrSKpjNWDljhyrj`oYLi5`OwN z<9j50LBgSu|B}Qfazb4`95-Rws-ecWr39pdQC*d>+CrEgTgd-%JDPgjNpT5hw zhJ>$5xJ$y{O1MG7pGbI%gx5>hDB(9HoG0Oh5_%*YCt;?9M@X0=;TIysUI~x?3+d_- zo+M$Wgr`WDCE@83j*@VUgxL~~lW>BBXGxeN;Uo#CNO+Ef9to#Om?z=65_%;(U&2`u z&XKT4LgLxAYlF+txUiGn(mXqH!gNn}-`_b1)9bI=uYrCI^lPAB1N|E4*Fe7p`Zdt6 zfqo72YoK2P{Tk@kK)(k1HPEktehu_%pkD+18u%Zsf$;ZCLwo**i_*`2zXtj>(651h z4fJcEUjzLb=+{8M2KqJ7uYrCI^lPAB1AW$jdOwrj3Yjh6ua#dK%EyK6*+qCMY#pv) z8c*YzaX~QOgFL)DTbN}O{NqdffCOIs3%bx}~^ZYl6+UVQtx5Ehn?e;R%aI)4J=!NLjhMjBsJ!dI;~ zd|x_ViS)%npW)iCsAt>M!+5Qj%@1B*8NZ_`hhL;kyJZW$Zq>Xk%(Vs0@4cJyfgf09 zKK=See^pOqvg3_w$igG=xy0)y;k4ROG?ysznl0Z!vp+@P|_(&HE765dTKC=8Ye18#t zc@=(DxxAvGtiGy_z$k8D>BxrEAtM_EO!Lp5UtC@>Kd^92d380iW=)%>&n>O5sj69M zfeYZQ`en${{k3{oeFd1&YXXZ)^~%zk^6H9ueL-!#o(pn1&|`X(b_m|3_j#K1|M~u zClF=G51^zXqW;%Fr2-{&26pzH;O~uDnwZ^Hgl{`fkuYo+jnYQZxyPioV;+6j)djBpt{!@61bL$rtR5kc5 zdi*XW+b{d;s{O3|M4xPP{&^CtUtCaIwn$ZxFcTrnMca^IeMeP^TJ+n@`usJ6m@thl z&0`ke+DET{@$jr$=7JAVf3ffs&#Y(&RQt7f^*5kCb{X8!_Umc};D- zCAalt`_RTb(+-aRk_77)C&sg-`f8C!Jzw6BUr8_yCv`PUsk`6+2q@P0fAe;=MDc=9h6 z{D|um{3tvLUxH@|f$Og7dZAb*MKXT$lC`nk_cTl!gVrvE#% z66V6vPul_VH@~!OQSE{SHbQGY+;+ji`H$Vb)UT>pf<2;g9qZV@Vtn163~-O_B-s&J zB6}}Y*b~tatJfA{e`K-jdSGU6^UBEdN%JsbF03zIte=Y=y#_t2G3Tt)<`q_z)z>!E zF7WGfsu}{N)%qp=KzUW|ym8rM&t`=M*sj73UupHcIn(FOs;#YVm{)iSqGKnF8GF{4 zac5_b8$YkArmQ+pUNNt6amC`=`eoMaJc4{wLhVoZz;i9Q#yTB*>)N@p_rpCR9p0!B zE0D9a)3Do9i2O2vZ2(d=vaxG)He!mx0_dax z|FN`2YRp6Z=_sFt9Ba3VKPRMe$6_yv`%Y&e90xqv2*(4O`&7LEr6?9kw8Ox(6%`ojp zwM}tteR)OH+JTk+6ZTQ+uU%YKHl{L)$ru^`TkL~-p0#L0+OM@Y8AIH-Sx&o{E<2rN z*kz?vRN$YoyJC5A2Qj*7Pts<2<5sxl+d`n?xiE|JZ&4noE2K2?8qi~+V zJS(4hRzBgZf6jcXF7vE7@mMgGu+0?1ZfHMs(7K{o0l!!VOQ2U+C|r;x{V3D1cp{Ve_4PPQsVyrD)Ys#{p*&E} zW0x}QC-5|)aw(2W^jzNzIb(?u3K>}fQjEie;j(p5mt`S@=%%7~}SZ zvkP-^TvuMvs4osQ`1P7vzdj$QX%!2h*CiD;lEtNs#XMqz-U(|rjpW&lQde5v0Q<0O z;XF(%q`bl}yJk4PDqVtez|#5E6^e9CZF!go>Yb?UWqy@^acNy~Rl`*k^|e`Id^LVC zjnf>+5k0TM@25pp)nG%ermRA*T>u$sMwithqdvf`IvA=lIA=`@KI)*(1hUGlI-K>_ z*UFh=BIq?7a}wUFp5QrK!?Ff{#p2=^3|BSSxy96SMR`_)CUD$Z9fqi?!3pSMXU~vw zrIb<}RyIWTH=9juZhbAZDOSOz(W}Z;nY1%=OaqvrX93M=l5HI)O8YbvS7ZBqOasm* zBRN!f6#g=t4eI`7bz%I>DP~S2emlDx!@W?^OgU(^<|%M-9gcsAPVo_|M4viUA3auI zzFd!4Y8@Gem4z4`o4Hp9>@~_#%fjt$ofQ{bzAi#p5vU~5qrY_agHgJ7Zox&Lc$ zPn&UJUhypN%>3NEI9?OugdE3O2;ws_oqoOsC(h;(F;vm8NVx_%<8DAdZqS!j`715& z9SPc9B2%7*nEqe6`o75lL>6yiZ`}ya!hHmu@bf+Z4*d1127RGi5U9s>fKqi|!7PHO zPb+B$Cvbyq^UOKRKU|v^Wv81Xt(9PV# zTrGkAF(5Yt=BsNIdTqc@pI2X6voOMunpOS-^K->gn8k?L|Bm@MZv+qS1W~j)1Nf*1PrON>%X~;Z7a&CnYQ`8j(^sUzJV(mM{CeP~)^yBitot0@XFZhr z^gijKZTFD_tmf z{s{H7U}o1Ws;OOCqgOPRRmdePvtXZDuj$a(_2!piuSgvdI;Ydx%EgM_VLjEWuPqpA4o-ea zX?>MUBTp6hQ}v~#4LZj6`am7#eD->T!8gU=k%G8C|H#vKJK&41zvx zpzv1*Wa62BiBH(zBpTyAW*pVa^oG(Upi|2IvIf0WUtr#plQq;BU7uA6hVXxY+h_nU z)iD&{$?)u`WZqBHBP*BcXu+UM1V8R1{x|P6UNOO1Z$M>oXRh zU7-lJBc~c7N@?{deOYZlZ>X#dRO7xICnIeAhT1^An(Q+rBt>YZFcd@gunyb`lyyYI zBIx;CjG7gVrHku$Em0qmI=HN|v|caMr(zk&gO`rl%?rdI|$2dGc01(f}(-MC0ok8G!0 zs1=PFwt1>k6h6NKYXVHT{icTPTZ9n^BIuai&AdI(6IspjJ2L}lx5X9u^vVkKSJ^#? zz&{gXLJh`*D)`0JgfZ;e=D!UuD1F#er=c)J^4IHsJIv3b@tC0Hx?IPo|Wn#AZgqbM0 zOUbX@Z9dH+y+)|htdrzn^_K$Xk<%p@4+!M(UD^zX&!-PR3b?i;F|+tK0&ct=>xLYD!i8WgzS zHd;7|M}vFc+)XF;@p3~s5^`q6!i(xEYA&c)mftuRydiyYprU@+oKnm!RF-gz{OM@k z3IIy_65QfF7v5_wW}h=5?L5S4G25o5NasSN)P834Ss2fCt2CR zcL_WnOSqd#?e_eq&dtgJ&zXN7utlCNgj-7C>E4zW_IUZeylK=E)KZ0cL&cedkEEb! zcVN6_KFv(l!*km}Qso=fMxhzw|nnmR_RDST2)JiF~y|%KAuqx3TY2`RV4< z&bJ~C)~u}lXTJu%Y7IEeY|Wi6?bd3ShP7s|WC*dr0oUe;vodj6fBdz3#y$8){IUL= zxkad}FLr5IyT$^I$YWp60j%}sL%cgLEF(?}%E=Gu#QJmQZbaP;fWw+N7HCA?0Hit` zaQhIC$CI-nX;2paIr$^qSbxskeW>fJacLM!V}VBG5u(xo*XW27khrWrCm+P${}*2a zGcU=zWYKx+7yfWSx_9-7`CBdv{ct_o6Z^0@*{Pd{6+2#W8(mg4Z*jwtvf{B5aUZ6* ztg&(IgyOR%6fZ7UFTc$L49`l7#}#Lrufx@q&$p-^j|l(q?>P175BoKckOo!{kDOm8 zRH+}RUjzR~HE?~m{q_H&YUmd`js{$ZxU@rX`Nx%nd7JY&I!QZfW=5Th&zsF>J)Q$` z@0@Vf^Y|of$nb%6N1cl2%>(N`+KV-*Cp^bmo37@8HoUXmxPf)9lasWxn-8gTWh7|> z$0gOdj!n`A&vdJ2J)U}!mO5^5ook3o8$!C>pP+kKeOjH%d-lkZB_fhNd)BgN|9;oZnT&4t{XF;kJYdzOF=en5d#N`Rs6aST*e}pg4mhpcF{3rSe3|ssMS(=VNVcGZxxcrEJkQQS< zbEF@6PtupkoG1Ch)BT|@BfQ-cnhv2(C@^cjbW&Yq=ciCgeZ5VqE_LiK81d}V5 zGsi2zBwUIKR~t;i=-4D4EDbqI!&z0(j3@C&Pv%%Q4YKJjmPz+grd&1VH_3NW9+Zn$G>87oj0zNmY+YXr_%G%aHLH17or|Z%MH`+Xp;O`D9MisSBY(jT{OgBH{tsj zO^KZn(j@7B2Nv56hfQtsUn%3deI;RzlG=&HZ^!?P@*U+Si94~(A*TMzmV+Fb^M}zk z61z1m2eSI^{eJj2MvKuW_V`tKkh=d@%f&xY9%`8Qka*WH^`ZmQ4tl4OR8!QI)W0YX zByL(LvxG@oC!4n$+4?~GD{}13 z^jit&w@BC)@LL*IQL6WXaCaf;4nEaRaIs>Gg-GAC18{b*O&OnX}2N1_k`MX^1gLw<|EL@H;FiCfdQQW~G1@}0D4(zK8~ zStxT%(uTf5HchpEBTaMC{+K3i%O7bXas0-#Z%&rvh@ysDNzz61$j=f?q%h`~xHV0e zG)|3e^Dpv3x4*p zlk|{sr3IGO9j09U;aJ)RG;JHC_X zlW?W&9${(t(($uYUplUqZR;$UK7FA~dnz4!O4*W((Y3_mQ4%w8TS&rb>MjjO%EVq; z7=KISP2!utj9sO6ZCYN)u?C*a;eMd;I_a@A-ctXh;gIzHo$I9Wkg_t1QpPMRt2v8m z?hIB}pwxYpD0M4E((j0K!$6)*$qVtN@m7otfr-;4<;j2;q=_3#QI1k{mJ_gPauVM* zsB$RPw;W0yeilJ@FK|$LJ@Gd}S?qQZ?&oCPAE;2o?Iutwb%%eyawC5JbUpN%hQ;Er zC=Ldx2Kd7f;`|nhItln@{76evH}N&ZHso5u+AK;t4~+gx*BOj(G>P5PDXIZua&kO) z2fi-{9GNj7oKTj<_o+NhC9IzTf26uMcj7)}7slnk&}C_{C@tuUnS{k0D-)IpZvn2K zwi{zj_(8ZaJa0Cy@mgsaQfI~zB+rSTgbl%JEJ{s!OjueLqta?-QL5i9W=w$xL)241L9VJR9LWR=tRi<>TAft~Y;fvE8hIo)T z!v-oZ;0E`IQi>W6)Wg1n$xoV};`HS)=>+S=G^1i7uArDyu~h3-YLqGzN)THfsS6~& zSCQN2oqQi(!{#Lb|s64mwrhiV~Iqttv=DS0auD&z=7%>`;?($mz9MamT7 z%hF^~nqf>_$T6`wIVOCs36t~EGEK@R#)pk%g-O5oysVzAf9XH`S0?UxKg#s#%eddX zf_!a_dqObz{bVn4tu)S3R%KDDBrFvKt4o#8O1{X+bRt1zlsyl|7YDv9EmDoJ6j`&y`i|22M(zt1vT5RnqgV^^Bk>~fWR8E+ zZwvgkz<rt=BuV&sKYZIxm4RGhq|M?ubguJUke$sm!UC1{Xzr-h)Khk$hWc3!b zkkydLYM2Wlzo;f*5jXO_Mkd^U6&G@z{K&f*8xXQ1*oBKe5i$?l*#L7x zkZJHD;0t6CnB2o~#$g@}yb4f-Tm){@0doV8Y4A%xA2M02Hykj6EC3rCqYoxJ;9|fE zatXM7N6e=Zckq0`6|xBY2=Ihl57xkEnZA%6u}Z}>ARBYGBJcxX9htv1rKtXZ?=-Bx z2i^rtgL?(|9&iqFJ(!I)PX{_0;80)}+*83Tx>8gO-0Q(fW|(isyqE}Vj1RveAoIX^ zz(MGjfWH9`AZv7|s7}B{TuX!d0n;G!z#9Qo$R%Js8g?K$V2(M`o`$r5bu1|AIOf%P z;FVTLFWhNs_=oxBn{d~_f_Cx1N?e--t^>M4*04i-0W0DT&IO)B7J;_{Mu>9}_!z)} zTmilZ*h8)d>-9vQLZ1c?1+3sM02cyp;a&p%26TnHMlbAl0L(#pv%nhwRk#;{PXVrw zE5ISW5f+JaABqw=VJ*W{q|2G2MgjW7fAB287jhOj7qCZqMc}Q#Ib2%=J_I}`b_SON zA@HXHe98rJK-@H3F}~}Ie22^fF9mc+e83+7Pskc>6g2`E4xLo+c_0FEJ-B~A*c1MT zfcF7gAXk9R@v#0T@e{lqcmTNqd<)nGxd!|K;1GYnUjco{_%Mi40}evg03QaFu#R#) zxbFbi2Wboe?*%R+jTK-s57-&u^1wmBO%fmQBftpy8lDuj1lR@r5^y=-3I8j=w}7tD zsR6$L4nn6M{1p(A_zXmR0Da;QxGk^+I$UrRU=N*Cu+AXF2eKo09^eYO2z&wfLi`6? zdZ8IaT6o}iK!ChR1+NCq5qdd*gXn{|0@ctj0e=M=;I83~@&@SQ{fY|k6QDW;=>@m+fuE4MU|S%Dv=87SUy3Th zx}zF?$P1tday{6@A2xyij^MF?E&R^{R{{-iuLoP=W2t(C#RK;bfSr-vEU;f7$}Rk< z0N(>dBo5%c!(daaty&M>JDj3;NLM}B8XpS1g$;S&?LY(KRsn7?0{ZZi22TWz!-iSl zyFdWk@sU3@7$`=v{=lq!aWPz zI}UMxKLRi(9(fI!2D<`Eka^%zAcuqn_7UJ(q7S|gh#*sAuzn1X2{{Y=1t^2eO+Yk& zEQFg1J_C$JxHaI!M9ig;w1X{$NDI;)0$uJQt{jP8K*9sDUg3zXOU8H;rV}BcKfWT(B8X0htCn0JYF}1P=u2A@jg# zKnQfQz(;{7$Q58#3hWFW4X_iCN%X-PfIDDe79!5TWzw&K z*8yMPX9<|I2<;WgFYsEx6W3ONs{td(HQ-@OU_;2%QjB2$0g-bkYB!JyxgOkM8Jr-~ z;2S_DWR2w*uL4~mmw?q)Al{I<)WM*jgH`@fGx2R`YE^%LnHVxn*UrNAM>rwXLX9#!);12x?uwg#hUg$f5w*V6nRt0#{29yWr7lB)D zMEoHOz*&F^%CiW(87M#+i@++IkiU?*;2fX?atYXDGwL{GjV&m@Kn%uIso-2dNXA-V z=`&u!kAUHL-b?ryASBOzxBhshO`dI!0kUu{;WVIt_yd+cx0Zd@d=c)_=hK8u3Sm3= zN!Sd~g-qBH=n9$ePI3&HyA9nakOf%)UIb)97J;t=BFOdN+9ISGvg3BNt3UzK2Y&!K zGvLn-iVEBb`@+2lY`Y6(6mkf7@otoP=+uK9_8{LO^T6W)RmfT3lYl$q8nFIelv~I& zcqEVoITgGQ7z?=qtWb=y0+|c;0Ys2Pz{NlS(E;o2L)m~#gWZ8l$UJZqPykr~UIm0e zE&@LTav)Rt(Ix=JL?1jG5I`1zj{_x;E5L7oGNOL~=>?*QJ~$SrfSd~60~BOJAFOi_ zafU4WESNkejyi<$33tNN0e8r}!?@Qwf-vBZ?6X$#9JYbDLx-?d3G#O)bigIY5NEiD zlv30bU?Su!u-|dy1^meZzXgWFUE>7CU4RfWbrLc#9RBdY2B**t5I@0*fIZ|?aGNre zAEE;uaT@gvauL|+4B92g0neMc@;F3DF1ZRid82e;RCm z8Tmxg0&aE%^@6yAgMk{z0`NAV9&!oTxeE7L#2s7$tb<$w*1n4ScgQ^O-D;FigvGl? zQELDJA>>r>F`x)?4cPD|!h_5M#{m^Y2b=>` zLl%Lr0yU7ix6s!BwU7ni-M3-e*(ghQaPM#zbs4hY9?Hyplnuxw;OUQ%M&f5JZ1oIf z0PaQ4@y;nQmRzfV=LbM4Wb(e=7~p0a@&bGph(Vu7-kTc^WaGY!yw7+O;6R7G_iYVS zlY0youpQ4DVsMjNxjA|GRU` z=!PVny%`Q+Sc&0(SNsk#{^v9NoZ&2nJKmJU%Z=eMh9@$7nbFH&cp}3J%=v#;yqo$P zC2>f_5Rv>+F$@%QJj4173mL}Ho%~WUBoi}+a$@era65(_7#_f|9mAa&wq)3V;ob~; zGTf2jP7HTpcs0Wk`p^H}#MIm649{YCGQ&cKM=;D|*p^{^hAD<`-;$J*;|%X$cnQO^ z7@o{<0>cpu2Qci$usy>@4686)&y=f2((=dfIff51oX;@LT=$>1@UKJURZHwd&Nf`f zx;B?EhPwL0Ee`c3;3A94lyS>OS}Nd{dhLha9@xFCl0_9?`Qg?;+Omot$Fec_-EPZT z<6kHLZnyvMZ27z0{(JWO-ERLqd;P1lW1_>vJAs&)TX!Sx?8Q=j`q1LdNu!05cV-6n zb#iiacMQZgk~nAn$8!OJ)Dm`|-~h>sHo<(dla1ukp-ut3zRs@X+7kI-->8I$;BnFX zmI|Aj3bnOLq}b{-saB zaq$u_C}ZZz#y5qqXq4n7L$VnMp32~Sa(IF;7~7>tR-h&8(~?DKhfv-Ba88nfAz0Ir zxymy>G{Q4FEFm;OygH@7i?`n(-~I#pHd-o~g>5^;8_to}?%B?w)^BE9*7 z_yi$UB^Me-*c$#~J#1lcbbMGaSrL|s=8PN}laz?YX*kZoHqr6032Yx1oIDasImZdf zo;OX;Br<25<$|%wF_y7KO4m@q;dr4FFIYuU)f}<_b$mG4&FQC@48hzViE*?L@r;N&rOa3Vh{7#ps@^I%eT z_aIL3aU-K6v9fP)Tqw$6N-*~2OU4YlB;;tUW;wp`6hAIG8oRJacI`{}Ndv2gjg1xt z3(3OgnEPS}C-5VY!3c*rBVAvYdPt#o@xzmZeCa94MaK{qSRPAf!XxUa^eA{tQgrxe zTmnaG9b6@nhdeP*bR600dL^tJ8#QGlhd8HWh@l<-8xR!ImWEEMaj zi|f%+x&Dc+{II0RNPYqjZ=sUy2q$p7L!%S1vT3+&-i$3&EdwZvEDaMn1FiKK+gC_ zCkU}pw_jWe)>Xs2qP$<6q(VkC#sPH#$;C4wc^|&;Pfwe&QHfk6+obR$8(2|$y=84WZ@`7fE9wZ4f9SaU&a{iWm0A+hTIKSl}eCWRYUo4ONGt&JsN)@kE27 zKFTv0AWk^m8DbGlCSufasXnvnGv>PcdJpRBX=x!YK>w;v<+ne-E$~0g0!hq1AJS;+ z{$7tvYKs34nN4XR)m+S@>4I&+Aeo=!dm7#^+}Y5=@G4bC8KPwFqKTu}U73UDc94|h z*i^L~1m-7Tv8g4$p7M~Y{Ez0k14r!pjE%(x8hmVJ7$4_gXx@!BvGDC1%*c zRz3sa<`u+38jsDAM{2_Gv?mxxVzI$V$ak+hWsXZ~tTPPvJP2#xdx^{4Ax_;lQm$2~hl<2O%B>)iV#~olQWs=@ zwkY2$_(yiEBKvAaHtr`)_P`y974-4mgjk2h8vl}iR#X@yyrV#A;aV5yNcLKd!=5CA zD6(4thC%odAINT`F{rgfD*@~1lfPuwF3GOac*g+i^s^enbcMF;eq2&5yw3~2_>>d& zKIjkMd{Ljtt|Vk<0ZFX6lrxnCIp!x9$v$GE;U^dSic0+Og{HH3zt9-yhl%4ucB006 z2e@nf5f^{>pMWcW#DVOPZGk&Ovdc9||0>D>`kv5>L`-7DX%xUGVoAwfrua?)q5kZ~ zg&)L@*4Q;c;-e*E_m6GW4O(1Sfb6qHc7BnDLDJ@pI3$YWOLiiQKn%&x1dxrNuQ;t_ zM+CArR|58(!L1B_p5lD;V&YBgNm3)-SDYM4a`fljV1p43ANET^T7T>>A;iZGcpCtI zk-ZlrJ0ZxHiGO`Q;#~)EzWp3#)7)#CcU+`f63=My-V~&q{wP5dY$~Z2WS=>*b1t!n z?0Hx5d4I7TrIsRjD5)`cHxP9VZ{mn89tS&<9eaM1dYqz)h{EsRUs-_0H?cUMxix32 zQla~{%xz`cC`ZZKQ%a&Tk2O=X*^H`Q*+++4j`cMCk_;r%?2MxkyYx6r+iR~S&3T9{dwRajD3 zR#;J3U073CTL^^^yl(<3TVJuhdVS6M+VvIr)$qGMziea8#@daP0PnfrJTEVGJte~P z703Z=GS?KZsb53YOU_Xu4H2Aw|9)HGw*~%&1yV@`WwR+d-9(_EY?|So@j;EH$QGrW zgu*F^&0?9W(`pLJAsOy0z8r_mlE)_=3MyR`uz*Q=4>pS<8bwFZmNG8tw6PrCA>-z? zOVy|B#s9p}g^_L0$u?`G6X;F;(dJG3<#1J21%6%Yc60vL+dYR5-O;&NVN9iJwd=SgMHY z1oLLJvVxo+M?st2&)K{Utw#=&RJHmsyY>#muHI2(?3!S%K{qES6;%~|LSry-7Gd6+ z)+H_~s+unGvGEb{To)&j8IdNSuJ4|Nb$)Vyq+Srk|Id-rrCW+a(^mJv=6}(V@r_}b(Otx0X&Xt; z@RGt}qQki^WCuZ?=*T#-EthvZb`JEmvZ2%2hO%}*p2?-N)se?+6?QtCMa_Mjt!2nD z5_bFEvIBeJtUZM`)3@}y++jhN-Zv-RGZzhx-!g5%eeYSj?=87qF(ce)=B)2^T8ny3 zI zoryc`o2k~_upIL6uvL%yOWOo}ZLj5!E|>i1bDxoyvdyDE#qPA)a&-LIn+tibBTj8H z<7_S+^|Yq@h7C94uGW6PZm`+$$iA0qt4>~9wXE*s?e-g0lXrIFuBqDpHT5y;Qr)xk z&Wi!jdhzS*p%HSz15Z(PxhQ{o~G|u)v{IRFT+>rc6r+OUEQH8kzOVviaj(>de zZN-;Q<-IN3w7E93I8XO(QgmCXwy3A_$YlY;JFnfRnorx>SGd_&Ph7eGW$BfVD#@QF z%eQenv(WmBU9XKd`sX8J}l@7g^yi$CAZ(HZO z^z~CJ=ykj3wkUgTn{!%nbS$1NT>jm9iFpO>oI+;PM z6LoEJEKkdUXl>$P%u%IP6qL|#$jd9r$YYxL!WxyFORD9#*(EJUsHUW6K$?840FU7A@XsO+EJU zZjh#9G${S#>}{zV4yCz!Pcy%6@w~-@d{2iMyTr@QrrU9Arnio|yTRRd z<3$_el%sR?TAs-C9670b`1zdL=sR2N3xaY_>kliee$!oep?LE9t5b6|mxL`Z?5ll! z%FTYC@05D4ex%^{BzMm1o?(LzJ~vYDd^sR>+Jn9m0;}nC1${KR9Hz;&ysA{Nz|lwV z-ueTTcKf!*H8I)0tyPn>rj`~KjjgGr4Q*-B*qRRcXIs;5bXSxwj-0mfU$v%a>ihb0 zy%9z_D^E6!;^ve!AI&Li7CW7lMjg2Lt=(SdVPoHAZ7A$I_0?R1N;+fuggcex*IIgP znSSB`>%zS`CYlMH-rU-pPGdsrx<6=Of3_UD`)#S;<2&z_3=8*MR37@AwK{R;UKg(`udf~Q zAF6z7Z>#smg{k{zxNT9maf73FWzhbYbCj+07PomM(B>H5nO}T$n&q6BeAQ=B`xkpn zbgjJ{J=b_#slhAjlm@RoT7HWw{A+dDS6+@Q%UBcrs?DHm?NN$h^%bL+tzXJprEj^W z-OJF~@=^Np-o5OYHMQ#7nCTmqzgE-fZL?czYf94ibC2C@MrHY&J()e`wqj3%k5v|t zH7nzMGei-hsb{6lSvYbe_^0OVm+jS$<}A%(p;H%=a<{VMc_&Tnw4S?f?4$FC=1hyU zMdId6iB>>!hW_){Em~_yi>6I$HnwPr3i6~ylcPN(4Vo+MOgoBti|jLc$~0)0qawY% zxIJS^q@;T%y*tynlfE5YySQPSAZ^Idu*K7{w43ypX3O|1?Dlv374!(FJ2d*k%4j9q zK+1R|TM@+VBg_$Ov*q*0_X&YId+VFEy+a zLBoPR;j1^5`}%sMK7H3}WM%f4udBZHGr#|&bw=CD!`w@KwtENfSafL4V&$BK`HNX^ zb6nOB_^i`$Q?AXCgrYAu4^2~h{=%wY#NsZtEyLxLuDv(!)?)O%>>Z|;gx0%rmaqNR z(53sHRvw*V0w3*59&tV6*5-(+QBSApX0|a;f0wy4+qlG_yHYg!zUKi?Q%(9=+Bk)~ zPK!-1bKf^x^k8DLYHr((ql2fh-kUBf-*)>{{R7>+K`XkOs*cQGnAGv`HAidh_`>`i zt;g4H)gK(LXzH&J^J22@`}@x$67yq~Y-Vp$Usjkjx>c8B14oq(c60K7GXL1z@7iNl zTz|2ua`oniee<Y0oQP$e!Hj{E6ZtA2Yf9`yV zO>+-DuZr;Hu}4mH_AxiSk;F^MsCeBl>g8yKHod2J=(4nI^7v&=s<#5BG%qOCa_DKj zq9(WNoK|VRt_9=U=HxD!GBhCHm;ZgRbF+qi&n`4PJc9mV82X1&rhjO+Zg#{iyBk#Q z@uM>b9@~^HTV$GHNd9kYr%4B4LHEF0BaOptOL8b3Zl@hEXNLIl+KLwDwsv$6D|1v= zg=Wm{j6Itrr_J{BG1sG8$vU#Nm0bBTp()rH-Mp=M1HFLiqr>gmfbN{dxdeXW% zJ0S0h$C-yMj}QOmH6;1F=3Vs(TE6oWRPD7qv-YSiUzzF=yj^4N{odQ1H(qjl?s#X> z{o%RGwWfdKTfFl4WZ$aAFzeED!|N{iPn+G@XkV4yhvmZ`?p>g{cJA_mpjQEMk5?A; zF9=z9qj|%u_74nqs!ZviFg8=c;ahaKdjsW$aV~u*dTC_4nYHo4tyh9!Lv2e`v?{;* z9F2cg6x`|N5RVldpF|9U7y@OEyCw; z^}0-3P5Jf>jIFJ2y`9y<%YN#)E>l)kOsuTva^tWtXtGJOZ7tSz`Ic{2U2MHNxtpf^ zjEKs&pV>n%D}+tbSrlq$8@-!*`rh3m?~crD%WbQ!pQ!(CK)bdNZVw81`Moe@9Y31W zIlAhhM#pYh?#p^5&dgr)dgc67%Sof7V=6be<&@0*R!NHrQs-uEIS@Vg(zrGj(L)Ch zJngmkcK8Ps4~N~G^|vRVl^?4UasG^HavT4bYt2UKD2B|+aadLwFYSd6BBzRf>VGOZjeoT5Y*tHg+n$Kgv`o9+-G%2v|LeLd7pK1d7&`sSyQ?4m3V$RC2nBi*_TQbvLnCQ}LoLf?sGgpm~(pjm^7tgy`ceYdF;_&-hY(;r9 z)q1_i4O!D>HsrSC^(cA4^;yrq3-VNoOBleEy_d&DZXur;Z`TVg# zAJRrVU?;7d8Mbp=%rL|Fd+mgE<5G7{s_m+7ZoG1d`WCNu+$$$f^+IRSV`{I%dCwjIyI`xUxrJWnIzBcRRzc@xO(MI!j^G!o@hGni~FCL|F|8=wZ zCK;Uzs`e}<0ZwIAATTY!>ldkPWr)zsO=Bu0>%RF7X7u#XvR!VDr;csG_8h{c8gBCj*`uIyThE6@;}auwl3PXVgDiP zC8wr-=w9W2^yyV@izkkiK2__AGP{mqHE+vqL6t>RbLDsLRH~Y;FKpM%O&BI{w%1j+ zpe-!P45YcGrKOc!peSx#3_VI_l!7@?=_o~8MP@Ga&*vh$(xzlkBd6MdxmV$O`#5pQ zYk^$6-xn(&Gm+$`g`dFfU?oaddxGKNI}8tRF~dVUPvg52MX{X<`1g8UYioE}wycvG z+etsiPC8?&{Z})#+Kn@|e;#kr2a50d_5VEHE>7Y(TUd;?Y#c;dTG6(&g>(@4A2i-S zoX1_a$i&;@LZ=zY>pJT%vn_et{PIQp=TT=_5f5gSrxcChrypGXW%hvsi*r63Yt9SI zR353R6R=~y;jl;VEnWqgm5k48UQ^>A@nX%xG;RVn!YSrdsnVx6lY;6DmLHpZwQK2q z&75{$D!EF|JA;o*$qcaAOz*6oGk(bcJ?-IJzC7OiM89RXq9WVH11?PGZH|;PUUR_q ziF*9Tz~u`}w5|8Gf8%i1sBHB1WwmN`2L?WLnByzApyG?!1wTvM@yhy&7Y%E|Gd3Ll zdTDB+md=Xhp2}{AtPbAw;7v%V8TM80KzsX9dK2Ofn7NuBw)Z@@Wfo`A$srk{M4K*Y zOFy+)F*$QawfDH0-{MkwTs^>B{Ylr@OZ)YRi5s_b13PbC>pUZ+=B+2&{Z_zwpZv9= z7kW$hEjpigVM!Z5S~4|l`TLNOSpkZv<<3c4KJQvkapD%sanzmyjhFA|j^Dc~X2t$bZNG8k4)~gf*FOS`#U5|Xz;CT7~QF8`nwG-)2qAK32y|ZurIFr z^saoR!@2f_gDWP)RJS$LboLn|JoeCflbN^HHRB8Ql@$wIw60oA%89w3;#Bs%y^(KS z+}4x5{U*E`KBH&Es}K))ueyERpDSxihn(pca!&qf$k}>LL#Q5Cz0@hCugScN*1-x| z96yoh9d$~lec7}t@vA+@f$l}yiF$~vGc5mlmIMtCX?LW}j*F9cyeX7UV_6$sKWY#wj_YGpE+qcf8ZkV9r;oc@{f(NdBB@ z-b!xzrpIo-zCNq(o5LU8cRN1$MZ=4zBjf$upM*YKZkKZK@cso6nMvP+rnj`}bgA>% z?=Q@*e?PoP^?7Wb+PTtKafb{RwC+H8Z(VvhY^>?DaRpZ$wiwJ8lp2lex%8FV+O@-c zsy9>n13p`)dIg(|iP0NzY-&PQVa>_v*ULUX-L;TEQE{TRYul{>x}(EB-;O!1vN5L8 zz)f(o``jeY4#%7-#xLnr>T#=lVMk%|@E)S&Mymz|52~E6{!rU%s4L&YCE|j6LuBN> zfk)1%ZSP4}tZ{JPI-dXF`JEb8{Z7*Xc3Y0F`?ztEb%lkaXuF=NuECD7N%CV_dvvnd zRFk7WHRIhTKmOsnd95$+b417Y z939_frsJd4#i=-nyBV9}hfz<7!a(am)j}H?_#T7Rt6K6va+{LNxg7UQ)S4s(^LFw4!NyNKb)O=WEZz6WODJqcWQlZ zR6LzNXO{n%i)PuLWpnH+b@Emo>iE6VhkGg{dPRVW?_2rDU(L6CESuiyRB>m$b)CDr z-M@8XLGbE6HocqEkzL&OT5nr&__}Avxq<_$)pRcF9;b7J=>`|V{C689YPI{i>5Njq zjFfdPp6U60udwqq>-e&7>z?uDSEtoqtv5O06)}~4`SZQouMVnq@Ht9)n^dhc4#^@<}T^23QS-Eu8vw26y`k!<)zN4VET)}L$+=#DJ^L!%DZ2vTO zO~mQUg+tF4xRsoKo&SB(kQ-XbpJq83U2gLwVZBb?<*hFI>-x`jveS`k*ZEVM^@fG( zO9!S`%)LH(?~M<-jzbdb$E^K4U{YV3qaWs`f7*9i%_nuzr#*H77IpEuyqQUM8#X$6n{y+7LKI4uAZt61Nf^X{G>J=rquXo6eJS)>L{<7O7F9pax z-~1;Z2q=js|Ijt6(0FjbJmTa@dbs{{Kf15TMdXyxN2Z5MBA=L%x+#7V&{$K^Dg9JW z8>^|fw@a4xZs_gEs~CUQ+x@xc_$J1egevYng|xBMLrkma$vy@6$%iJ5m$<)+^-WuE z8PxLbZlAOTNq6MNTQoB>o~F5@=!WSeQ~7rXdv$snDB9P5XjbQ#(6hUFH||aPxSFbS zEZtgp?}7iv0H+JDoF8gT`d%b#KmX)#<&Tylo{IE41`L0*OH=*ImC}0$uLO)e;+}lh zH8OU7k&A}<+y@_R!;WaPPE23xdE(p-AA0e+s<%u0m5R%UPhl-NoZvmGmvfJaEyvm| zT9mzF`^_D(PO*{Yr{l96ru0oX+NML*F~4aeAM89aQsMoZDvuFc*LHE9Ww7vDL42UG zAS^90e1zyg_06jTDjl{>Rj}1+Yc#{`!54N_?-b5Kj?M1JDQ8!9o7+Km-`ZpS+SUhE ztw|grAGLhL=^p&Hho2s-cAn}YzkYVU^7o5tvhxRRa!`IJAG7yTL}0b4;W3ZgS9T*j zZ@W9Juv)TQIe6aJ58J&LobNO9z5bfr8WRHB+N<7nK#z{++{>s=pN#Opt6H7L7uET7v;-Dy+%Cu^K?zilrT z@lCC2ZWnyCe{_^SP$CNN`ek@as-WWdl&6}x?G#St$65sKvQ_TVUvbmd=k)bg8@gZE z`}I@B+G*E1t@_LwnX=Dc<1RPuWa0LYd-j)E2U#Z1do_`(YMN5GJCB{Qzw^sofzU%JSywc(x#WBaM32c$Q;s!J*eBFQ}QlZD5^7$D5P=^H;sj$o83k zkgK+A(L~cqZ(iNT#gU<>KdvZG-nQY0>*cS44|LiKnc&!}=Hi(EI_)Zr_bdv-wk4^k z9_m-+);=lf-oZJK&v<3iX$Ltv?5bgl?@bjP9YQVsl=-p4twmF|&ZpDX(rNS7rOl@Q z-x)!$HU4U}Bc9S?Q$r{EuiP|owX)#zD9hdE%e!t&a!D}GecpXve47Eiocf>E?d#IH zMg8{V)m|NE^@z-GxyL)`+pyK~!RI&bxo|Uc{PDS2o8DO3C97u}Pb&EG>T=L*CCw-4 z{EVzOv%X_ZcC`ot<(n0==9Eu78*rBQru4#2uT>V)O@y=dZLQwbJmrnz;j!o054Tmb z8Wdtgb3X1(4J(|V$q!03?)JR)#B}%ayICb#XPa}z^t|JDXz+wnD~<1eonp7Ja#!2M z%QCi}(QH@MYx|1tW7@1%R}EVhnO3{7?D3vwdKGIGGRG@6)IJ`5?egRCQL{(QEb{cZ zC2DJXEa=f(g|N~(>la??^KJQt(>nP-c1xP9H9xwmRp8Ue?t{-i4JtpeW%?ASYrO`s zx2f<|YR0In`hLE{*KK!>Oe=}_;@sd-*sg`gse@*x4DH5lacJ%{`-#JXFBr&70wFShrTyRY@i_B^WQ?xQPb^h;jpJ#>vQTu$C-~aXdeZQmkx%;{2o_p>& z=bq=z5I1o(=fZKE0>NhExDv$l(Xr>lAMP@aYuaIVQ|@Drqa90R#-km_%t+7Gg#UhlR2@ZCDVClZJIB4!knAKL`38L}5(xGAD7|u@0yJOb&(IWCW*=4s6s*Y{pq>4yzGp?D+^zA01}XD@Sa|=2(nM zPlN>sP9Gh?Gv%6+QO0PpjS>he5S%_b)-E+88x7gAm7Hx>QWHu1AiEaHnM2e|V^<^Z4Ze8Cm1K{(F~w|U{ZwH(c2Ll0BE#wZ-* zg|ociZc|5rM|fd!fQ}c;0X$!@Pn+O?O~djLh1?_v2>}Y6-v207I35M&Y==#0HtiAB+jm(4vdD@&$+G zHuYKpOZe`hX)~k?OI3WC9rL{$=R~a|N2*2)tXBe}1o8#)uDl@Qh4@-NXfIz-TFV#I zc9;#aH3OKya+~_+1bZjm*2Tk-0py188|L}sy}ZTy>NJiM6pJzOqGryszdyhH5%q$s ze*Z`13s0JxFYqq7EnlE4xUI}{LHp3s^15Dvye3AqU6$usLPDp!NRC=(g8g^b^T{y* zfkulu;5VBsE_v0GEf8krF3#mkz3m4(9x4$}DQmvY5LR{IYMVm_ zI|VoNq)?)yY1Pe0FK|V2r+jSTVRM@W%`v2Wfh!H1=PEWiC`U)GykUF7R~sudGg`c# zykfH%l2Zg2oEX)%qeXv{Woy{SNv# zhtH9xKv)+ZUf^y!{QK&{!wYK(?SnE}CtfI%<{-!lYJC>^7nUwisXD~KCOCw)r3-5X znM-MrtQ_n*5mLTx{?dNS2=F1H;4kgoTLz(O7fsk8)Ck|$)Q?T!IP5!;4ZLuOx2Vq5W$V z4E{zz@52jvcV6&_qyLydl9AT9>V(1a;^a!+BypK>y zs3%QHK11Hqs%rE?Da3MI;o&@=*w9j;!lvHFK%cAj)JN#Gc{+peL^xk~!Q4;PLC2{& zEG2%&&}+z~sU$S}cMlK}?30iWxhR>&tVEaDx|agpS;};M4+U? z>tj<_!cc0!@3a~k{Wziz5l5(z@Tsz?cTXhtKNfmHsIjTfAj9?(q2){TgL*t_Q)i-_ z7tU3CMC5AojCQ1=>*nW7zZ^8KPd`w*r7+qD!`Rf#S?%1{n8L1x+!aYHWMD>^gpL>H zGX<>CPjztTkt1;c=!RW=`3cC$hYS86YX-Uc9j=C18S) zn8BugM{EuVuQLea-9aG{s*g=wjxwV#i1CS0n2xmr;bx=pN>mtxBj6XSP5l-!_4(xt z8rswY7)0WiS=ziO;TM+9ea$EUb>fhC9=yWSTIr17 z6^-+B?c4E)ZnS=!o=N$FU5uDU;g&i2pgx|~X^&_#jnsiS@qENc-rTIHHD93DDL^)@ zK0yy#jRa9VUr-iqO$q>69p$`W0Cnitp)VR550x{=AdJ`jb*@e6U*~LxU?9dDBF6J( zk0QCDV5m+}-IM_`SVn|f{Q<}zjBwW<9LmtJslOXXCF288t>LLJknu4$=ZoaP#vMYk z9yZqLf%P!Kf+iARCTeMAHL;TY2O~;jHTMi+bBWn|hf8qrzNfz5*jlv<$ctC9KE0*ipYYmbefz zlkk5Ppc%gbe-f{a;QuSSJOKZi*QJ$c+~&;)J2l zv)E`~poq=ttWCWKX%V@?8Eh(~_Q0iyb&E$#sUz8&DDfifd~-iu7&!<0l>_TOhw&pV zeY1mdmd5H6FD6c9e3)ash{Y81(C*N@MXv-b0(N}SdjZ7V!~xjWDYU_He4!1lv#EW6 zT45=lY}Kn?+ZSqs)%DCyYAl@LEssyIrh^@Y?=k!w8WLL}M>y!9C!Y)o44GiGgkio5 zadHPptq}@;j-klaNGi6ea7xtiqBOaHHrT{dBIXConLpkjG&KrCK&=xt^>e5}-lnb{ zO-&Yb;e~N?fI$vpb`Q4m+@+0@W6kXZtbmKHoMBWRc~P}08~DX8y=>0lg{cq$S&4j5 zHD9pbjW7Gng^!$?$g754BkN4f3r%cl8OGyn2YJgRcRuoNUiEI;@@&SlJ9C^pmt#HO zo@ntnkeoWfrXCIC_#zifI_DrfOg5Vli^vo2LEeD6ydaTU5J^v>M<*@`t*=A>Xn7$5 z$3g6u-6Z>M>WKg>=^@b|VOyy!N=4<@1gB9f0=71xQ{ga`Vl8@DNLNNtDamli0;eOX zq<^3n2D~$y_Q(xUX>h8-OPDXIITCK7P9Xx=)U6o#AFP<(!BF6l!XVZk>Si@7$ZJe7 zxuFeHOu0cv>wnKfYj?f%Q{E#wAB!-YRBI&@MaOJmzF@%0JjPeI@=UwPtwda*wGQsZaiJ&m zC9DJo9pRjHT{MbH`Ge8^#4UVLbRaK$jYx|C4HKhO%C`Ns>y(3v)Gm(6u_rF*nel8hTZd z9ANl^bcnGX;4KRwIsVM6=7p!AC;@#IB#u+RrxNY`+S&Cd+Xl<;>pvXo_em-hhP&I; z(^-mPLiRh@NL#RnRgKl!8w11()vdi*J*= z)6^CRKe%i5b~OayoJz^s4yxt1(9nRx=xaT&Uy5>b5at+*K4Sfa;S&g+o)<>X5P{V` z9N2pju#qA#=$Bk<*<9>_;W!o@He)-D<{O|z=h22}%}yhfnut)s0YQhr1B%8DMcV+M zMxkg3%KjiWF%2LDB4CXtI}Qsrb-y^rkzdfq!-x{};f5j*Uq6Qr(&zAHwQ_wC{9SYu z77SorcL!I|zVH!U1Nt%gv6AtqF(jWi3bzf(wMGF)rP+XQ6e?}%EF=uVSa*mXo{mii zkT0~&@rx5;f#zux#|esBoB9PRzrf1DNK*MZn|cAtW1wGh$f|}sjE^n6sd{D`2yRo4 zLS5)@24MH3Cf4USbEZ)!BohV$ z^K|g?01zZXFTnmn2cZ@gp*cv^VG-JJwsL*scMFPR4*nk*qi2k3rC1S_?p@G z>rh%X0a_Bkv2T!L{Rl3j38q9#1Fb1qLK-rv^;7hd6L98Aq~@SE{0$C&jGf>{;d-2K zix<8$2v(d_ZR%(=4!yA2AoP{U0rcEZR(FIRNiyGYgeG~8wJ=qaD#4h!_Ib=Q# zv`5gUu++T8C=?KlNY>H0;k;g`C5Hews1JI@*d0t2F~R|yu8l&C^=XJqb~-*ij3{_a0Fy+ZEgM_(J^&AP z_90t@&IQh=WAg79+<}z^-6eVZC9*f|**g0~16iprA=3nwB%|hC5|9Awb89mgDeg;Ag0@TdnMGS8^0qy$Zy!8fjuU%(Y-y&BT=yjjQ;05@uc=vJR zvAe~Cp^iVOJ9G~$%oj}nCNuo?LRBpJ;IUHYt*gB88ipM1mf;%fLS$l!o(eCT6%`}A z89)jurYKn#3YXwb^FZ3A<&b++0Sh(qo(YWR zZOycIJVp)RB8;KKT#RrumKYIx+j2Z>|bZm1?PH^T3X@5!Y zKQ!bbgqjS3@H-DN=px+X!TsTN=O;o}@PhlI+>Rs5?O-|Z2Cxlsp`LtXt?48t;g55r zy{V)O{*f4;L1O$K4tRV~E(no}#V0Y|gmgg^G>XqZ(FL6e$SwvX6#cL=jsMc~l7Evg&MRvtzNME`*N3}X4~!d{VWjh2A@SW~PG=%X?SZ56x#MlMBvBRliq zRSwLyL&)<53;kuPWo=+2=?fO_3Zk!is z_`@g=@ge$v9)TzjJut9j5=lHk-b3&8i-&6DHrd2K!?j8rzWsn-drck zI1op~2A1(t;V5cg8K1!}g~<9hEMpf8X&rKdEMxD#(j~lMr4Yr!^+m2^tMDumZ52>c zM+OUd9>_67_6m+1Jd76N?*`&ulbvm zg{_>A{Emq@rK3SL&Kcw&A%z0957u7zWH?CqQfF+k%&n% z&zycTv)Qn2s-;S7J?R`lZ$QqIe8WD{Lei0>f({NB1zqpe4*B*Y8y_;5$+t5s_W=1; z;%Ev$|CX%^yY7&+RY%P9KOy9T2*_y-0dWYq-L!<*5!O$s9CTr-dJ=?PjzoYN9I@^_``lPDMZ5b)<_jf=m zS`@46&a3p!7vc^YLX?MfR~wiHtoqxdj@j+*t?Pp^sznj` z0OGO!&FWshBX1P;!h!A1T)0ixWdV9)p(w8kZNkQVV7;%sUhgj8G>&zrZV!f+?DcSX z(f0cK@a*)4cJd(6J+RKSbofvjbu+Xf(K59SQ~7WQ7eV$FMfsP=25)MODPa0QF-W7UNRsIlqJKRhUruWrW51@Byq(pDh>+C9* z29$>l=S2L%ld7fbK@Of>g~EZZ10Q)pwVe4$EMaGeQ2gS1%xe~g*8pAlgQaL><0Fsp zsv(E0eK9tfK@fROaR;g^?DI+-6>F>gaQ~@4h9~0(b3JDDqLxK&aD$Mi8gF6n2G->m ztqe@T5k7GIl4dscLMw}+y||aqP|`&QiH8!(ttJIc>CyaSzv&{azoh}cG|V?(!FrpO z_@_brqsHGW(On!J@2?~~>p!-in)*LreG4NT>ubNi0iVQu@~O55qv^xs6Y*n&-iK}* z5zg?5*56+Z^9|_HkUkCb|AqAW2m9f>77w7;Se!-Jl8Ea;d*OWjycBOJH7P)kWT3NF0G3uhD>q-eO$Hkl|Y zt%9r`2R&SV`3L>m>)UYsEc&|3Oyc_2&o^w}Am5PR8|HC5gJ;4?-!ezSUcByRn$+$| z$ElZR{%<}AS23jvmAG3{Muf$Qw@RTVAMZ1L>k)_WZGaE>w?6(2^9|cK$lr%Q;cM~U z5B{;KhxTbW{C)R1>*g!zEBpQ04x*Z3j+8oqYf&)vGT24yiF~C&D6Mwq2QYR6|M6Y< z$U_Daxchw_e1?0bbpn^@0~Ko``@$_}7`b8s^^uh`H`?e!+Z$%DTn}S^1y}zU;1?FS{-0 zBfn8Cs|Gd(`+No&BG0LoHvxul`LfeBe*#{VQeyDPihLz}2h~k@OIR~L@^@Y}_;;wj z9ZcmO+)G;45LrWQ_BvRhyTl3*IbT|LHv?FvcrA0Fcz=Q79a>x62KUWEB_{9&6IjzSH7H=z#Sy#iI zA)b{%Je&$M$fsX-$&8P@nBR#Hx=0HBIB#i7cTC7Mwk`o^vWg8wL)@#&&1FchCzG%G z6KgwwbmGQF_U|Ls|GxXRux~UD-?YXkc#v}nN-Zwvx*@2{P)v%4gm zb(q~}^%k!7AqywNJ4k8aCSc)y#KJ|hh5HyM3ATIL!hNG6%Ado+UC;{(9Fx8pT%`C& zycyCWF0wXHi3O`}hxaYCaL5bAqOzTYliPY>L(_hu4QTo>g zP_YyJb;^HC|38_rOY-U8mPdHoa2szM8p@*%|Gqr>vmRSNOdfTIIBirOU4&6z$JQ>? z%vl~?v&$nFYh0&>@~EUU(a%X9l`{H~@9rPTqlDuEJM1W%Z{(MfIg86abr-)Sn2%f!(cT^l_Oq$K?p7Zez+mU0y7x79 zOVB`enTL4yf!tQOUT1$J`f#_EmfTivlc8f#{EIKbNty1`llRJ^_=HrWj=8bwtVi4j z>F$IT`yPqI<}(J#3}kA6q$jFi=@}zSXL~@m%J|5=xi0iwJm_nKaJcGQ+@WL?sXO1{ z{yl2=pl|T1jjX6$bj|WU-=gFDdV?A{PYgnP#>4;UJdx-2P`>I!#3!b6oF`(aLmg|0 z_&1P`k6d5cAOF_+J^a|u*6$ZGY%Gdv4c70k#@EjPJ6GaH=8yAn(6ocNelCw*zqaij zxqi3EBYofc3G!VqIXRq9yo2L7E>EG_S%z=Xh1fR=zJJ$ZK{Lnq@7x5XCz`SE;3+vu zIWQL!4&3eh&|p0ze#-zdtb>%FY|l&Ir&FHZfip+)szvrMr{U{Bt5&28gHCoiMBk=! zVEvR4rz!=$Mt2x*65%w$cj<cRCJi%qs z7gQk6BYhmVGim?V_op|6|Bv`jlo}1?{a39TS+KhOi4#tJdH!;v?V0@2*U9iQ$Z)v) z4_RGd(mh0se*_~E^lB(y8|Izx5g;jQ`5<@}Pp~uFaqF_y_ssylW@q=y5r@1cSz&p! zyng6-EgmwS=>C8|QE32sIM}w~dL2|ghH5zwk>r^}e+o(;)WEB`hu9)go~NbnofZ7; zv#_O5UU*@FH@iP%|Nf%Og=$xu`Zbu0g7TaJ?cL0{z2`XdGv23%=>KD0`frRuWl-~{ zThb`@t55gWe_XG=A^!K#x556quf1b_xD3{C{SLt0+dyQ1-yO6g{O^NjUtc@D|1;wc z`3K`a41d?g`q%ku?wjvfB5i#UY3ni*YiR4Tk3Dy?=N9(dz@Dqva{+tiu;(=P9K)VN z*fX3xyR+xzP6VwfOLOdbgO!NoXIc6Xdv0aVSJ?9z_FTlCCiYy*o&!4)G##F_7Ihz| z`(TmY&*?bsMqYEitjs6vuQHqMMxOjeIbU|zr%ENB>KyL`s^Q`M%KEgLy*Kuy^lYR_ z=l%i-h8R&|Lf?s9kKzg*m7wycR?7PEEM_YH} zMOd}cDj-trEM;W|+!`Sp5#9w3Ukp^Oqp!UOR<$LGE9zyuEUM@Cx&en*=wU0J@0OP4 z`9r$0Jtf`}sB;BbU&R4o<+&9x0Xp3$vVsA3V=;oN@MAm=Dt|+Uwy@QTMoUZ0Bdo{a zm$DA_#3Kn0d~3BSs0%ME9UQES{Fn=`o3&7Eu0j2Gs6hR3s-KAZgUa;)DhE(qh;8Lp z)?qlW>z3M7g_B^3T6>7z6TTpv!&gxtdY@(Jn_>SRV3)t(+~NfnBknT+px$=Errd!S z(u{UhZ2(~x3?0t;CxEV|HFnU3ef?RZTX`Md>qUpq3-}sab0?bUKu)?rcc@62f_m$6 z%uctm7Gw!87*G`hjPv?fH(*-Uu~IqoRVpV}nmKP47Vdw@IBf1Shog-sp-)&IFVHDzTe9K8}WmDh4u?R0nlm#}IJe_0A zwoYT3aRXWDEB4IuuuPDFSGCoC_O>?lzW_qSqS`g|0@aH53g%!vbhvhm3!Z>*0t4sk%2Qg)VPoQVFPJCIX%o(IkC_3vc<(G|RTNZ8chvh1PV9tw{3qGl3?FQqc}{qce;;kzIU!E;~9|-Kfq?urz zfzmuDvY9DAqNqq2faV94o_LCLQy?YIt*Ml7bI{#fwNL zAVo+PKyq^VxEulZE0^bvZOEgP6*tZI1ZbXpBbEl?ZKXxZLFnf^oqot;I#^>r8QIO$ zxtjWE&>Bzb7lAmp6O{@0-4$CIj+PenJ-nAUqAVU>oKn6>@n!XwAP4cGbr5Y(aREu* zX~;&_;Vmid)034a?5V{dWU0epd>prA@jYm=e?g!Sz91za9Cb#za)<^-9DhMd{D1*` z(MDWBL?%%B=@@)3nYuxX%)n?m_AMu!_^UADXv_*1KBCb=@q;s^G-M+Cl0<|wUPRq^ zGibcTy7BN8Bx68M{qaobW;#G7x&9>!rE zr1`83Rm0~$+MOog$rlZSXMlINE`xYR_oJ*HCwsg7XotIxRp8yaMfna((h#uZ8o;0O zAo!1o@Yg*E{;E3oPd)_xU0S=w@IL^vpbl&3IE(TGYs*N%h{NcC@?BDSL798iI&`D9 zZ#k7Mmdc)UD$8VLWK&3IEoCm%vI{5>ssLpjh9)=5WoGZ)uo_Wy+^YEtEGXI+(xnec z_u66fZV1EmJ{aFhH(}u;ZX8B6V?VkGBjK?Zs<%EK>l=fC9~~j)(pX!Q+_0$I^Z?SV zU^xJgJtoyp6zdBd_0z@rx$eX;^J#chEW2b;^)l6fC&u!^6#&ri1*!gM6o>B!%x9Mh zV*UA|SbP;OA33}_45qY6Ok3hKVtyv&V>RgJ!a1MwqW9cLbg*^lj|$>r z?{ws=YGGi3+?hyYYr^N?0tW|Q;I6R#%ruodu5FQCg0TJ}jK)6!p%uEf;D@FT@{L zswxCPvQOaH>{=2xk>k=LWO?m)48gmUEe@yOw##d}s|hrBS_JQ(=dw*!P7bA1UQl$w zdo4OzGa7sZr3``H(NTe07w*+cIGW9Vp`|rEoKnXAfOR#5Y?Q$AZL;dWXk4P-Y_YGK z^;Z}|)vc@v$iuY0EEX$5N~_(h7m;BXSppf&ac~#_b+?p$%1ETP1dC{e;{tlFGg@g;qlm`AoI852^5aDYI)(V*Ju<=?H{4L9C#*9o}I0{*S z-`@a#kh+)veFz;!t^wxjWjN<0i`CQ-o88YN~th+arm|GLty zO(cDxTQuNPbmfHes={r=innB{<^Mv$`U4gND;kKet^@$v*Jyw#A_)ll&6JN5XOjzR z&*262jbQwCkdC13EvOt61n2)D8yFePPrsz0p+}&~M@ngLsC=NwRn3&q0M&SdX5Rf! z=V%zhGR?gEBR-&1;3;Fvr<+v13sdK;uYxCtr*IrAlek$q_6?gYZ!Vv#T!93nYb=Yr zd=^JA=1wRPnOpClHwGR|)hBvC-ZIjkcd0;KeKFJokx$x|4N zEd?LE2^(;4VD6;v_mB3xbH=pR-xhg=D+VSI@>~gh>U# z5vu;ZFV>Ny&6oIz99y0zo&I-VJn{*g!qS~Wy>&eya2UTup^Ev6Ocbyw;9(Ja0uso_ zAeGgJgfG_~s(KO~>9lY0-h|q!cx0u$N52Wd+kbXN+?9}%JJ71Upo;|&&Ai4eQ|hl zZf0d(+k!d%io6yAudytyyq)ngZdv84@TyYxlFSUp0%IR*Gt5Vn%aWbe?hAjK;eZlE z)y?5)A%vm2-F;A8P>$aS_})|{N??+^BEQ-Dt`YmLYS8xxQNEGBdueRk;TMDVt89VP zq~n2JD%{UOZpmodI~ZE;WN}ssW|0GR_=0AkkIM znL7^1p{TWT+pRlr<7j3F@OU9Z|1bg7rm|eW7(sa^guXL*NEU;$Gm_PAC@Vvv1oEGY zteO8<=wa1H3@!XkBin~n?f%gtw+Z^m(eHTzffB#moP)|UFbHX$2bIT>f>EvBu@?IuK`EA06ldseU~ z+3~h6AG7CG_N-*j5HBiU%AU*Ea|L^@WX~w}JjPm{W6!~?B!)ddXC)j<-(cx|?0J(t zeOS4iJw<%P_S;zPM)tgejcx0)fIY43Ig>q4v*!W!q|I;ZvXwnwVb5pSa}j%**fWJa z$FV2Rp8eUg2c9tZjx-E*nGr`{kECA(IANu|?qoTWo{JUy@ft#3@MEn}IIO?PDU8Ai zv#(yzxyL2PEB-oXbUA?^G%|KRVMs0?CiwZ_$KHHkG9q%n86$I1nVYC~A}*+`fpu#P z#W05_Ro${;_jQ>B#2~xGPo00J?q*yD(t)*BY z#lK2$j!E%uDc&r_E2MaW6nB?mJ6*#CF*QVr?d4V}e@u#ZO7Uw_yh@7mrFfr&Py1CO zgnLrC9bSo4Zk6JFQf#kpYY|hQO7VwM{E8I!m)gsve2tVJDCM7#;^(FEd?`OsijPU< z-$=22+`&?Qj1*6m;yDf41&JLNjgn$=I^whFODXOt#W7O6T#5^&_=*&3q_|d!ZBi@^ zw+Q!z>9Z(WihE1((^5P}igDc9|7YWx)e_w2#5g`*j1^Ca@d_y(n>8yddv=y4bxv|>jyXL$OB(o))PDYi`t7GB zrD&2e@KfVSVq<=`SYO&$JCiACt|lvWwk9JrYr1&`fv%9+`z4G0^trjICIY4DlB-Ee zO3z45(F8K^NZ>d0%`;M2OQ&xJ`bmw9&KQ-B$ZHgK*P-D){)?{R7O=sZhJ!pMM@srkudCinG zmD$Uw%wA6I?7_L*-j~YkX;Sv~HqJ4XKPYrku4zT%wq1aS^r7(rk=IIjs(1d_f8(Fp zB_hPkK}Zo>*z3HI^+R|Qb#FGP^G5j`bS^<%iBu={#FuhEP_UM7~)s z#SQEKyYRVh9I3#0`iZMSr+kjXUVGq&+OKx1FLp{(CxT)Bz-(aRNrwW0>-=#ZHynLx zP>((Pzz?mx`|P@E4blzArdon!hwVH*!FT@H+fdmG^vMA{$btuci04;0)o(%iK{yZ) zf=M{j#~BYA$NA%o2mK6PE$Sg-9{8d5KHy{fN8^;HGWJt;Ql;~_1lRrGU@z0{Wne!#*N#TraIQISn$e z7F$XH7Yw-jy| zlDSeDGd`a3Hq0nu9J;se|jq=tZEh_8v zad+X|2P-P&IULuql_%Hg4+ZCk1FQ+aljvy!u> zcyXSiTx$SH+fga!l=~E%wl&(f!QWq*kC@8FG-3JSD2t~uz@mERymKF!sVQgr!=1~# z?7_|0=gB3Tn{ZRdG~>pF`Eafu$`i$TxYM{fGMdLUX&y9Im?!6hs~aH*`)Dkp?;oJc zK5xzwxDqZ@_a#DwgiFJ`bAK6pE3P5(95xToQ48dGga8=9G!N&z)K)8NqTrf@d2mfa z+$uY^aOGN<-MOYIZW!B@^XUYhLYN@+ch1WedT|TKcycX)XUniAT+0yOnr4-%3hz>^ zslO-ZkG1i~+W2E_{6qX|npc8X*jhKiT4T*o*BW)LM>SY$XI>HKqixpNlj}UjhwBig z;(Sf28t=*`r5-u%i50MEO3k94A=ycv>_9O;A2EII{!+cvlVHRfjaIbkM#B$4x_6E5Xn@ zl**c7NxSLa;|BJr^ufvSc6`WBM4T4Lp)=4WBE{& z)kv_Z-Z@{d4{75DaQ-7Si3hE!5hgDbY2uu(N0SGwv9y-;+CHibd`L3GPE!sNbOS<- zgoAV5!OxBGkG)Q$A7~oEas3gZ7Kw9l&ey}wF83Z3JC9}Og)!iTFehFB?X?3qZW%(c zG@f%_TnosukR~-Am2RaS!(F*>>H^cHXte4{GjY&~`IIxzzsv(kGnq_WpML?a>sYRt< zj&CBtkpT~nQ5<(2;d{WLnDrYakH1fr;E(OOgLh=jWLz_%wS8?pWSobC_Rw!8(uWGp z-K5BINtA2tv~a}EbZd;pjg`?@OQbO=_EvD-VIG`!h-+0@e24POv$O5H_5^BvNdLN)7H(6%ITw-l)~2NXQ#kO&nN8Vq6LRO~;$WO$&d$ls$ew;5!0D;xgp|~@q`VAs zLUMLia-PYQnw5NCGeRakDIZ1UfFmARfwQdHW?MkAR_@VPu$&;{$itHsg_n>Tb z4(RSw7Pty$DwJ(=Dm#ROwLkTBg7+)RR-_CKB+N}u%}C*%kq=6k zAUgFEQcb38lP=esqSHO0pAa8AI7SC=O5Esi(ec4yTGp9s3K3B;4IgvA$<2v0}E{PmSMH&z& zXU~qUd$rNUKH}V*REWj2Y{(r^L}jGs;siL5%S_G8O+{*4^r*3qk1@o>I_5BjOPZE# zLKR>nC7a9%>Dki~((qn_o6V)AW#r|~AkRL-Gd&ylWoBk)CFG~&m>_=BIPd#va;2IG z4w&RauB9a8BoTo?j)bgihFunC^KdA+wUOdb1OfOw-{{3l$+qGFGZe z&B{+VWoKokW?|}K^;*EsIVatmU{0Erfsvv(_%)}4j2K6%A?mQ#L|CWPIjPBc=2Ux? z*r`2m0VT2Y0K~Pk=MtXGOHZDKE-2($pj1pv=V+oLrzGX1_sq=gIXgY8Cm{61UtBh) zXQuYd2<{o&Q)IdZEx>5$*$=6ok(!j_T+LM}Mwv6BlX5`+)aY!eQ2d?X>r`gU&YqQ* zGenwOY?j#sPOPPxap@*=UQ))`ET9E@_ndNUmZ)J<96UILEdbm7*A#mui|DpK40WHf1Jd zC1Vz#6108L{m>05TyLjR?3>i26clnY7hp1)>F+Thus#sOG_^{Hl|NA zC7I?%gEMov#V!VivL0;&Pw)W8y$Eb$Qm5rjpPp*6V`-&PN1KyCxEupEr0+!YA*inzi8bd=wX)=IzX=ZjlnC{>H9G#*$nReP{v)}Nx+i+x;kqz2gQy(Oi z(`QAl_})p(!JjoqKMwCgMUzUDeh+NtkXnyu$KcUB(*$hu1W~-&yEHbw*)GL&Iy+kb z8$W^qS|6N+5y*$X2=RV|M5HSa_iDy*8A!(?ehOhe(mFrb;|NQUrdWv^lIxN7K^%rq zjC3Mm-xjbHk=7tykMJqd#fXDi;=LTwTEsUI&QTj2V4g;(LE72BOn&DM@FTm!M@w;6 zgdRvc`-z=>$mC1Dgph(Z6#KNrdqSis{uE&a()3-XSK0$7q>B-|b%5=Qw6nk0*%wSc z;uka=cNuLccI$|D)bQI<+zP=DX=nd0`Gt2N^gubqdk}O;S0I+-{2Pz7v%i=8z~>NB zP)>380IUPjiHIw@fF9Hr@w&%w#zDFmv0qpCwUO2#o`>))(u)x9MzA9710QV|!XMNI z@nhZKzeie&I0F9H@C7JGZ1=@d{6P@>(I}_*TZB1CS0WC;nQH~o&VE+%zn(?df^v#| zaaQ{jX*#oc;kqZ=K%^;t3?Uq8Enz1)pdb z($4-+@{?{wh(S5UI}s)zU5fZR!VIKW_vg4%2xg>pksLP)VLp{3Rz_hDBJG2CG{P#R zD-gFG0N*&$&c0Cck;V?h*&5{(uSGb7w6o8%`yfnHhxwwM;wS{gV(=m2tqA1nq0-ot5Mq$_84e2=!HBdDaTdY^q;n8|fRKW81!B$!KBP8?a}eer zy$!Kj9M%YFAH=;8RwJ!NJOW`o(yI}dA{0}b$I&0*J*49iFG46mn&NE;`;g8V!Ew(a zR3N>UVuaI3pFy?4+~)`il;f@xCw~HKgEa1AaYGPl(1u4m10ewA6yuH-*8}M{5vwMG zUP${O{uCh$X^Ou`;E`5Lf*e7JM>+-Z9E3YayHCb7WrWp8#~_}Aa2jcfEeJ=DUX8c} z;R@0eA4T{B=?cW!DJVml;zWdRk*3&;a2aWeKSL-+x)kyHsh}0o6bC0@9!Q5Fo`A3( zX^IOFwjjL}@hOBvq`eYBbA$=h9`S-C@Ngmc0r74G1=5EQx1NT!NNW+#KnOsZ;x`d` zAWbpu)N$cR^Z6nLuMQ|noGblmK%kXfg8a+j=TZSAxP8b zGsU%g1lN=M$&K>%xFu!qQQ#lp==|w{ab>;i>%A?UH6E4gow4V!Xl90n@!iON3kWtd$9^vN28)O4E_bLYq}w z2w>R9j6v&ME}7wyA=v8HbkBJWf}%Y3qz}?1Thr2JepR$&`JXeWH5S;a*RQ98wDJ48NT^= zrT|0w1R^-&H-^nM1uZgxuZhux+UfA+4HbndVKI-zjn|EY^~~#*>J@gu9t?`y>{e@e_sHMG-i{8$hjw)^&HQ=u8`66o2oXiYOeyS-K z7nlch3ht%tq`~`*Y|8|D@c^UirbDH1PAJr@^H;&UrL| z(rBX0ro3D;xmf-ow9rnhAA07(b7#VfASsU$Q|gm>7$r3&-jtpXhtTxYTt}sIbu4rG zVcN#j{L~Cw1IKefr=(m%R(|%ZR8uESUb;S+yjuf0r6pzLrgj<_)!PBPerE@Krxy2( z9@V=Zu2H=mlScQb-gbHpq?P&K&;LjY+10T*k{%T@-SelGDy{^$c>wMU5r>r36>B!OU(^;aLFnHID9&6cNHVQR@eHJY+TaFo6`EVtIipX!^2VlJyl^4ygSErf zfBI`5b4g@m$9-veO*gr3FJ98@hIWZETf0OtzSu=BlgoYhJ8M2p9J|)i_wBt|H zavPnLftLg+!L7CaRO0E?EIKi5Rip7Oob>70 zj%FjMiF6{d9tkhF)d8Kdv)DW@ZdS=$v%f$s?nhBgysljc9skqkVf}u5vCWjb9hwhV;*$UK--FUlt_@DV zo%vDNdu8+HeEWR-jg*61dn(>7pZRNL@3-InHtTDv?VFD8>b}@@)%&#r=U;p2+JWyn zyyKPsQCH0yXLsLS^o#7|wdy6^*5z1id(_A8oly1N*lR?{YV(j;L;Lv7R)&s9?p4zB zhc8m!R?PB0_vGy(?l&@vUh>)g{)K@(E^qbUcEC-RH~qWJs^sMMeHW$|eZDnzVAjNh zXWy9g)vMZNTC2-Px&z*-@T1eWj9ziOM4Ng$``|C_wilSw2fnv&M&$ONr-yFX>0Y@= z$?LA(T#_Y+c)CNz3<^SM5C9ba7kV*%mFk z-I@G~f6%Y7e_s3S%=8g~Q2%#yc0VXav!|2dh7m$vR8bz zx43g8%m3T#Y4lL1mkk z$eL@H$eJ=XZr51>?px$n+|0A{z!$E&UAgqHZr7H)wXj0_=EvH0;JtQD6)hBgA=S@s zu=RO??;WTcoTKUc+~k&pKeJ8KP9>As6bkIgWm*+A_ocj2>w-w@O9fpNURqCAcL)xp z(#^$1+lC4?3O{Xgt*fg7NlxqT>NG1T+AqziwX>7}_9~!nq`ONHJ-tohiwnTp&xnxVLq#Xli zPMlKm>1)0fp0E8m>Dlz~8Na^OVbYhKuQvJZ(~9=d(OKI@uiBg2JtjEo^JhyA$Zzgh zqEOHE8l|zW@A_oYwceL&<$s4hvEz^OvA=wO)4kINJ5P8#aYgoeZqX;vBhK77|Jk@F zJT89H^5#DCqTS1fyytr1g2MaE|Qrw zevI{Wx~0q9@{ZU0%&r}=v-#L{72~Y_@-tWG9$NNB`t>%Cul1ejHm#;&)=O`_82?(^ z&^Ov&O%jwd+CKB=)yGyBoW1vC;oBQ-c>4|PyQBI0v-9R1`DIAonX5;Cbztq2-?{bg zc2 zed4S+$s#7s+_kO{XV9OGOSIOaMDx`)b4WBdS0za_7wvFSpv7nhYjwo~i=&qHcM@ng zqmi~}@=W4LQN5FPmy|o{cBpnH*q*WrG6=S8ZKigJJ+GBJ!8P~#ci`sqOV)OFz>zI$ z=`0|f06A|2CLckLgud_nee0%aqTeSqP1=2T?(U1SlXqoTU+THcdh?T-2;14k!zR8x zc$jBT^{YRxy}Lhs*oR*azuuy`@#g2N(^7?==F{)G|24Cji~H>PpQrz@?ZWE;MJ{K* zoBU?whi5-N{$u!=U71I^o;{Y*%<5yxKXt3Jsz*_k;nYVjzToQl!%fda`VG%bS$wYV z?DbzR(!aQ+_PIgHuWvm%X3X$Kzy8@W?bO;Q@4j|-Xz-6!t(Ubs^||KcpzWg)K3wzJ zs&yXgP0y~A{jom!tzm!rJ+}4LzF(NO-TC&jrQTP53)?bvT~K(7WM$s@o58)*vo5Xu zu=`1KpB?KrY`Rw))O%;k;axK({=6%H>Nm?SzMFD(=C1|*E7}Av`E$ibYrB+o?CqW| z|IxU|*u9yydhuM>AN1?GpVr)LvgY#qe6LsAJvJ+0sqAL=myZ7HyMr~C{Wm`Ta_{b5 zX`7$Vd+hV`x<0x{q_s_-%pz%^$C%-{^qyWPQCu_PqCYu@AsQHO8?>}SI&L^&uxG0x+E&{!p}qY zUF*s9@jcr2=~sW++SOfo zc5{32o?ywuSNghz1cygx`-KH#ySg@!jx$kmnTxM{?C9WD+Lq3Ra$onD)QqIrNv71` zcFb~7sC@m%+d}642-9?UndY${0B}-407I~$XxzO#qj8@NCyK1}B&Rcl0k@AbV8WYn zvQ1)d>Ad0M)$G2yM{1#})=lLTHl7*&#O52~k{k9pSQ71i9hdZ*Wh zR~8LV*zRNbap3mB@0`?K(S5(>$H}j5XkK_bHRSs6+fgmmllqh&nSAZ|xTQk3fL&)> z{k388PoF&3Y?EcfmMPaKxcu_UHp7<0=Pxv^UD@Gsr;j`rc6Ob!!gavC^j??ZTqY?_ z{8h>~5l^5oP zj{LdB`=uiXE*QKtqH~vbei;4gAa(L8O{<`#uXAH!CuUk}T5p%BM?@7I30n9{#r#th zK^HzZPgxMyJ*;7Lv#4j4`2NG zMNvCVJ5}4( zGfurdWPPdS-YISImPMA;@9jw+d2()>kn|@;#vLB9?z`l_Jcke1@owAg`ClpL_@x~C zvU`4;aaT9>oayJ5xN`k~m&&v4TIf@(R7pK8)To-N(?XiArw_@GePEbAf5~4P-gF5( zdFjKZ^|g@OBCVH03(4^^qEU<5AnG4z7;PU{FUHaz2IIR2t}<>&)vEyJm{g0A&p@W$@4RW$=Ij#1udm)!B& zl%KBUuE-vHdaC|}77f3*CkQJ34?SsLDP z^Uj0m6K`x8)3$Wa?YZaYc_j^>w&vq&ABK6npBjED<d3IPYT2*R7gNqGO!v9%k(4-M|MuW#mOQrk_?&CIhh*dwCtsQJmG^jk zt0^4~d&i&u;Jry_*Zh8~*}nb9_RTtWve${C4_Y7X(R|yJX_jjrzp*mM=eS%c?>m01 z$F0Xd`gze?S7wc$Iik<1Yr~??4%u21^7+{l!AsOY?-;#$*u$Pn1OQCJ+lxq^)6#KQ#N7h2yyRM@mxRXND`HQ6kedgxG?p~kPt!T=b zKgK5a@-sK>@o`DVvmHXNcKq0x4RPF1ERijGUYO~A)%uG5>)=gudz?S`=E)Jo%l}&c zd2n=r&*8RhZyo--<=B^Azml=|{glipU-ww{bDvdTx9Ml@`S(TNHdgPsmRTP@`@_}C zUj@%ee0j%({NHK1?CF^qf!l_^X8B|8*4k$IXM%b+xw`B3@1BUvo^{|izbCt$ z``n{)$Lpydzp;MAt%b{rbFK|nM!&bQXiS&vDJznyZ&qH6YyI1X$J^h1E;Fys&2G2a ze6y)j;o0qP^%(!hx!cFKmzDJYe8;W3M+dk(5z%kk)pz+%asy+#>Azf@a8^6ZzvGN; zy}c%V`sv6nW6D3NbUToJvUj`V_ul=u%IF-ZlSzP1VL~>4g_`r3#xKgqBqIU5&;A{!(hrz_< z_j^WvoBG4?`7PR?{Iu1Xr{?W0Ntrlmr@At{U7NPwB;1a<<-Jp`=KhSoG_GTI#Ho!j z+kTwxcjT)1M*DsKst|2RD0v`*Lqq!o_nlar^=5{4rjwNdpQznR@%41_g+AC9*+bi% zj2aiO&eB;$GitP6Lsy_RxVM~{L!L-Fw7|F8IjnezcNNUTKVcqTl+42jW0xQ17iV_e zl6oofe7jDkoq3%+woli`&hD}GeVE7A*Ws~!Fy5pC*{Q$ngYk~mGoLLerVVu%k)dJQ zaBYa)i2OfDyhr+R-&_+oYWVT4%k$st*7l|F(qEdMKGF8djIU%VmscL0z3s`=C7-^2 zN7%Dx-TJ?~G<#;^3XimAeiJ_2-D%R#H$$#Z=~+5&W7EpYaVfvO@zY|BNt2?_I9Tp} z`}e1(T^acP4IlF3`8nt`5Hs_#xoXtnDvZy|3+w`)R5-QL|ac=_zgKa6tz;)J(GZ{Ae=TdNmR)!p|07OL&?^9u!wH{482TRp*T(b2(q z@BRJpa~1n9%5*b#Zt=N#(=zXq*Yf^#>aF4aZ+N{ve%sV8L!UDr&1-(*mF&w?y^hzgBCmMq6uCR-)jDOa2SgfiCij&pQ4SLgiBf9Ln-`@Gk6zkfZ?=X-y@_w~F7Od~%I zYW)JuZl`+|<%G-;rkl>LOwO<`mfrbQSihA_&^#P1hPxA0Lar9~y0mCEy?8;oG;$#y z895-Q>N8}8q3_CFTIfhuYX_J7Lb>ERAS%zTiMr_1IHpt}Z_4vhvU8@Jf=BZ7D`Hbz z-(e6f>tG49Y4_r0b@NNJHc>|$X3rQf8BdcAOtSKBO3rXVat3dcoI+WPX9dML$q08AD0ED zPg@sbM8Xi;RVM?>zg3-^wl6(Qi&hKFs0KRGeukGQ~`U>cjxfIaeO!JpbAKP&hzh359$3CLT3>60`^^wLN`=QKZA(QWu3egcCY zov3ZoKju3eOmHLbd&-#f#_`v)yzJlDZ!KIAvulWY${_LVY2J*~o5e-ABge>u+b@>4 zuj5QvbVQ(y!(RulYR}bu|EACsJheFGQXgoc`|0dhsw$?Yt{U$U?YnLpx%-gx3)!~y zDfz+mx=JMzJ364QKf|RsGi_roX?3agKo7nEUc0+VLUmb|? zJt^LZpado#B^$i%NRaaJvpGymmAGYe+Ne8@bBx#cl$N6b(xFp#(b<_~Sl`Z3b_7Pr zQqwICbbLSgW&|`-0o7M6YRIMD#VS!0j}yxTp#qTd7A(_6Ap>dUf{_%F2;UD+gAH)V z4&-wD4L>MP(eVaP>zi!HmqOhLXD_w5lUkgeC2UM$Kw`r?zJq!e6}USxBE}YApxO zQEG{UAO556T8lZ`lq?xMtaAG?-?F0G;@t>)3DCvqfNeSQ2Wp5gm&W~!q>;egErVggCJZLfZ!IZ zT?Gb*t87B>KW6m*$~RktLHAzn501Wx1*K?+#n+~XLd+6LokVribcNMpzdj+xyyqt( zDkqxS^$xYoX*-gr`3}3KSeJVhO#>8K&o%Oi?%AEw^rqs8SCIl6 zW`%=bTD1aB@|I8o$<%Oi7+2syvF;htzUEzfjvj(sx&b2G3kmJ);%2kNwT4)X)as;% z*AX)6?~9e534Um}z@afj`5bxus>Q`FdBVw->&LqJveRp&*1J(+PtW|ChGsLHXZo-T zFQT?Y?s@u9RxnrgfX=(uL-^B~8p`UsV9s(nRZ91g>jqDrX}|L*lY_5Epb3`Z6AtOL zx2S}8^Xy$Mc*cT`!sPN#2%4``RL$h2zGw>_@#yHs&GgU6JTZ2Npzm289iFYBmzj4P zl)lRRA~|aKhk6XXqUdLJv@%SxWMM(4ruXyG0W%m>#MRN77AyoMo_R1~^ z=j|)WBO8WOVg@f%4Sg3lZs|R9F=tsHtF7GdJtlmK)XISh#V$QiMJrBw2$)>yH_kX% zD^!`0HJpS9%#YaQq7}eJi)~y)1=L#=$mw^Qg}xm~dz|sLoWZGW3m1QiP1;bv)_n6{ z1_CS_<{yAYHW&~b{1I`Q{7yJM*a?_65lKW`*4RQgU)t55w7t2J1pL^k0F<`v(;wS( zgLZzKbOUHd8^!oL+WmcTgoA?_?W-I3^npok76fb+!L2F4whDhayf%2}W)_xr(02FR zN>mu$_YFg!;!b%<9Cvx?5EKh#TBtcHJ#R%M>79y~bv@fwX)-j5rDoDiA14=gkG{8{ zq7j|5nqyqp^->>jT(b=;^`PBnqKFjQX6`;WXKycgw5AvBU$5);R?FEft`y0|8S|d1 zVqeb-dJ>s)@=1F+3YL`HH=k_5^031u1e9FoWqSUo=HZ~-{;G+IgtW3(Qcvwk#+J||cvm-tlc-61pt#&Vh zS%pVbB1-=K3VokChOvfGx$*<1EnOi-T!56*ctUi>wlB-ulF21CzxA-AXx-` zQ{J`Ptm|D_gxf|1YOD)Pu2nA_tOlU_8VxdLRqaj=>#C(4N={`x9lQFy%oP7zj*Z z?u@<6&9~hYZSSkF%IWPWSe=9o&gLKJtXf^7^_*X4^wL?{lkGwiQ%9NFrgo=*ZOY)SK>n~>!7<>B&y)hJRXt7CXIF}6U)f?Vt$C>XMVs8nA-9= zdUp7R695bAhXHdICHD7x+1P7O^yz?oT}ErJe|Tn0fQ8jC^3nHMCyhpUHdxXnEk#qs zUnhnX$HBsKU}3SjVb@^)KMI1L>z~xlhNVSMM_{xsUc>25%xRA|Ewrw!IGa!oE4Y`6 zJb%B2&Wba^DHD)Jv*oN$#&Ao#d}@PFvzWoKQShfgthjpr>Y>aL{Q>mMTZY&7CQUn) zUYEZ1J21g7YlM|YEtM$*cN3(TMf-h(Vos^I#t9ZNtoC(2Ar&sPLG&m^L8Wqco4b}H zW!Vo31=LMF+&dpi7vP+i>YIQ(UeRRDhjtP0js=B32)XHWhVyKoZh8pF=9rfhwuWH& zQ2c&4p1#w6m`=SGT@}ixM@^SENRiu5?cnD6>gsRR^F7XDVo0t!ShKVSFS{xYVaUDy zbuq@c@IA6@(ry1@5&ZT7s{RUmA~5A;=?;;q%I-pSII~ZL?@od|1bpUHaBt#}X?of~ ziWspzYL#GJ!^Qxu68m}Xr4wS3RsM8o=69Lh8% zA=Nuxj>ASNp&%tNhh!+QtLa189P50vHMj1tWM^g{^njpM*Dj?~`uROQ)tog~=FlnG z0)^lPlKnfXo`ul9YCo>dlhE-82H#Ha2*MWdR=}E(emm19_l&FHZ7Z+Qz35@^(rm4b eckEf+C%{YW9k^#~Er(0AY9?S5Ph`?YDSrd7nlOt1 diff --git a/libraries/tbb/tbbbind_2_5_debug.dll b/libraries/tbb/tbbbind_2_5_debug.dll deleted file mode 100644 index 1d06bba1af91218ec2e25a7767dc07b7c45a7ce6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128640 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 diff --git a/libraries/tbb/tbbbind_debug.dll b/libraries/tbb/tbbbind_debug.dll deleted file mode 100644 index 7bbdf323161d2c775fdfa6965f2b8ae11a300158..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 125568 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

%gg?zp<%L^h@4;|0(|1K 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 -Z@39YEiyyZ6>P&Qh_-gl>>7OfE^J#R( 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 diff --git a/libraries/tbb/tbbmalloc_debug.dll b/libraries/tbb/tbbmalloc_debug.dll deleted file mode 100644 index a07bbdde5b1bde9b0f6785eb03ed533b005e2eb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 336512 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 z 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%0D$Mv&(-RrpibzI*%u3sJ3r;h7Sm3-akxPDZv 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 VMKGO*efK+NK0XRgwg3PC001p;z@`8I diff --git a/libraries/tbb/tbbmalloc_proxy.dll b/libraries/tbb/tbbmalloc_proxy.dll deleted file mode 100644 index e61f83954dbf95585e58c2d0f8351b8e3d14c03d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83584 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 YE0H#PTk{?*H|xe!i?Y5l&iVQO130`DtN;K2 From e4dec7f8b4551ff5ae99dc15a09e82ac9eb33176 Mon Sep 17 00:00:00 2001 From: rvidye Date: Mon, 22 Jun 2026 15:38:32 -0600 Subject: [PATCH 2/7] modern target-based CMake with package-managed deps --- CMakeLists.txt | 148 +++++++++++++++- CMakePresets.json | 15 ++ external/CMakeLists.txt | 92 ++++------ src/CMakeLists.txt | 11 +- src/arches-v2/CMakeLists.txt | 165 +++++++++--------- src/arches-v2/stdafx.hpp | 4 +- .../units/ramulator/unit-addr-mapper.cpp | 42 ++++- src/arches-v2/units/unit-dram.hpp | 9 +- src/trax-kernel/CMakeLists.txt | 62 +++++-- vcpkg.json | 10 ++ 10 files changed, 377 insertions(+), 181 deletions(-) create mode 100644 CMakePresets.json create mode 100644 vcpkg.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 1989187..51a0b46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,155 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.21) -project(arches-v2 LANGUAGES CXX) +# vcpkg toolchain auto-detection (must run before project()) +# Precedence: +# 1. An explicit -DCMAKE_TOOLCHAIN_FILE=... +# 2. Otherwise, if the VCPKG_ROOT environment variable points at a vcpkg +# checkout, its toolchain is used automatically +# 3. Otherwise nothing is forced and find_package() resolves system packages. +# This never *requires* vcpkg; it only removes friction when VCPKG_ROOT is set. +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{VCPKG_ROOT}) + file(TO_CMAKE_PATH "$ENV{VCPKG_ROOT}" _arches_vcpkg_root) + string(TOLOWER "${_arches_vcpkg_root}" _arches_vcpkg_root_lower) + set(_arches_vcpkg_toolchain "${_arches_vcpkg_root}/scripts/buildsystems/vcpkg.cmake") + if(_arches_vcpkg_root_lower MATCHES "microsoft visual studio") + # Visual Studio Developer prompts set VCPKG_ROOT to the vcpkg copy bundled + # with VS. That copy is a frozen snapshot without git history and cannot + # reliably resolve this manifest, so it is skipped on purpose. + message(STATUS + "Arches: ignoring VCPKG_ROOT at '${_arches_vcpkg_root}' (vcpkg bundled with Visual Studio). " + "Use a standalone vcpkg clone instead: set VCPKG_ROOT to it or pass " + "-DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake (see STARTUP_GUIDE.md).") + elseif(EXISTS "${_arches_vcpkg_toolchain}") + set(CMAKE_TOOLCHAIN_FILE "${_arches_vcpkg_toolchain}" + CACHE FILEPATH "Vcpkg toolchain file (auto-detected from VCPKG_ROOT)") + message(STATUS "Arches: using vcpkg toolchain from VCPKG_ROOT (${_arches_vcpkg_root})") + endif() + unset(_arches_vcpkg_root) + unset(_arches_vcpkg_root_lower) + unset(_arches_vcpkg_toolchain) +endif() + +project(arches-v2 LANGUAGES CXX C) set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +if(CMAKE_CONFIGURATION_TYPES) + set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Supported configurations" FORCE) +endif() + +set(ARCHES_DATASET_DIR "${PROJECT_SOURCE_DIR}/datasets" CACHE PATH "Path to Arches datasets") +option(ARCHES_BUILD_LEGACY_KERNEL_TOOLS "Build standalone STRaTA and dual-streaming kernel tools" OFF) + +set(ARCHES_RUNTIME_DEBUG_DIR "${PROJECT_SOURCE_DIR}/build/debug") +set(ARCHES_RUNTIME_RELEASE_DIR "${PROJECT_SOURCE_DIR}/build/release") +set(ARCHES_RAMULATOR_CONFIG_DIR "${PROJECT_SOURCE_DIR}/src/arches-v2/units/ramulator/config-files") + +# The prebuilt kernel checked into the source tree is staged next to the executables. +set(ARCHES_DEFAULT_KERNEL_PATH "${PROJECT_SOURCE_DIR}/src/trax-kernel/riscv/kernel") + +find_package(TBB CONFIG QUIET) +find_package(yaml-cpp CONFIG QUIET) +find_package(spdlog CONFIG QUIET) +find_package(Threads REQUIRED) + +# Aggregate missing third-party dependencies into one actionable message +set(_arches_missing_deps "") +if(NOT TARGET TBB::tbb) + list(APPEND _arches_missing_deps "TBB") +endif() +if(NOT TARGET yaml-cpp::yaml-cpp AND NOT TARGET yaml-cpp) + list(APPEND _arches_missing_deps "yaml-cpp") +endif() +if(NOT TARGET spdlog::spdlog AND NOT TARGET spdlog) + list(APPEND _arches_missing_deps "spdlog") +endif() + +if(_arches_missing_deps) + string(REPLACE ";" ", " _arches_missing_str "${_arches_missing_deps}") + message(FATAL_ERROR + "Arches could not find required dependencies: ${_arches_missing_str}.\n" + "See STARTUP_GUIDE.md for setup. Quick options:\n" + " vcpkg: set the VCPKG_ROOT environment variable,\n" + " or pass -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake\n" + " Ubuntu/Debian: sudo apt install -y libtbb-dev libyaml-cpp-dev libspdlog-dev\n" + " Fedora: sudo dnf install tbb-devel yaml-cpp-devel spdlog-devel\n" + " Arch: sudo pacman -S onetbb yaml-cpp spdlog") +endif() + +if(TARGET yaml-cpp::yaml-cpp) + set(ARCHES_YAML_CPP_TARGET yaml-cpp::yaml-cpp) +elseif(TARGET yaml-cpp) + set(ARCHES_YAML_CPP_TARGET yaml-cpp) +else() + message(FATAL_ERROR "yaml-cpp package was found, but no known imported target exists.") +endif() + +if(TARGET spdlog::spdlog) + set(ARCHES_SPDLOG_TARGET spdlog::spdlog) +elseif(TARGET spdlog) + set(ARCHES_SPDLOG_TARGET spdlog) +else() + message(FATAL_ERROR "spdlog package was found, but no known imported target exists.") +endif() + +function(arches_set_output_dirs target_name) + set_target_properties(${target_name} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${ARCHES_RUNTIME_DEBUG_DIR}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${ARCHES_RUNTIME_RELEASE_DIR}" + ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/lib/Debug" + ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/lib/Release" + LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/lib/Debug" + LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/lib/Release" + PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/pdb/Debug" + PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/pdb/Release" + COMPILE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/pdb/Debug" + COMPILE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/pdb/Release" + ) +endfunction() + +function(arches_copy_runtime_dlls target_name) + if(WIN32) + set(copy_script "${CMAKE_CURRENT_BINARY_DIR}/${target_name}-copy-runtime-dlls-$.cmake") + file(GENERATE OUTPUT "${copy_script}" CONTENT +"set(_dlls \"$\") +foreach(_dll IN LISTS _dlls) + if(EXISTS \"\${_dll}\") + file(COPY \"\${_dll}\" DESTINATION \"$\") + endif() +endforeach() +" TARGET ${target_name}) + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -P "${copy_script}" + VERBATIM + ) + endif() +endfunction() + +function(arches_stage_runtime_assets target_name) + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "$/config-files" + COMMAND ${CMAKE_COMMAND} -E copy_directory "${ARCHES_RAMULATOR_CONFIG_DIR}" "$/config-files" + VERBATIM + ) + + if(EXISTS "${ARCHES_DEFAULT_KERNEL_PATH}") + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "$/kernels/trax" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ARCHES_DEFAULT_KERNEL_PATH}" "$/kernels/trax/kernel" + VERBATIM + ) + endif() +endfunction() add_subdirectory(external) add_subdirectory("src") + +if(TARGET arches) + set_property(DIRECTORY "${PROJECT_SOURCE_DIR}" PROPERTY VS_STARTUP_PROJECT arches) +endif() diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..c9305ae --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,15 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "windows", + "displayName": "Windows Visual Studio", + "generator": "Visual Studio 17 2022", + "architecture": "x64", + "binaryDir": "${sourceDir}/build/windows", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + } + ] +} diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index d713515..b1c9dfd 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,62 +1,40 @@ -add_subdirectory(ramulator2 EXCLUDE_FROM_ALL) - -# cmake for ramulator -function( build_source_group ) - file(GLOB_RECURSE files ${CMAKE_CURRENT_SOURCE_DIR}/*.* ) - list(REMOVE_ITEM files "CMakeLists.txt") - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "[src]" FILES ${files}) -endfunction() - -set_property(GLOBAL PROPERTY USE_FOLDERS ON) - -file(GLOB_RECURSE ALL_INCLUDE CONFIGURE_DEPENDS "*.hpp" "*.h") -file(GLOB_RECURSE ALL_SOURCES CONFIGURE_DEPENDS "*.cpp" "*.cc") - -set(EXCLUDED_FOLDERS "/dram_controller/impl/plugin" "/example/impl" "/resources/") -set(NEW_INCLUDE "") -set(NEW_SOURCE "") -foreach(Source IN LISTS ALL_INCLUDE) - set(should_exclude FALSE) - foreach(EXCLUDED_FOLDER ${EXCLUDED_FOLDERS}) - get_filename_component(SourceDir ${Source} DIRECTORY) - string(FIND ${SourceDir} ${EXCLUDED_FOLDER} IsExcludedFolder) - if(${IsExcludedFolder} GREATER -1) - set(should_exclude TRUE) - break() - endif() - endforeach() - - if (NOT should_exclude) - list(APPEND NEW_INCLUDE ${Source}) - endif() -endforeach() +set(ARCHES_RAMULATOR_SOURCES + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/base/factory.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/base/logging.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/base/request.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/base/stats.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/base/utils.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/frontend/impl/external_wrapper/gem5_frontend.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/translation/impl/no_translation.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/memory_system/impl/generic_DRAM_system.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/dram_controller/impl/rowpolicy/basic_rowpolicies.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/dram_controller/impl/scheduler/generic_scheduler.cpp" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src/dram_controller/impl/refresh/all_bank_refresh.cpp" + "${PROJECT_SOURCE_DIR}/src/arches-v2/units/ramulator/config.cpp" + "${PROJECT_SOURCE_DIR}/src/arches-v2/units/ramulator/unit-addr-mapper.cpp" + "${PROJECT_SOURCE_DIR}/src/arches-v2/units/ramulator/unit-GDDR6.cpp" + "${PROJECT_SOURCE_DIR}/src/arches-v2/units/ramulator/unit-generic-dram-controller.cpp" +) -foreach(Source IN LISTS ALL_SOURCES) - set(should_exclude FALSE) - foreach(EXCLUDED_FOLDER ${EXCLUDED_FOLDERS}) - get_filename_component(SourceDir ${Source} DIRECTORY) - string(FIND ${SourceDir} ${EXCLUDED_FOLDER} IsExcludedFolder) - if(${IsExcludedFolder} GREATER -1) - set(should_exclude TRUE) - break() - endif() - endforeach() +# Ramulator implementations self-register through translation-unit static initializers. +# Use an object library so the linker cannot drop registration-only objects from +# a static archive before Factory::create_* looks them up at runtime. +add_library(arches_ramulator OBJECT ${ARCHES_RAMULATOR_SOURCES}) +add_library(arches::ramulator ALIAS arches_ramulator) - if (NOT should_exclude) - list(APPEND NEW_SOURCES ${Source}) - endif() -endforeach() +target_compile_features(arches_ramulator PUBLIC cxx_std_20) -list(REMOVE_ITEM NEW_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ramulator2/src/base/config.cpp") +target_include_directories(arches_ramulator + PUBLIC + "${PROJECT_SOURCE_DIR}/external" + "${PROJECT_SOURCE_DIR}/external/ramulator2/src" +) -add_library(Ramulator STATIC ${NEW_INCLUDE} ${NEW_SOURCES}) -target_include_directories(Ramulator PUBLIC ${PROJECT_SOURCE_DIR}/external/ramulator2/src) -target_include_directories(Ramulator PUBLIC ${argparse_SOURCE_DIR}/include) -target_include_directories(Ramulator PUBLIC ${yaml-cpp_SOURCE_DIR}/include) -target_include_directories(Ramulator PUBLIC ${spdlog_SOURCE_DIR}/include) -target_link_libraries( - Ramulator - PUBLIC yaml-cpp - PUBLIC spdlog +target_link_libraries(arches_ramulator + PUBLIC + ${ARCHES_YAML_CPP_TARGET} + ${ARCHES_SPDLOG_TARGET} ) -build_source_group() \ No newline at end of file + +set_target_properties(arches_ramulator PROPERTIES FOLDER "Libraries") +source_group(TREE "${PROJECT_SOURCE_DIR}" FILES ${ARCHES_RAMULATOR_SOURCES}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fdada79..5175921 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ -cmake_minimum_required(VERSION 3.14) - -add_subdirectory("arches-v2") add_subdirectory("trax-kernel") -#add_subdirectory("strata-rt-kernel") -#add_subdirectory("dual-streaming-kernel") +add_subdirectory("arches-v2") + +if(ARCHES_BUILD_LEGACY_KERNEL_TOOLS) + add_subdirectory("dual-streaming-kernel") + add_subdirectory("strata-rt-kernel") +endif() diff --git a/src/arches-v2/CMakeLists.txt b/src/arches-v2/CMakeLists.txt index e2067c8..3e9ccef 100644 --- a/src/arches-v2/CMakeLists.txt +++ b/src/arches-v2/CMakeLists.txt @@ -1,85 +1,80 @@ -cmake_minimum_required(VERSION 3.14) -add_compile_definitions(UNICODE _UNICODE) - -set(PROJECT_NAME "arches-v2") - -if(LINUX) - # enable intrinsics like _lzcnt_u64 - add_compile_options(-march=native) -endif() - -file(GLOB_RECURSE ALL_INCLUDE CONFIGURE_DEPENDS "*.hpp" "*.h") -file(GLOB_RECURSE ALL_SOURCES CONFIGURE_DEPENDS "*.cpp" "*.cc") -file(GLOB_RECURSE USIMM_CONFIG CONFIGURE_DEPENDS "*.vi" "*.cfg") -file(GLOB_RECURSE RAMU_CONFIG CONFIGURE_DEPENDS "*.yaml") - -# Exclude the Trax Unit Folder -foreach(Source IN LISTS ALL_INCLUDE ALL_SOURCES USIMM_CONFIG RAMU_CONFIG) - get_filename_component(SourceDir ${Source} DIRECTORY) - string(FIND ${SourceDir} "/units/temp" IsExcludedFolder) - if(${IsExcludedFolder} EQUAL -1) - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${Source}) - endif() -endforeach() - -# Exclude the Trax Folder -set(EXCLUDED_FOLDER "/units/temp") -set(NEW_INCLUDE "") -set(NEW_SOURCE "") -foreach(Source IN LISTS ALL_INCLUDE) - get_filename_component(SourceDir ${Source} DIRECTORY) - - string(FIND ${SourceDir} ${EXCLUDED_FOLDER} IsExcludedFolder) - if(${IsExcludedFolder} EQUAL -1) - list(APPEND NEW_INCLUDE ${Source}) - endif() - -endforeach() - -foreach(Source IN LISTS ALL_SOURCES) - get_filename_component(SourceDir ${Source} DIRECTORY) - - string(FIND ${SourceDir} ${EXCLUDED_FOLDER} IsExcludedFolder) - if(${IsExcludedFolder} EQUAL -1) - list(APPEND NEW_SOURCES ${Source}) - endif() -endforeach() - - -add_executable(${PROJECT_NAME} ${NEW_INCLUDE} ${NEW_SOURCES} ${USIMM_CONFIG} ${RAMU_CONFIG}) - -# add_executable(${PROJECT_NAME} ${ALL_INCLUDE} ${ALL_SOURCES}) -# source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${ALL_INCLUDE} ${ALL_SOURCES}) - -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/external) -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/src/arches-v2) -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include) -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/src) -set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT DISABLE) - -set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) -if(WINDOWS) - target_link_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/libraries/tbb) - target_link_libraries(${PROJECT_NAME} PRIVATE tbb12.lib) -elseif(LINUX) - target_link_libraries(${PROJECT_NAME} PRIVATE tbb) -endif() -target_link_libraries(${PROJECT_NAME} PRIVATE Ramulator) -#set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) -#set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}) -set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${PROJECT_NAME}) - -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/units/usimm/config-files) - file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/units/usimm/config-files DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -endif() - -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/units/ramulator/config-files) - file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/units/ramulator/config-files DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -endif() - -file(GLOB TBB_DLLS ${PROJECT_SOURCE_DIR}/libraries/tbb/*) -set(RELEASE_DIR ${CMAKE_BINARY_DIR}/Release) -# Check if the destination directory exists, and if not, create it -file(MAKE_DIRECTORY ${RELEASE_DIR}) -file(COPY ${TBB_DLLS} DESTINATION ${RELEASE_DIR}) -file(COPY ${TBB_DLLS} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) \ No newline at end of file +set(ARCHES_SOURCES + "main.cpp" + "isa/registers.cpp" + "isa/riscv.cpp" + "simulator/simulator.cpp" + "units/trax/unit-rt-core.cpp" + "units/unit-cache.cpp" + "units/unit-cache-base.cpp" + "units/unit-dram.cpp" + "units/unit-stream-cache.cpp" + "units/unit-texture.cpp" + "units/unit-tp.cpp" + "util/elf.cpp" + "util/file.cpp" + "util/stbi.cpp" + "util/string.cpp" +) + +file(GLOB_RECURSE ARCHES_HEADERS CONFIGURE_DEPENDS "*.hpp" "*.h") +file(GLOB ARCHES_RAMULATOR_CONFIG_FILES CONFIGURE_DEPENDS "units/ramulator/config-files/*.yaml") + +add_executable(arches + ${ARCHES_SOURCES} + ${ARCHES_HEADERS} + ${ARCHES_RAMULATOR_CONFIG_FILES} +) + +set_target_properties(arches PROPERTIES + OUTPUT_NAME "arches" + FOLDER "Applications" + VS_DEBUGGER_WORKING_DIRECTORY "$" + VS_DEBUGGER_COMMAND_ARGUMENTS "--scene-name=cornell-box --framebuffer-width=16 --framebuffer-height=16" +) + +arches_set_output_dirs(arches) + +target_compile_features(arches PRIVATE cxx_std_20) + +file(TO_CMAKE_PATH "${PROJECT_SOURCE_DIR}" ARCHES_PROJECT_SOURCE_DIR_CMAKE) +file(TO_CMAKE_PATH "${ARCHES_DATASET_DIR}" ARCHES_DEFAULT_DATASET_DIR_CMAKE) +file(TO_CMAKE_PATH "${ARCHES_RAMULATOR_CONFIG_DIR}" ARCHES_RAMULATOR_CONFIG_DIR_CMAKE) +file(TO_CMAKE_PATH "${ARCHES_DEFAULT_KERNEL_PATH}" ARCHES_DEFAULT_KERNEL_PATH_CMAKE) + +target_compile_definitions(arches + PRIVATE + UNICODE + _UNICODE + ARCHES_PROJECT_SOURCE_DIR="${ARCHES_PROJECT_SOURCE_DIR_CMAKE}" + ARCHES_DEFAULT_DATASET_DIR="${ARCHES_DEFAULT_DATASET_DIR_CMAKE}" + ARCHES_DEFAULT_CONFIG_DIR="${ARCHES_RAMULATOR_CONFIG_DIR_CMAKE}" + ARCHES_DEFAULT_KERNEL_PATH="${ARCHES_DEFAULT_KERNEL_PATH_CMAKE}" +) + +target_compile_options(arches + PRIVATE + $<$:/bigobj> + $<$,$,$>>:-march=native> +) + +target_include_directories(arches + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}/src" + "${PROJECT_SOURCE_DIR}/include" + "${PROJECT_SOURCE_DIR}/external" +) + +# stdc++fs: std::filesystem lives in a separate library on GCC < 9. +target_link_libraries(arches + PRIVATE + TBB::tbb + Threads::Threads + arches::ramulator + $<$,$,9.0>>:stdc++fs> +) + +arches_stage_runtime_assets(arches) +arches_copy_runtime_dlls(arches) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${ARCHES_SOURCES} ${ARCHES_HEADERS} ${ARCHES_RAMULATOR_CONFIG_FILES}) diff --git a/src/arches-v2/stdafx.hpp b/src/arches-v2/stdafx.hpp index 4e08865..dc00bbc 100644 --- a/src/arches-v2/stdafx.hpp +++ b/src/arches-v2/stdafx.hpp @@ -132,7 +132,7 @@ #define NOMINMAX #ifndef _DEBUG -#include "oneapi/tbb.h" +#include #endif #include @@ -202,4 +202,4 @@ static_assert(sizeof(float)==4&&sizeof(double)==8,"Not Implemented!"); #define CACHE_BLOCK_SIZE 128 #define CACHE_SECTOR_SIZE 32 -} \ No newline at end of file +} diff --git a/src/arches-v2/units/ramulator/unit-addr-mapper.cpp b/src/arches-v2/units/ramulator/unit-addr-mapper.cpp index 63eadcb..907361c 100644 --- a/src/arches-v2/units/ramulator/unit-addr-mapper.cpp +++ b/src/arches-v2/units/ramulator/unit-addr-mapper.cpp @@ -4,18 +4,48 @@ #include "ramulator2/src/dram/dram.h" #include "ramulator2/src/addr_mapper/addr_mapper.h" #include "ramulator2/src/memory_system/memory_system.h" -#include "ramulator2/src/addr_mapper/impl/linear_mappers.cpp" namespace Ramulator { -class RoRaBaChCo final : public LinearMapperBase, public Implementation { +// Base for Arches' custom address mappers. Ramulator's IAddrMapper interface +// (addr_mapper.h) ships no shared base for the standard "linear" setup() — upstream +// keeps that in linear_mappers.cpp with no header, and the submodule is read-only. +// So we implement the little setup() our mappers need: derive each level's address-bit +// width and the transaction offset from the DRAM organization. +class ArchesLinearMapperBase : public IAddrMapper { +public: + IDRAM* m_dram = nullptr; + + int m_num_levels = -1; + std::vector m_addr_bits; + Addr_t m_tx_offset = -1; + +protected: + void setup(IFrontEnd*, IMemorySystem* memory_system) { + m_dram = memory_system->get_ifce(); + + const auto& count = m_dram->m_organization.count; + m_num_levels = static_cast(count.size()); + m_addr_bits.resize(m_num_levels); + for (size_t level = 0; level < m_addr_bits.size(); level++) { + m_addr_bits[level] = calc_log2(count[level]); + } + + m_addr_bits[m_num_levels - 1] -= calc_log2(m_dram->m_internal_prefetch_size); + + int tx_bytes = m_dram->m_internal_prefetch_size * m_dram->m_channel_width / 8; + m_tx_offset = calc_log2(tx_bytes); + } +}; + +class RoRaBaChCo final : public ArchesLinearMapperBase, public Implementation { RAMULATOR_REGISTER_IMPLEMENTATION(IAddrMapper, RoRaBaChCo, "RoRaBaChCo", "Applies a RoRaBaChCo mapping to the address."); public: void init() override { }; void setup(IFrontEnd* frontend, IMemorySystem* memory_system) override { - LinearMapperBase::setup(frontend, memory_system); + ArchesLinearMapperBase::setup(frontend, memory_system); } void apply(Request& req) override { @@ -29,14 +59,14 @@ class RoRaBaChCo final : public LinearMapperBase, public Implementation { } }; -class RoBgBaRaChCo final : public LinearMapperBase, public Implementation { +class RoBgBaRaChCo final : public ArchesLinearMapperBase, public Implementation { RAMULATOR_REGISTER_IMPLEMENTATION(IAddrMapper, RoBgBaRaChCo, "RoBgBaRaChCo", "Applies a RoBgBaRaChCo mapping to the address."); public: void init() override { }; void setup(IFrontEnd* frontend, IMemorySystem* memory_system) override { - LinearMapperBase::setup(frontend, memory_system); + ArchesLinearMapperBase::setup(frontend, memory_system); } void apply(Request& req) override { @@ -51,4 +81,4 @@ class RoBgBaRaChCo final : public LinearMapperBase, public Implementation { } }; -} // namespace Ramulator \ No newline at end of file +} // namespace Ramulator diff --git a/src/arches-v2/units/unit-dram.hpp b/src/arches-v2/units/unit-dram.hpp index ca2fd67..c13341d 100644 --- a/src/arches-v2/units/unit-dram.hpp +++ b/src/arches-v2/units/unit-dram.hpp @@ -10,13 +10,6 @@ #include #include #include -#include -#include -#include -#include -#include "ramulator/unit-generic-dram-controller.cpp" -#include -#include #ifdef uint #undef uint @@ -154,4 +147,4 @@ class UnitDRAMRamulator : public UnitMainMemoryBase } }; -}} \ No newline at end of file +}} diff --git a/src/trax-kernel/CMakeLists.txt b/src/trax-kernel/CMakeLists.txt index 04fdaf3..c5361de 100644 --- a/src/trax-kernel/CMakeLists.txt +++ b/src/trax-kernel/CMakeLists.txt @@ -1,21 +1,51 @@ -cmake_minimum_required(VERSION 3.14) +set(TRAX_SOURCES + "main.cpp" + "stbi.cpp" +) -set(PROJECT_NAME "trax-kernel") +set(TRAX_HEADERS + "custom-instr.hpp" + "include.hpp" + "intersect.hpp" + "stdafx.hpp" +) -if(LINUX) - # enable intrinsics like _lzcnt_u64 - find this out - add_compile_options(-march=native) -endif() +add_executable(trax ${TRAX_SOURCES} ${TRAX_HEADERS}) -file(GLOB FILES *.hpp *.cpp) -add_executable(${PROJECT_NAME} ${FILES}) -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/trax-kernel) -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include) +set_target_properties(trax PROPERTIES + OUTPUT_NAME "trax" + FOLDER "Applications" + VS_DEBUGGER_WORKING_DIRECTORY "$" + VS_DEBUGGER_COMMAND_ARGUMENTS "--scene-name=sponza --framebuffer-width=16 --framebuffer-height=16" +) -set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) -set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}) -set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${PROJECT_NAME}) +arches_set_output_dirs(trax) -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/riscv) - file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/riscv DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -endif() +target_compile_features(trax PRIVATE cxx_std_20) + +file(TO_CMAKE_PATH "${ARCHES_DATASET_DIR}" ARCHES_DEFAULT_DATASET_DIR_CMAKE) +target_compile_definitions(trax + PRIVATE + ARCHES_DEFAULT_DATASET_DIR="${ARCHES_DEFAULT_DATASET_DIR_CMAKE}" +) + +target_compile_options(trax + PRIVATE + $<$:/bigobj> + $<$,$,$>>:-march=native> +) + +target_include_directories(trax + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}/include" +) + +# stdc++fs: std::filesystem lives in a separate library on GCC < 9. +target_link_libraries(trax + PRIVATE + Threads::Threads + $<$,$,9.0>>:stdc++fs> +) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${TRAX_SOURCES} ${TRAX_HEADERS}) diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..e72ce92 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,10 @@ +{ + "name": "arches", + "version-string": "0.1.0", + "builtin-baseline": "1d021d236ed1da9923826f99f420bd0370e6aad0", + "dependencies": [ + "tbb", + "yaml-cpp", + "spdlog" + ] +} From 2c382671e0d4d0ba5b6797f96843aa3583f7bb49 Mon Sep 17 00:00:00 2001 From: rvidye Date: Thu, 25 Jun 2026 12:11:10 -0600 Subject: [PATCH 3/7] unified-cli --- include/sim-config.hpp | 183 ++++++++++++++++++++ src/arches-v2/CMakeLists.txt | 2 - src/arches-v2/main.cpp | 27 +-- src/arches-v2/shared-utils.hpp | 302 ++++++++------------------------- src/trax-kernel/main.cpp | 46 +++-- 5 files changed, 285 insertions(+), 275 deletions(-) create mode 100644 include/sim-config.hpp diff --git a/include/sim-config.hpp b/include/sim-config.hpp new file mode 100644 index 0000000..f4bc425 --- /dev/null +++ b/include/sim-config.hpp @@ -0,0 +1,183 @@ +#pragma once + +// Shared command-line configuration for Arches and Native Kernel Executables (trax). +// Both parse same --key=value argumets and derive the same scene/camera setup +// Host-only do not include in the RISC-V kernel builds + +#include +#include +#include +#include +#include + +#include "rtm/rtm.hpp" + +namespace Arches { + +const static std::vector arch_names = {"TRaX", "STRaTA", "STRaTA-RT", "Dual-Streaming", "RIC"}; + +struct SceneConfig +{ + std::string name; + rtm::vec3 cam_pos; + rtm::vec3 cam_target; + float focal_length; +}; + +const static std::vector scene_configs = +{ + {"cornell-box", rtm::vec3(0, 0.8, 1.8), rtm::vec3(0, 0.8, 0), 12.0f}, + {"sibenik", rtm::vec3(3.0, -13.0, 0.0), rtm::vec3(0, -12.0, 0), 12.0f}, + {"crytek-sponza", rtm::vec3(-900.6f, 150.8f, 120.74f), rtm::vec3(79.7f, 14.0f, -17.4f), 12.0f}, + {"bistro-interior", rtm::vec3(-0.813307f, 2.0811f, -1.28115f), rtm::vec3(0.186693f, 2.0811f, -1.28115f), 24.0f}, + {"intel-sponza", rtm::vec3(-900.6f, 150.8f, 120.74f), rtm::vec3(79.7f, 14.0f, -17.4f), 12.0f}, + {"sponza", rtm::vec3(0.0f, 2.0f, 0.0f), rtm::vec3(90.0f, 0.0f, -1.0f), 12.0f}, + {"san-miguel", rtm::vec3(7.448, 1.014, 12.357), rtm::vec3(8.056, 1.04, 11.563), 12.0f}, + {"hairball", rtm::vec3(0, 0, 10), rtm::vec3(0, 0, 0), 24.0f}, + {"bistro", rtm::vec3(-8.0, 2.0, 2.0), rtm::vec3(0.0f, 1.0f, -1.0f), 12.0f}, +}; + +class SimulationConfig +{ +public: + struct Param + { + enum Type { INT, FLOAT, STRING } type; + union { int i; float f; }; + std::string s; + + Param() {} + }; + + rtm::Camera camera; + +private: + std::map _params; + + static std::string default_dataset_dir() + { +#ifdef ARCHES_DEFAULT_DATASET_DIR + return ARCHES_DEFAULT_DATASET_DIR; +#else + return "datasets"; +#endif + } + +public: + // Defaults common to every Arches executable. + // Application-specific params (e.g. the simulator's config-dir/kernel-path) are + // registered by the caller with set_param() before parse(). + SimulationConfig() + { + set_param("logging-interval", 10000); + set_param("arch-name", "TRaX"); + set_param("max-rays", 128); + set_param("dataset-dir", default_dataset_dir()); + set_param("scene-name", "sponza"); + set_param("framebuffer-width", 1024); + set_param("framebuffer-height", 1024); + set_param("pregen-rays", 0); + set_param("pregen-bounce", 1); + set_param("bvh-preset", 0); + set_param("bvh-merging", 0); + } + + // Parse --key=value args, resolve arch-id/scene-id, build the camera, and print. + void parse(int argc, char* argv[]) + { + for(int i = 1; i < argc; ++i) + { + std::string arg(argv[i]); + size_t split_pos = arg.find('='); + if(arg.rfind("--", 0) != 0 || split_pos == std::string::npos) + { + printf("Error: malformed argument '%s' (expected --key=value)\n", arg.c_str()); + exit(1); + } + parse_param(arg.substr(2, split_pos - 2), arg.substr(split_pos + 1)); + } + + set_param("arch-id", index_of(arch_names, get_string("arch-name"), "arch-name")); + + int scene_id = -1; + for(int i = 0; i < (int)scene_configs.size(); ++i) + if(scene_configs[i].name == get_string("scene-name")) + scene_id = i; + if(scene_id == -1) + { + printf("Error: unknown scene-name '%s'\n", get_string("scene-name").c_str()); + exit(1); + } + set_param("scene-id", scene_id); + + camera = rtm::Camera(get_int("framebuffer-width"), get_int("framebuffer-height"), + scene_configs[scene_id].focal_length, scene_configs[scene_id].cam_pos, scene_configs[scene_id].cam_target); + + print(); + } + + int get_int(const std::string& key) const { return find(key, Param::INT)->second.i; } + float get_float(const std::string& key) const { return find(key, Param::FLOAT)->second.f; } + std::string get_string(const std::string& key) const { return find(key, Param::STRING)->second.s; } + + void set_param(const std::string& key, int value) { _params[key].type = Param::INT; _params[key].i = value; } + void set_param(const std::string& key, float value) { _params[key].type = Param::FLOAT; _params[key].f = value; } + void set_param(const std::string& key, const std::string& value) { _params[key].type = Param::STRING; _params[key].s = value; } + + void parse_param(const std::string& key, const std::string& str) + { + auto a = _params.find(key); + if(a == _params.end()) + { + printf("Error: unknown parameter '--%s'\n", key.c_str()); + exit(1); + } + + try + { + if(a->second.type == Param::INT) a->second.i = std::stoi(str); + else if(a->second.type == Param::FLOAT) a->second.f = std::stof(str); + else a->second.s = str; + } + catch(const std::exception&) + { + printf("Error: invalid value '%s' for parameter '--%s'\n", str.c_str(), key.c_str()); + exit(1); + } + } + + void print() const + { + printf("Simulation Parameters\n"); + for(const auto& a : _params) + { + if(a.second.type == Param::INT) printf("%s: %d\n", a.first.c_str(), a.second.i); + else if(a.second.type == Param::FLOAT) printf("%s: %f\n", a.first.c_str(), a.second.f); + else printf("%s: %s\n", a.first.c_str(), a.second.s.c_str()); + } + printf("\n"); + } + +private: + std::map::const_iterator find(const std::string& key, Param::Type type) const + { + auto a = _params.find(key); + if(a == _params.end() || a->second.type != type) + { + printf("Error: missing or wrong-typed parameter '%s'\n", key.c_str()); + exit(1); + } + return a; + } + + static int index_of(const std::vector& names, const std::string& value, const char* what) + { + for(int i = 0; i < (int)names.size(); ++i) + if(names[i] == value) + return i; + printf("Error: unknown %s '%s'\n", what, value.c_str()); + exit(1); + } +}; + +} diff --git a/src/arches-v2/CMakeLists.txt b/src/arches-v2/CMakeLists.txt index 3e9ccef..718d712 100644 --- a/src/arches-v2/CMakeLists.txt +++ b/src/arches-v2/CMakeLists.txt @@ -36,7 +36,6 @@ arches_set_output_dirs(arches) target_compile_features(arches PRIVATE cxx_std_20) -file(TO_CMAKE_PATH "${PROJECT_SOURCE_DIR}" ARCHES_PROJECT_SOURCE_DIR_CMAKE) file(TO_CMAKE_PATH "${ARCHES_DATASET_DIR}" ARCHES_DEFAULT_DATASET_DIR_CMAKE) file(TO_CMAKE_PATH "${ARCHES_RAMULATOR_CONFIG_DIR}" ARCHES_RAMULATOR_CONFIG_DIR_CMAKE) file(TO_CMAKE_PATH "${ARCHES_DEFAULT_KERNEL_PATH}" ARCHES_DEFAULT_KERNEL_PATH_CMAKE) @@ -45,7 +44,6 @@ target_compile_definitions(arches PRIVATE UNICODE _UNICODE - ARCHES_PROJECT_SOURCE_DIR="${ARCHES_PROJECT_SOURCE_DIR_CMAKE}" ARCHES_DEFAULT_DATASET_DIR="${ARCHES_DEFAULT_DATASET_DIR_CMAKE}" ARCHES_DEFAULT_CONFIG_DIR="${ARCHES_RAMULATOR_CONFIG_DIR_CMAKE}" ARCHES_DEFAULT_KERNEL_PATH="${ARCHES_DEFAULT_KERNEL_PATH_CMAKE}" diff --git a/src/arches-v2/main.cpp b/src/arches-v2/main.cpp index 6ee3e2a..c2c2e6d 100644 --- a/src/arches-v2/main.cpp +++ b/src/arches-v2/main.cpp @@ -150,9 +150,8 @@ typedef Units::TRaX::UnitRTCore UnitRTCore; static TRaXKernelArgs initilize_buffers(Units::UnitMainMemoryBase** drams, const Units::UnitCrossbar& xbar, paddr_t& heap_address, const SimulationConfig& sim_config, uint page_size) { std::string scene_name = sim_config.get_string("scene-name"); - std::string project_folder = get_project_folder_path(); - std::string datasets_folder = sim_config.get_string("dataset-dir") + "/"; - std::string cache_folder = sim_config.get_string("dataset-dir") + "/cache/"; + std::string datasets_folder = ensure_trailing_slash(sim_config.get_string("dataset-dir")); + std::string cache_folder = datasets_folder + "cache/"; TRaXKernelArgs args{}; args.framebuffer_width = sim_config.get_int("framebuffer-width"); @@ -226,8 +225,6 @@ static TRaXKernelArgs initilize_buffers(Units::UnitMainMemoryBase** drams, const static void run_sim_trax(SimulationConfig& sim_config) { - std::string project_folder_path = get_project_folder_path(); - #if 0 //RTX 4090 ish //Compute double core_clock = 2235.0e6; @@ -244,7 +241,7 @@ static void run_sim_trax(SimulationConfig& sim_config) //DRAM UnitDRAM::Configuration dram_config; - dram_config.config_path = project_folder_path + "build\\src\\arches-v2\\config-files\\gddr6x_21000_config.yaml"; + dram_config.config_path = join_path(sim_config.get_string("config-dir"), "gddr6x_21000_config.yaml"); dram_config.size = 1ull << 30; //1GB per partition dram_config.clock_ratio = dram_clock / core_clock; dram_config.latency = 254; @@ -305,7 +302,7 @@ static void run_sim_trax(SimulationConfig& sim_config) //DRAM UnitDRAM::Configuration dram_config; - dram_config.config_path = project_folder_path + "build\\src\\arches-v2\\config-files\\gddr6_14000_config.yaml"; + dram_config.config_path = join_path(sim_config.get_string("config-dir"), "gddr6_14000_config.yaml"); dram_config.size = 1ull << 30; //1GB per partition dram_config.clock_ratio = dram_clock / core_clock; dram_config.latency = 254; @@ -375,7 +372,7 @@ static void run_sim_trax(SimulationConfig& sim_config) //DRAM UnitDRAM::Configuration dram_config; - dram_config.config_path = project_folder_path + "build/src/arches-v2/config-files/gddr6_14000_config.yaml"; + dram_config.config_path = join_path(sim_config.get_string("config-dir"), "gddr6_14000_config.yaml"); dram_config.size = 1ull << 30; //1GB dram_config.clock_ratio = dram_clock / core_clock; dram_config.latency = 92; @@ -433,7 +430,7 @@ static void run_sim_trax(SimulationConfig& sim_config) //DRAM UnitDRAM::Configuration dram_config; - dram_config.config_path = project_folder_path + "build\\src\\arches-v2\\config-files\\gddr6_pch_config.yaml"; + dram_config.config_path = join_path(sim_config.get_string("config-dir"), "gddr6_pch_config.yaml"); dram_config.size = 1ull << 30; //1GB dram_config.clock_ratio = dram_clock / core_clock; dram_config.latency = 1; @@ -474,7 +471,7 @@ static void run_sim_trax(SimulationConfig& sim_config) UnitL1Cache::PowerConfig l1d_power_config; #endif - ELF elf(project_folder_path + "src/trax-kernel/riscv/kernel"); + ELF elf(sim_config.get_string("kernel-path")); ISA::RISCV::InstructionTypeNameDatabase::get_instance()[ISA::RISCV::InstrType::CUSTOM0] = "FCHTHRD"; ISA::RISCV::InstructionTypeNameDatabase::get_instance()[ISA::RISCV::InstrType::CUSTOM1] = "BOXISECT"; @@ -793,7 +790,7 @@ static void run_sim_trax(SimulationConfig& sim_config) printf("MSIPS: %.2f\n", simulator.current_cycle * tps.size() / simulation_time / 1'000'000.0); stbi_flip_vertically_on_write(true); - stbi_write_png("out.png", (int)kernel_args.framebuffer_width, (int)kernel_args.framebuffer_height, 4, vec_mem.data() + (size_t)kernel_args.framebuffer, 0); + stbi_write_png("arches-out.png", (int)kernel_args.framebuffer_width, (int)kernel_args.framebuffer_height, 4, vec_mem.data() + (size_t)kernel_args.framebuffer, 0); for(auto& tp : tps) delete tp; for(auto& sfu : sfus) delete sfu; @@ -807,7 +804,11 @@ static void run_sim_trax(SimulationConfig& sim_config) int main(int argc, char* argv[]) { Arches::set_full_exe_name(argv[0]); - Arches::SimulationConfig sim_config(argc, argv); + Arches::SimulationConfig sim_config; + sim_config.set_param("config-dir", Arches::get_default_config_dir()); + sim_config.set_param("kernel-path", Arches::get_default_kernel_path()); + sim_config.parse(argc, argv); + Arches::TRaX::run_sim_trax(sim_config); return 0; -} \ No newline at end of file +} diff --git a/src/arches-v2/shared-utils.hpp b/src/arches-v2/shared-utils.hpp index 87caeaf..81d2a05 100644 --- a/src/arches-v2/shared-utils.hpp +++ b/src/arches-v2/shared-utils.hpp @@ -1,6 +1,8 @@ #pragma once #include "stdafx.hpp" +#include + #include "simulator/simulator.hpp" #include "units/unit-dram.hpp" @@ -15,6 +17,7 @@ #include "util/elf.hpp" #include "isa/riscv.hpp" #include "rtm/rtm.hpp" +#include "sim-config.hpp" #if defined BUILD_PLATFORM_WINDOWS #include @@ -31,12 +34,71 @@ void set_full_exe_name(const char *name) { strcpy(full_exe_name, name); } -std::string get_project_folder_path() +static std::filesystem::path get_executable_path() +{ + std::filesystem::path executable_path(full_exe_name); + if(executable_path.empty()) + return std::filesystem::current_path(); + + if(executable_path.is_relative()) + executable_path = std::filesystem::absolute(executable_path); + + return executable_path.lexically_normal(); +} + +static std::filesystem::path get_executable_folder() +{ + std::filesystem::path executable_path = get_executable_path(); + if(std::filesystem::is_directory(executable_path)) + return executable_path; + + return executable_path.parent_path(); +} + +static std::string normalize_path_string(const std::filesystem::path& path) +{ + return path.lexically_normal().generic_string(); +} + +static std::string ensure_trailing_slash(std::string path) +{ + for(char& c : path) + { + if(c == '\\') + c = '/'; + } + + while(path.size() > 1 && path.back() == '/') + path.pop_back(); + + if(!path.empty()) + path += "/"; + + return path; +} + +// CMake stages config-files/ and kernels/ next to the executable, so prefer those, otherwise use the source-tree paths baked in at configure time. +std::string get_default_config_dir() +{ + std::filesystem::path runtime_config_dir = get_executable_folder() / "config-files"; + if(std::filesystem::exists(runtime_config_dir)) + return normalize_path_string(runtime_config_dir); + + return normalize_path_string(std::filesystem::path(ARCHES_DEFAULT_CONFIG_DIR)); +} + +std::string get_default_kernel_path() +{ + std::filesystem::path runtime_kernel_path = get_executable_folder() / "kernels" / "trax" / "kernel"; + if(std::filesystem::exists(runtime_kernel_path)) + return normalize_path_string(runtime_kernel_path); + + return normalize_path_string(std::filesystem::path(ARCHES_DEFAULT_KERNEL_PATH)); +} + +std::string join_path(std::string dir, const std::string& name) { - // CHAR path[MAX_PATH]; - // GetModuleFileNameA(NULL, path, MAX_PATH); - std::string executable_path(full_exe_name); - return executable_path.substr(0, executable_path.rfind("build")); + return ensure_trailing_slash(dir) + name; } template @@ -119,235 +181,5 @@ void print_header(std::string string, uint header_length = 80) printf("\n"); } -const static std::vector arch_names = {"TRaX", "STRaTA", "STRaTA-RT", "Dual-Streaming", "RIC"}; - - -struct SceneConfig -{ - std::string name; - rtm::vec3 cam_pos; - rtm::vec3 cam_target; - float focal_length; -}; - -const static std::vector scene_configs = -{ - {"cornell-box", rtm::vec3(0, 0.8, 1.8), rtm::vec3(0, 0.8, 0), 12.0f}, //CORNELLBOX - - {"sibenik", rtm::vec3(3.0, -13.0, 0.0), rtm::vec3(0, -12.0, 0), 12.0f}, - - {"crytek-sponza", rtm::vec3(-900.6f, 150.8f, 120.74f), rtm::vec3(79.7f, 14.0f, -17.4f), 12.0f}, //CRYTEC SPONZA - - {"bistro-interior", rtm::vec3(-0.813307f, 2.0811f, -1.28115f), rtm::vec3(-0.813307f + 1.0f, 2.0811f, -1.28115f), 24.0f}, //CRYTEC SPONZA - - {"intel-sponza", rtm::vec3(-900.6f, 150.8f, 120.74f), rtm::vec3(79.7f, 14.0f, -17.4f), 12.0f}, //INTEL SPONZA - - {"sponza", rtm::vec3(0.0f, 2.0f, 0.0f), rtm::vec3(90.0f, 0.0f, -1.0f), 12.0f}, // SPONZA - - {"san-miguel", rtm::vec3(7.448, 1.014, 12.357), rtm::vec3(8.056, 1.04, 11.563), 12.0f}, //SAN_MIGUEL - - {"hairball", rtm::vec3(0, 0, 10), rtm::vec3(0, 0, 0), 24.0f}, //HAIRBALL - - {"bistro", rtm::vec3(-8.0, 2.0, 2.0), rtm::vec3(0.0f, 1.0f, -1.0f), 12.0f}, //BISTRO -}; - - -class SimulationConfig -{ -public: - struct Param - { - enum Type - { - INT, - FLOAT, - STRING, - } - type; - - - union - { - int i; - float f; - }; - - std::string s; - - Param() {}; - - Param(const Param& other) - { - *this = other; - } - - - Param& operator=(const Param& other) - { - type = other.type; - if(type == Param::Type::STRING) - { - s = std::string(other.s); - } - else - { - i = other.i; - } - return *this; - } - - ~Param() - { - - } - }; - - rtm::Camera camera; - -private: - std::map _params; - -public: - SimulationConfig(int argc, char* argv[]) - { - //Simulation - set_param("logging-interval", 10000); - - //Arch - set_param("arch-name", "TRaX"); - set_param("max-rays", 128); - - //Workload - set_param("dataset-dir", "./datasets"); - set_param("scene-name", "sponza"); - set_param("framebuffer-width", 1024); - set_param("framebuffer-height", 1024); - set_param("pregen-rays", 0); - set_param("pregen-bounce", 1); - set_param("bvh-preset", 0); - set_param("bvh-merging", 0); - - for(uint i = 1; i < argc; ++i) - { - std::string arg(argv[i]); - size_t start_pos = arg.find("--"); - size_t split_pos = arg.find("="); - if(start_pos == std::string::npos || split_pos == std::string::npos) continue; - - //--xxx=yyy - start_pos += 2; - std::string key = arg.substr(start_pos, split_pos - start_pos); - split_pos++; - std::string value = arg.substr(split_pos, arg.size() - split_pos); - - parse_param(key, value); - } - - set_param("arch-id", -1); - for(int i = 0; i < arch_names.size(); ++i) - if(get_string("arch-name").compare(arch_names[i]) == 0) - set_param("arch-id", i); - _assert(get_int("arch-id") != -1); - - set_param("scene-id", -1); - for(int i = 0; i < scene_configs.size(); ++i) - if(get_string("scene-name").compare(scene_configs[i].name) == 0) - set_param("scene-id", i); - _assert(get_int("scene-id") != -1); - - uint scene_id = get_int("scene-id"); - camera = rtm::Camera(get_int("framebuffer-width"), get_int("framebuffer-height"), scene_configs[scene_id].focal_length, scene_configs[scene_id].cam_pos, scene_configs[scene_id].cam_target); - - print(); - } - - int get_int(const std::string& key) const - { - const auto& a = _params.find(key); - _assert(a != _params.end()); - _assert(a->second.type == Param::Type::INT); - return a->second.i; - } - - float get_float(const std::string& key) const - { - const auto& a = _params.find(key); - _assert(a != _params.end()); - _assert(a->second.type == Param::Type::FLOAT); - return a->second.f; - } - - std::string get_string(const std::string& key) const - { - const auto& a = _params.find(key); - _assert(a != _params.end()); - _assert(a->second.type == Param::Type::STRING); - return a->second.s; - - } - - - void set_param(const std::string& key, int value) - { - _params[key].type = Param::Type::INT; - _params[key].i = value; - } - - void set_param(const std::string& key, float value) - { - _params[key].type = Param::Type::FLOAT; - _params[key].f = value; - } - - void set_param(const std::string& key, const std::string& value) - { - _params[key].type = Param::Type::STRING; - _params[key].s = std::string(value); - } - - void parse_param(const std::string& key, const std::string& str) - { - if(_params.count(key)) - { - if(_params[key].type == Param::Type::INT) - { - _params[key].i = std::stoi(str); - } - else if(_params[key].type == Param::Type::FLOAT) - { - _params[key].f = std::stof(str); - } - else if(_params[key].type == Param::Type::STRING) - { - _params[key].s = std::string(str); - } - } - else - { - printf("Invalid Parameter!!!: %s\n", key.c_str()); - } - } - - void print() - { - printf("Simulation Parameters\n"); - for(std::pair& a : _params) - { - if(a.second.type == Param::Type::INT) - { - printf("%s: %d\n", a.first.c_str(), a.second.i); - } - else if(a.second.type == Param::Type::FLOAT) - { - printf("%s: %f\n", a.first.c_str(), a.second.f); - } - else if(a.second.type == Param::Type::STRING) - { - printf("%s: %s\n", a.first.c_str(), a.second.s.c_str()); - } - } - printf("\n"); - } -}; } diff --git a/src/trax-kernel/main.cpp b/src/trax-kernel/main.cpp index 15b723a..d5545f7 100644 --- a/src/trax-kernel/main.cpp +++ b/src/trax-kernel/main.cpp @@ -4,6 +4,10 @@ #include "intersect.hpp" #include "custom-instr.hpp" +#ifndef __riscv +#include "sim-config.hpp" +#endif + inline static uint32_t encode_pixel(rtm::vec3 in) { in = rtm::clamp(in, 0.0f, 1.0f); @@ -182,40 +186,32 @@ int main() } #else -// #include int main(int argc, char* argv[]) { - uint pregen_bounce = 0; - std::string scene_name = argv[1]; + //Same shared --key=value argument system and scene table as the arches + //simulator, so a native run and a simulated run are configured identically. + Arches::SimulationConfig sim_config; + sim_config.parse(argc, argv); + + std::string scene_name = sim_config.get_string("scene-name"); + std::string dataset_dir = sim_config.get_string("dataset-dir"); + uint pregen_bounce = sim_config.get_int("pregen-bounce"); TRaXKernelArgs args; - args.framebuffer_width = 1920; - args.framebuffer_height = 1080; + args.framebuffer_width = sim_config.get_int("framebuffer-width"); + args.framebuffer_height = sim_config.get_int("framebuffer-height"); args.framebuffer_size = args.framebuffer_width * args.framebuffer_height; std::vector fb_vec(args.framebuffer_size); args.framebuffer = fb_vec.data(); - args.pregen_rays = false; + args.pregen_rays = sim_config.get_int("pregen-rays"); //SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); args.light_dir = rtm::normalize(rtm::vec3(4.5f, 42.5f, 5.0f)); - if(scene_name.compare("sibenik") == 0) - args.camera = rtm::Camera(args.framebuffer_width, args.framebuffer_height, 12.0f, rtm::vec3(3.0, -13.0, 0.0), rtm::vec3(0, -12.0, 0)); - if(scene_name.compare("crytek-sponza") == 0) - args.camera = rtm::Camera(args.framebuffer_width, args.framebuffer_height, 12.0f, rtm::vec3(-900.6f, 150.8f, 120.74f), rtm::vec3(79.7f, 14.0f, -17.4f)); - if(scene_name.compare("intel-sponza") == 0) - args.camera = rtm::Camera(args.framebuffer_width, args.framebuffer_height, 12.0f, rtm::vec3(-900.6f, 150.8f, 120.74f), rtm::vec3(79.7f, 14.0f, -17.4f)); - if(scene_name.compare("sponza") == 0) - args.camera = rtm::Camera(args.framebuffer_width, args.framebuffer_height, 12.0f, rtm::vec3(0.0f, 2.0f, 0.0f), rtm::vec3(90.0f, 0.0f, -1.0f)); - if(scene_name.compare("san-miguel") == 0) - args.camera = rtm::Camera(args.framebuffer_width, args.framebuffer_height, 12.0f, rtm::vec3(7.448, 1.014, 12.357), rtm::vec3(7.448 + 0.608, 1.014 + 0.026, 12.357 - 0.794)); - if(scene_name.compare("bistro") == 0) - args.camera = rtm::Camera(args.framebuffer_width, args.framebuffer_height, 12.0f, rtm::vec3(-8.0, 2.0, 2.0), rtm::vec3(0.0f, 1.0f, -1.0f)); - if(scene_name.compare("hairball") == 0) - args.camera = rtm::Camera(args.framebuffer_width, args.framebuffer_height, 24.0f, rtm::vec3(0.0, 0.0, 10.0), rtm::vec3(0.0f, 0.0f, 0.0f)); - - std::string mesh_path = "datasets/" + scene_name + ".obj"; - std::string bvh_cache_path = "datasets/cache/" + scene_name + ".bvh"; + args.camera = sim_config.camera; + + std::string mesh_path = dataset_dir + "/" + scene_name + ".obj"; + std::string bvh_cache_path = dataset_dir + "/cache/" + scene_name + ".bvh"; rtm::Mesh mesh(mesh_path); @@ -296,7 +292,7 @@ int main(int argc, char* argv[]) printf("\n\n"); #endif //6909.8 //5259.9 - rtm::CWBVH bvh(mesh, bvh_cache_path.c_str(), 2, false); + rtm::CWBVH bvh(mesh, bvh_cache_path.c_str(), sim_config.get_int("bvh-preset"), sim_config.get_int("bvh-merging")); args.nodes = bvh.nodes.data(); #if USE_HECWBVH_V1 @@ -356,7 +352,7 @@ int main(int argc, char* argv[]) #endif stbi_flip_vertically_on_write(true); - stbi_write_png("out.png", args.framebuffer_width, args.framebuffer_height, 4, args.framebuffer, 0); + stbi_write_png("trax_out.png", args.framebuffer_width, args.framebuffer_height, 4, args.framebuffer, 0); return 0; } From eeb54d19455b843910900e6c96bd9cd1db29c69e Mon Sep 17 00:00:00 2001 From: rvidye Date: Thu, 25 Jun 2026 12:15:06 -0600 Subject: [PATCH 4/7] legacy-kernel-rework --- src/dual-streaming-kernel/CMakeLists.txt | 59 +++++++++++++++++++----- src/strata-rt-kernel/CMakeLists.txt | 58 ++++++++++++++++++----- 2 files changed, 95 insertions(+), 22 deletions(-) diff --git a/src/dual-streaming-kernel/CMakeLists.txt b/src/dual-streaming-kernel/CMakeLists.txt index 8e8885e..9eba725 100644 --- a/src/dual-streaming-kernel/CMakeLists.txt +++ b/src/dual-streaming-kernel/CMakeLists.txt @@ -1,15 +1,52 @@ -cmake_minimum_required(VERSION 3.14) +set(DUAL_STREAMING_KERNEL_SOURCES + "main.cpp" +) -set(PROJECT_NAME "dual-streaming-kernel") +set(DUAL_STREAMING_KERNEL_HEADERS + "custom-instr.hpp" + "include.hpp" + "intersect.hpp" + "stdafx.hpp" + "work-item.hpp" +) -file(GLOB FILES *.hpp *.cpp) -add_executable(${PROJECT_NAME} ${FILES}) -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/dual-streaming-kernel) -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include) -#set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) -#set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}) -set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${PROJECT_NAME}) +add_executable(dual_streaming_kernel + ${DUAL_STREAMING_KERNEL_SOURCES} + ${DUAL_STREAMING_KERNEL_HEADERS} +) -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/riscv) - file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/riscv DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +set_target_properties(dual_streaming_kernel PROPERTIES + OUTPUT_NAME "dual-streaming-kernel" + FOLDER "Optional Kernels" + VS_DEBUGGER_WORKING_DIRECTORY "$" +) + +arches_set_output_dirs(dual_streaming_kernel) + +target_compile_features(dual_streaming_kernel PRIVATE cxx_std_20) + +target_compile_options(dual_streaming_kernel + PRIVATE + $<$:/bigobj> + $<$,$,$>>:-march=native> +) + +target_include_directories(dual_streaming_kernel + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}/include" +) + +target_link_libraries(dual_streaming_kernel + PRIVATE + Threads::Threads +) + +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/riscv") + add_custom_command(TARGET dual_streaming_kernel POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/riscv" "$/riscv" + VERBATIM + ) endif() + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${DUAL_STREAMING_KERNEL_SOURCES} ${DUAL_STREAMING_KERNEL_HEADERS}) diff --git a/src/strata-rt-kernel/CMakeLists.txt b/src/strata-rt-kernel/CMakeLists.txt index b956b4a..b2cd617 100644 --- a/src/strata-rt-kernel/CMakeLists.txt +++ b/src/strata-rt-kernel/CMakeLists.txt @@ -1,16 +1,52 @@ -cmake_minimum_required(VERSION 3.14) +set(STRATA_RT_KERNEL_SOURCES + "main.cpp" +) -set(PROJECT_NAME "strata-rt-kernel") +set(STRATA_RT_KERNEL_HEADERS + "custom-instr.hpp" + "include.hpp" + "intersect.hpp" + "ray-data.hpp" + "stdafx.hpp" +) -file(GLOB FILES *.hpp *.cpp) -add_executable(${PROJECT_NAME} ${FILES}) -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/trax-kernel) -target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include) +add_executable(strata_rt_kernel + ${STRATA_RT_KERNEL_SOURCES} + ${STRATA_RT_KERNEL_HEADERS} +) -set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) -set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}) -set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER ${PROJECT_NAME}) +set_target_properties(strata_rt_kernel PROPERTIES + OUTPUT_NAME "strata-rt-kernel" + FOLDER "Optional Kernels" + VS_DEBUGGER_WORKING_DIRECTORY "$" +) -if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/riscv) - file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/riscv DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +arches_set_output_dirs(strata_rt_kernel) + +target_compile_features(strata_rt_kernel PRIVATE cxx_std_20) + +target_compile_options(strata_rt_kernel + PRIVATE + $<$:/bigobj> + $<$,$,$>>:-march=native> +) + +target_include_directories(strata_rt_kernel + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}/include" +) + +target_link_libraries(strata_rt_kernel + PRIVATE + Threads::Threads +) + +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/riscv") + add_custom_command(TARGET strata_rt_kernel POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/riscv" "$/riscv" + VERBATIM + ) endif() + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${STRATA_RT_KERNEL_SOURCES} ${STRATA_RT_KERNEL_HEADERS}) From d092dde0c7261d4ce26aba63bbc1947e0a904bd1 Mon Sep 17 00:00:00 2001 From: rvidye Date: Thu, 25 Jun 2026 12:46:42 -0600 Subject: [PATCH 5/7] tooling --- scripts/benchmark.py | 94 +++++++++++++++++++++++++--------------- src/trax-kernel/makefile | 5 +-- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/scripts/benchmark.py b/scripts/benchmark.py index 5e2c5e5..dfdbcfd 100644 --- a/scripts/benchmark.py +++ b/scripts/benchmark.py @@ -1,25 +1,30 @@ -import os +import argparse import json -import subprocess +import os import shutil +import subprocess +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] +DEFAULT_DATASET_DIR = REPO_ROOT / "datasets" + -def get_test_configs(): +def get_test_configs(dataset_dir): framebuffer_dim = 1024 base_config = { - "arch-name" : "TRaX", - "scene-name" : "sponza", + "arch-name": "TRaX", + "scene-name": "sponza", + "dataset-dir": str(dataset_dir), "framebuffer-width": framebuffer_dim, "framebuffer-height": framebuffer_dim, "pregen-rays": 1, "pregen-bounce": 0, } - - test_scenes = ["crytek-sponza", "intel-sponza" , "hairball", "bistro", "san-miguel",] - #test_scenes = ["hairball"] - #test_scenes = ["intel-sponza"] - test_bounce_types = [0,1,2,3] + test_scenes = ["crytek-sponza", "intel-sponza", "hairball", "bistro", "san-miguel"] + test_bounce_types = [0, 1, 2, 3] bvh_presets = [1] configs = [] @@ -47,50 +52,69 @@ def format_config_as_args(config): def generate_log_filename(config, log_dir="logs"): - if not os.path.exists(log_dir): - os.makedirs(log_dir) + log_path = Path(log_dir) + log_path.mkdir(parents=True, exist_ok=True) filename = f"{config['arch-name']}-{config['scene-name']}-bounce{config['pregen-bounce']}-m{config['bvh-merging']}-bvh{config['bvh-preset']}.log" - return os.path.join(log_dir, filename) + return log_path / filename def generate_image_filename(config, output_dir="images"): - """Generate a unique image filename based on the configuration.""" - if not os.path.exists(output_dir): - os.makedirs(output_dir) + output_path = Path(output_dir) + output_path.mkdir(parents=True, exist_ok=True) filename = f"{config['arch-name']}-{config['scene-name']}-bounce{config['pregen-bounce']}-m{config['bvh-merging']}-bvh{config['bvh-preset']}.png" - return os.path.join(output_dir, filename) + return output_path / filename def move_output_image(config, output_path): - """Move or rename the output image to a named file based on the configuration.""" target_image_path = generate_image_filename(config) - if os.path.exists(output_path): - shutil.move(output_path, target_image_path) + if output_path.exists(): + shutil.move(str(output_path), str(target_image_path)) print(f"Image saved as: {target_image_path}") else: print(f"Output image not found at: {output_path}") -def execute_program(program_path, configs, working_directory, output_image="out.png"): - original_directory = os.getcwd() +def find_program(configuration): + runtime_dirs = { + "Debug": REPO_ROOT / "build" / "debug", + "Release": REPO_ROOT / "build" / "release", + } + configurations = [configuration] if configuration else ["Release", "Debug"] + executable_names = ["arches.exe"] if os.name == "nt" else ["arches"] + + candidates = [] + for config in configurations: + build_root = runtime_dirs[config] + for executable_name in executable_names: + candidates.append(build_root / executable_name) + + for candidate in candidates: + if candidate.exists(): + return candidate + + candidate_list = "\n".join(str(candidate) for candidate in candidates) + raise FileNotFoundError(f"Executable not found. Checked:\n{candidate_list}") + + +def execute_program(program_path, configs, working_directory, output_image="arches-out.png"): + original_directory = Path.cwd() os.chdir(working_directory) try: for idx, config in enumerate(configs): args = format_config_as_args(config) - cmd = [program_path] + args + cmd = [str(program_path)] + args log_file = generate_log_filename(config) print(f"[{idx + 1}/{len(configs)}] Executing: {' '.join(cmd)}") print(f"Logs will be saved to: {log_file}") - with open(log_file, "w") as log: + with log_file.open("w") as log: try: subprocess.run(cmd, stdout=log, stderr=log, check=True, shell=False) - # Move or rename the output image - move_output_image(config, output_image) + move_output_image(config, Path(output_image)) except subprocess.CalledProcessError as e: log.write(f"\nError: Command failed with exit code {e.returncode}\n") except Exception as ex: @@ -100,17 +124,17 @@ def execute_program(program_path, configs, working_directory, output_image="out. def main(): - program_path = os.path.abspath(r"..\build\src\arches-v2\Release\Arches-v2.exe") - working_directory = os.path.abspath(r"..\build\src\arches-v2\Release") - - if not os.path.exists(program_path): - raise FileNotFoundError(f"Executable not found at: {program_path}") + parser = argparse.ArgumentParser() + parser.add_argument("--configuration", choices=["Debug", "Release"], default="Release", help="Build configuration to run.") + parser.add_argument("--dataset-dir", default=str(DEFAULT_DATASET_DIR), help="Dataset directory to pass to arches.") + args = parser.parse_args() - if not os.path.exists(working_directory): - raise FileNotFoundError(f"Working directory not found: {working_directory}") + dataset_dir = Path(args.dataset_dir).resolve() + if not dataset_dir.exists(): + raise FileNotFoundError(f"Dataset directory not found: {dataset_dir}") - configs = get_test_configs() - execute_program(program_path, configs, working_directory) + program_path = find_program(args.configuration) + execute_program(program_path, get_test_configs(dataset_dir), program_path.parent) if __name__ == "__main__": diff --git a/src/trax-kernel/makefile b/src/trax-kernel/makefile index afde1aa..0e06370 100644 --- a/src/trax-kernel/makefile +++ b/src/trax-kernel/makefile @@ -2,12 +2,11 @@ make: riscv riscv-dissasm #-mno-relax riscv: ./* - riscv64-unknown-elf-g++ -mno-relax -nostartfiles -emain -Wstack-usage=512 -mabi=lp64f -Ofast -I ../../include ./main.cpp -o ./kernel-gcc - #clang++-20 --target=riscv64-unknown-elf --gcc-toolchain=/opt/riscv --sysroot=/opt/riscv/riscv64-unknown-elf -march=rv64imaf -mabi=lp64f -mno-relax -nostartfiles -Wl,-e,main -Wframe-larger-than=512 -O3 -I ../../include ./main.cpp -o ./kernel-llvm + mkdir -p riscv + riscv64-unknown-elf-g++ -mno-relax -nostartfiles -emain -Wstack-usage=512 -mabi=lp64f -Ofast -I ../../include ./main.cpp -o ./riscv/kernel riscv-dissasm: riscv riscv64-unknown-elf-objdump -d -x ./riscv/kernel > "./riscv/kernel.dump" - #riscv64-unknown-elf-objdump -d -x ./kernel-llvm > "./kernel-llvm.dump" clean: rm -f riscv/* \ No newline at end of file From c1719bc9b3f5151889e5088286e996b955e40872 Mon Sep 17 00:00:00 2001 From: rvidye Date: Thu, 25 Jun 2026 12:59:10 -0600 Subject: [PATCH 6/7] documentation + linux makefile --- SETUP_GUIDE.md | 290 +++++++++++++++++++++++++++++++++++++++++++++++++ makefile | 34 ++++++ 2 files changed, 324 insertions(+) create mode 100644 SETUP_GUIDE.md create mode 100644 makefile diff --git a/SETUP_GUIDE.md b/SETUP_GUIDE.md new file mode 100644 index 0000000..11e0f36 --- /dev/null +++ b/SETUP_GUIDE.md @@ -0,0 +1,290 @@ +# Arches Startup Guide + +This guide assumes a fresh clone and a first-time build for the ray tracing hardware course workflow. + +## 1. Clone + +```bash +git clone --recursive arches +cd arches +``` + +If the repo was cloned without submodules: + +```bash +git submodule update --init --recursive +``` + +Arches expects Ramulator2 at: + +```text +external/ramulator2 +``` + +Do not edit Ramulator2's CMake files for the Arches build. Arches wraps the required Ramulator sources from its own CMake. + +## 2. Datasets + +Place datasets in: + +```text +datasets/ +``` + +The default build config points `ARCHES_DATASET_DIR` at this folder. You can override it while configuring: + +```bash +-DARCHES_DATASET_DIR=/path/to/arches/datasets +``` + +You can also override it at runtime: + +```bash +--dataset-dir=/path/to/arches/datasets +``` + +## 3. Dependencies + +Required packages: + +- CMake 3.21 or newer +- C++20 compiler +- TBB +- yaml-cpp +- spdlog + +Ramulator2 is a source submodule. It is not installed as a package. + +### vcpkg + +The repo has a `vcpkg.json` manifest. When you configure with the vcpkg toolchain file, vcpkg restores the manifest dependencies into a local `vcpkg_installed/` tree. You do not need a separate `vcpkg install` step unless you want to prefetch dependencies before configuring. + +Windows example: + +```powershell +git clone https://github.com/microsoft/vcpkg C:\dev\vcpkg +C:\dev\vcpkg\bootstrap-vcpkg.bat +C:\dev\vcpkg\vcpkg.exe install --triplet x64-windows +``` + +Linux example: + +```bash +git clone https://github.com/microsoft/vcpkg ~/vcpkg +~/vcpkg/bootstrap-vcpkg.sh +~/vcpkg/vcpkg install --triplet x64-linux +``` + +Then point CMake at the vcpkg toolchain. Either set the `VCPKG_ROOT` environment +variable to your vcpkg checkout (Arches auto-detects it during configure), or pass +the toolchain explicitly: + +```bash +-DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake +``` + +An explicit `-DCMAKE_TOOLCHAIN_FILE` always takes precedence over `VCPKG_ROOT`. + +### Windows + +Install Visual Studio 2022 with C++ development tools. + +Install or use an existing vcpkg checkout. You can either set the `VCPKG_ROOT` +environment variable (Arches auto-detects it during configure) or pass the +toolchain path explicitly. An explicit flag always wins. + +> **Note:** Visual Studio Developer prompts set `VCPKG_ROOT` to the vcpkg copy +> bundled with Visual Studio (`...\VC\vcpkg`). That copy is a frozen snapshot +> and cannot resolve this project's manifest, so Arches ignores it on purpose. +> Use a standalone clone of [microsoft/vcpkg](https://github.com/microsoft/vcpkg) +> as shown above. If a configure already failed with the bundled copy, delete +> the `build/windows` folder (or configure once with `--fresh`) so the cached +> toolchain path is cleared. + +Example (explicit toolchain): + +```powershell +cmake --preset windows -DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake +``` + +Example (auto-detected from `VCPKG_ROOT`): + +```powershell +$env:VCPKG_ROOT = "C:/dev/vcpkg" +cmake --preset windows +``` + +### Linux + +Use system packages if they provide CMake config packages for TBB, yaml-cpp, and spdlog. These commands are a bootstrap step before CMake configure; CMake does not run `apt`, `dnf`, or `pacman` for you. Otherwise use vcpkg explicitly, the same way as Windows. + +Ubuntu/Debian: + +```bash +sudo apt update +sudo apt install build-essential cmake git pkg-config libtbb-dev libyaml-cpp-dev libspdlog-dev +``` + +Fedora: + +```bash +sudo dnf install gcc-c++ cmake git make pkgconf-pkg-config tbb-devel yaml-cpp-devel spdlog-devel +``` + +Arch: + +```bash +sudo pacman -S --needed base-devel cmake git onetbb yaml-cpp spdlog +``` + +If your distro packages do not provide CMake package config files that satisfy `find_package`, use the vcpkg path instead: + +```bash +make release CMAKE_ARGS="-DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake" +``` + +## 4. Configure and Build + +### Windows Visual Studio + +Configure once: + +```powershell +cmake --preset windows +``` + +The solution is generated at: + +```text +build/windows/arches.sln +``` + +Open the solution and pick `Debug` or `Release` in Visual Studio; the +configuration is chosen at build time, not at configure time. To build from +the command line instead: + +```powershell +cmake --build build/windows --config Debug +cmake --build build/windows --config Release +``` + +### Linux + +The root `makefile` drives CMake; the build mode is an argument: + +```bash +make debug +make release # also the default for plain `make` +``` + +Extra CMake options pass through `CMAKE_ARGS`: + +```bash +make release CMAKE_ARGS="-DARCHES_DATASET_DIR=/path/to/datasets" +``` + +## 5. Runtime Layout + +Build outputs are staged into: + +```text +build/debug/ +build/release/ +``` + +Expected contents: + +- `arches.exe` / `arches` +- `trax.exe` / `trax` +- required Windows runtime DLLs from package dependencies +- `config-files/`, copied from the common source config folder +- `kernels/trax/kernel` when an existing or built RISC-V kernel is available + +CMake build trees (caches, object files, Visual Studio projects) live in +per-platform directories: + +```text +build/windows/ +build/linux-debug/ +build/linux-release/ +``` + +## 6. Run Smoke Tests + +### Windows + +```powershell +.\build\debug\arches.exe --scene-name=cornell-box --framebuffer-width=16 --framebuffer-height=16 +.\build\debug\trax.exe --scene-name=sponza --framebuffer-width=16 --framebuffer-height=16 +``` + +### Linux + +```bash +./build/debug/arches --scene-name=cornell-box --framebuffer-width=16 --framebuffer-height=16 +./build/debug/trax --scene-name=sponza --framebuffer-width=16 --framebuffer-height=16 +``` + +## 7. Runtime Overrides + +Useful runtime flags: + +```text +--dataset-dir= +--config-dir= +--kernel-path= +--scene-name= +--framebuffer-width= +--framebuffer-height= +``` + +Runtime lookup order: + +1. Command-line override. +2. Files staged next to `arches`. +3. Source-tree fallback. + +## 8. RISC-V Kernel + +The TRaX RISC-V kernel targets Arches' custom ISA and is built outside of +CMake with its own toolchain, using the makefile in `src/trax-kernel`: + +```bash +cd src/trax-kernel +make riscv-dissasm +``` + +CMake never requires a RISC-V toolchain. The prebuilt kernel checked into the +source tree is staged next to the executables automatically: + +```text +src/trax-kernel/riscv/kernel -> build//kernels/trax/kernel +``` + +## 9. Optional Standalone Kernel Tools + +The default solution keeps only the course-critical targets enabled. To also build the older standalone STRaTA and dual-streaming native tools: + +```powershell +cmake --preset windows -DARCHES_BUILD_LEGACY_KERNEL_TOOLS=ON +``` + +Or add the same option to a Linux build: + +```bash +make debug CMAKE_ARGS="-DARCHES_BUILD_LEGACY_KERNEL_TOOLS=ON" +``` + +## 10. Benchmark Script + +After building Release: + +```bash +python scripts/benchmark.py --configuration Release +``` + +Override datasets if needed: + +```bash +python scripts/benchmark.py --configuration Release --dataset-dir /path/to/datasets +``` diff --git a/makefile b/makefile new file mode 100644 index 0000000..909aff4 --- /dev/null +++ b/makefile @@ -0,0 +1,34 @@ +# Linux build wrapper for Arches. +# +# make -> release build +# make debug -> debug build +# make release -> release build +# make clean -> remove the Linux CMake build trees +# +# Executables and runtime assets are staged by CMake into build/debug and +# build/release. Extra CMake configure options can be passed via CMAKE_ARGS: +# +# make release CMAKE_ARGS="-DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg.cmake" +# +# Windows does not use this file: run `cmake --preset windows` once and pick +# Debug/Release inside Visual Studio (see STARTUP_GUIDE.md). + +BUILD_DIR_DEBUG := build/linux-debug +BUILD_DIR_RELEASE := build/linux-release +JOBS ?= $(shell nproc 2>/dev/null || echo 4) +CMAKE_ARGS ?= + +.PHONY: all debug release clean + +all: release + +debug: + cmake -S . -B $(BUILD_DIR_DEBUG) -DCMAKE_BUILD_TYPE=Debug $(CMAKE_ARGS) + cmake --build $(BUILD_DIR_DEBUG) -j$(JOBS) + +release: + cmake -S . -B $(BUILD_DIR_RELEASE) -DCMAKE_BUILD_TYPE=Release $(CMAKE_ARGS) + cmake --build $(BUILD_DIR_RELEASE) -j$(JOBS) + +clean: + rm -rf $(BUILD_DIR_DEBUG) $(BUILD_DIR_RELEASE) From 70daf4072bc4d1cea6945d17a30f1fe0c48b6471 Mon Sep 17 00:00:00 2001 From: rvidye Date: Thu, 25 Jun 2026 13:10:28 -0600 Subject: [PATCH 7/7] cleanup --- README.md | 1 + SETUP_GUIDE.md => STARTUP_GUIDE.md | 0 2 files changed, 1 insertion(+) rename SETUP_GUIDE.md => STARTUP_GUIDE.md (100%) diff --git a/README.md b/README.md index c1b5e45..d0a35ba 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ mkdir build cd build cmake .. ``` +Or use [STARTUP_GUIDE.md](STARTUP_GUIDE.md) for the current CMake workflow, including Visual Studio setup, Linux setup, dependency installation, runtime layout, dataset paths, and kernel options. ## Setup (RISCV, Ubuntu Linux) Testing programs on the framework requires the use of a RISC-V Cross-Compiler. Fortunately, many many people have devoted numerous hours to getting a decent RISC-V cross compiler implemented using GCC; this is available [here](https://github.com/riscv/riscv-gnu-toolchain). diff --git a/SETUP_GUIDE.md b/STARTUP_GUIDE.md similarity index 100% rename from SETUP_GUIDE.md rename to STARTUP_GUIDE.md