From ed29a8a9d1bf7e0916c10032e7253d8065100fc4 Mon Sep 17 00:00:00 2001 From: rvidye Date: Mon, 22 Jun 2026 13:39:07 -0600 Subject: [PATCH 1/9] 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 3782c74d..00000000 --- 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 12862fa2..00000000 --- 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 41385db9..00000000 --- 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 d8932192..00000000 --- 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 a7ba1375..00000000 --- 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 0ff3972d..00000000 --- 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 db082f89..00000000 --- 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 b676a30c..00000000 --- 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 b3003374..00000000 --- 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 83d0576e..00000000 --- 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 c1817a18..00000000 --- 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 6d7eb3eb..00000000 --- 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 1e7ff50b..00000000 --- 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 dd143311..00000000 --- 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 336425cc..00000000 --- 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 c135b922..00000000 --- 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 2a2cb1e4..00000000 --- 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 2e3e5cc3..00000000 --- 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 13857c47..00000000 --- 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 8c60e25e..00000000 --- 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 fce714ff..00000000 --- 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 45f29727..00000000 --- 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 ee628e1e..00000000 --- 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 eaaaac81..00000000 --- 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 ade91c33..00000000 --- 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 ad9f0f31..00000000 --- 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 4dca07fa..00000000 --- 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 21c61188..00000000 --- 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 4c015223..00000000 --- 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 8ac11211..00000000 --- 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 059f1980..00000000 --- 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 8207667f..00000000 --- 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 f4f55a6c..00000000 --- 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 423033b1..00000000 --- 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 5515421e..00000000 --- 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 b79c53dd..00000000 --- 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 ce867121..00000000 --- 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 8c209937..00000000 --- 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 0d9de176..00000000 --- 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 a161dd03..00000000 --- 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 4827551d..00000000 --- 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 5439264f..00000000 --- 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 69286c8f..00000000 --- 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 7a4a1e31..00000000 --- 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 4650c192..00000000 --- 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 2e1df309..00000000 --- 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 265be075..00000000 --- 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 46e7b95d..00000000 --- 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 c1a6e8ae..00000000 --- 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 1011f029..00000000 --- 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 11583bad..00000000 --- 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 25a8fc1e..00000000 --- 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 a49dcdff..00000000 --- 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 1a31d8a1..00000000 --- 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 7485b31c..00000000 --- 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 c06d5b5d..00000000 --- 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 636aea97..00000000 --- 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 e32154f4..00000000 --- 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 34913710..00000000 --- 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 1ac2e3ba..00000000 --- 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 fa7280a5..00000000 --- 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 34bcab68..00000000 --- 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 2df4b140..00000000 --- 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 121f167c..00000000 --- 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 57f3b9db..00000000 --- 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 dfcfcc03..00000000 --- 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 b2e6b051..00000000 --- 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 a4d2a9a3..00000000 --- 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 1797e35b..00000000 --- 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 43983a00..00000000 --- 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 91c7c44c..00000000 --- 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 56dbeb41..00000000 --- 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 6eb0f2e5..00000000 --- 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 a204b9c4..00000000 --- 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 401ad004..00000000 --- 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 6d2a4d64..00000000 --- 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 4c0578eb..00000000 --- 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 98de0d42..00000000 --- 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 412b5a35..00000000 --- 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 f7ee5a94..00000000 --- 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 f8325dfd..00000000 --- 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 c3fbaf65..00000000 --- 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 36a9da49..00000000 --- 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 e38c47c9..00000000 --- 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 3fdae350..00000000 --- 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 82ce1df6..00000000 --- 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 0de49aef..00000000 --- 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 2bbacd55..00000000 --- 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 a228cfe1..00000000 --- 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 5394b818..00000000 --- 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 0ba38f21..00000000 --- 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 2caa56ba..00000000 --- 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 965af129..00000000 --- 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 306903c1..00000000 --- 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 z(WL_-FWbL&@LP%Y@~@v*k_ATA?`H);!Z`y%`wTh8>oAY)EDRa`lw}CFSJ=L?>pr0D zjgb66+8fYCD=y1UdzT&IykI8i4Cxt{-|18ST>Gq)zf>c)bC75$ztz*BlxIdz{u7o? z*Us5UDE}U~i2Hqi?@=Dds!7k7bKsK$BFEUjGw^${{mUZQ8oq?=m*@+@57=LHfbd7>V>!zcJ~`>X`tM@4;0BwB*s|5PZ!nGpoP zh^5oDTPndjK>ps^OHd+P)PD=HdBOg@jo(%FZw*PDReTNU{pvWO_sXY3=nWep0v;gc zLQlX0X(vp{q4yogBm%zRHlN-}Ba4|Fuu=G4@&M%^(Zpbq^$3dcU|dl-|q;djE>0)3v|dK zMDDYHOY!@P{aZs4XBGGEZ_QOAaC4Hdj!92xN2aaM$=-qI4dG7|bG|yGI_6fNCL`># z(&QeJHC?+M5-m--mxR)U89|dPSUO!hu107w8x-oTy$U6iCaPkyP%rDnYhMHXIc^`d z?k?IFM@!<-GdM4N!YzI)4zbVLieG3ejz^+x#b-~1wt^Yailske3+ zO1M_^Eu821>tIEV-LID&Y6|HuHEe3wqK$gJ_!Yh?xQdAPWLyPXQ3v;@^4;ot)tV`*IXDtd-=GRzmH1-y_us{w zy;b5nb%!c(W`q*oXLD-r+IKPMfRXSfn9Av`9flH~dA_i_`oa?=fV9TzG5nzu=ZQzK zNi`P@@DD`v_kT^#VgF_ReN|sj7x3A|lZ&YCuOXD<;>k%2uT-ONy}S36n8YU= z6h3A@FL}pc^3F*wc~d5P$DJ@BY?dP+d=NtVt1(D$_!sn2VQ@0$HR2jJzgcGa-M?A9 zJXEiG))t<7_r@=9jkBD3*ohd%FERM)SWLuKwLS7aNLGH+`^RE)K#9R1R^Yc{7Ce3U zJ*7PO-1-Z=7g%3SpIqoQmn>fcdfhy+VEN$st^!Qsg!N)l99~`{*>}i61;9am9Xwr< zJ3>4JS&qvIel8E{eKIk6k#CZ-x`5+Fza6n#|KaXkwdY8CAy;JX7Gq3K+ z*4f#pMTn;*yf2$lC154m8Mt1}W*#hBLfpWn9$XZSnLfC5AT{@ypesNcJg^iAGJ*4V z+hr1%mYOi5E`!XcS8`&ov2yAjF7u5D0gZd!Nyc^rGy(rj#Ti5v-eH0nKRmZwYU-Km zzMYm|*DS5eWw`;z4DK5hV7E$WNx$t~)8uN9W;{a4lfF(MgqrhEE)@W50FO1RS z)fdkcl3T*OOQ{TiVu2P{(ewD44IJM9ywYsz1GkhQbCgxQS`2cyjL0O-1jxTlIgB*J zG^E)YX)7KJ;xWa;L+^H!<@7+M-J!Bi9xC`Is9Z19ufOUO*ssX7 z5GQ+{MAy(HG*qsHq#Pgf3{<~y4SMP9iiM0ZFwp{EunI5$@;djas0gwORB9eA$ z*y}AvTX6G*a5L)k7qh0Ge9zV-u9TyRZ~HS${3-0*(-$v5nLNGXYq;Rxm<5QNlSz#;2ZWk!M^tA&LIDy)gHdM(#v;T4D5v}vv}ZW&EUdgg9xi-+6mi&S zH^RtYkVbRw`Bb@0Ao9|H=eNf!0v9n%+!jz|a{uIRZ1S#p?b9e^G(%%?EWUSE%nYl; z5epCHw6{vzJ0c8LIpzPfqtI)$%crSmLTJTylvDo!Qob9GPCmWMDZh#0zuM*hunX{q zIXaiz3c(?iA24L$yGEei^&szNQGqRPT@TK{dF?h?9VDjI`a`hzJvjA=hTrohcmatn z@x@Y3XwmiDXC1Zlz;0O(OXHQ$W{5Juy!O_fso<&bZpuL6orAH`9JDC8`s&WnA9gW+ zK@0In636NpekR<0MX2rH4_*Vm<9hQIH=^tu9~fFcw(tOvGq=Z z$M@E5cB@FREw;{fidDkiIsFrY#tRsJoIX;fEmqjce7f)*HUp_x2Mup8HdTZlNtI?W zJtLr%=-J8^t~Bq7VG-Z7()>gG1-b$NFl#{1<%#sW+gk>Myih6_o#+*s1p5J5 zxCS5U` zs=~3(v^^8$=bljEKKa8j6lRaBH?M+WR4MaO%1UCB`C<&MjhE_f=3O;8f+Jz6=1z(b6@tDVJV9Tl=(O?_(E^h=nq(FzlPRrG9Ea&J zl&G0cSqBaSMvWXCXUv{JyytO(Ycl6k-AFyF80$ej?LBgj1eVmwxXwatZ#|qfwA<>S zvk%izpHy)bs#p>qjd4aWwSyJA;g;3CzAo%d#(J*Qy7v(>P%M8)-IZZKoxYgd`fsSO zJQ*%apIM`BcQ5T4mEAwz!%x>YfHzF+gRsozm!fj)j9a5Dzok!nccD;d9xhBM=4(TZ zHB_9dTd3}TFG@@Q1b(`XR3x6mv)LR-KnpaOV<4U3T`a3!*WVhbe*+6Xs9fT5)_`^s zyqO|*I$d;E8MNUFL0S=P7fMjtV~K_M7NN7ihQ<=3p`{o3 zJJhaxN632@e!5N}lyueOcs7qwjWdoMX%jmhC$M^gs5ieDB@}O1BRS4)Z>SO^0UHyG zmJebFUoJg9PYpUA_Z8lpet>r}@T(zmdzAp@$vYZUgY#4)+*%#w|2_$`#;*XH71USO zp>zz#$?iC_0wFGW1~+CVb}Va?u#^8#Qd5btGhJM8q*N$=5GSfNkQ`@wI1+$^+MwMD z{w^T5pu5N9c8K75=#04o9>g5%8yma?q#V!_ezv2ChU&tb(zS1wa zLm*?0gsJ;qrr*;r`Td0-79nJHl5iaf$%}cChqL}5ep~{cU@!gAWB!&m0N+cB%33C zQn&`EdgY-%>gz(h=)YOt&oF@KQ+;d1ujvRL7Ot(heh!>3_30*|{!ZEDw;8c!R@$%OPX&z<~zXU)8+3rC>w9D8MZ%6U)jM*;G@Lw>wFYD#RX&0B*sFAsG4fN z1CgY=+e{QBo;Jl^1%+&JqBO-^vVrDlVIFT>Ik~Rg{ECBIalA1Cjhkyl#mHcE4&?{A zV;BZ@02$x+^KdTqo6QgKv*aM=S=hljw0kq?#5n&wZVnVhgl5#+J(qY)m=|cOi66pz z1#C(XzvB9aa2gQ#`$vzqxgph9bFLiF*w7ilJUDqgYruj5Jrz9zWt&t;gd|uS=NsI5 zj6w!j*J(7m*?0sF8qUyBEr2}W$<65 z%6zXHEbT6z9x>8`lPyj;kpIG{1Et4i%$W5{6F3{cB!O<24YZk~A~|}Dy<-Zlb4F|l z4~%2E<8sf&u!*-y(JhT(VAR4a%{CO*XikUmmrg3BH1xF(iUeX6`~v04^9@srMo{m+ z{SBb+)#M^(HEfhcM52F<=G&5yA**$N3pUkix`f_WmaY*RUg#^POQd>_hQMj9}SAhP1dV>g65&L!YO+CtPNdNLZ( z%LwEYbK>Uclr;Hy0S+!gItV)G%H1C_! z@0KI9Y%f2Fxp}$+G{FJP{X>SVA)O6W)vAA9t;s2L3JX1`g+{Q@m_Q+W-`@eyiQcK_ zwyqzu*h5KSvu)_y8%M8?by9~I^(jXu0Ode*Q56oB@0B!Rnn4R*y`gq3ipiZhiY1%0EgJ$RA1I*slZ+Y{o(aq;$sX!9_W} zk~0p4x%qV1d9BUl=LldM@Wfi=25Zc~;e60j={K{VFXXcJ#3#oAg{6yFQKCAIMG_hF zKD`3uh!Y!~vY-_JrZR`e$%}0OL}1RX>i`sh-PnR{aA0xsX%^UPEm&AI*k)GHNU-3n z0#S*17P*)Vsgm6%JicbPbty%hIYFT$^CitH8J9C3(wdmoWZq(0YH@RM8xbaFh0@|j zV3?FEduZ+B6<_wAfQoVb3jW)@w>J`zodI<;XFWqC6`$*XG`nI!*P`71m}`YE94sv@ zIo>N5zS90T9I~g1g*N&I@8o`l_qCGnoW6jthHJHkRC`6_JekOD=!{qN4EGGqpvqE> zhmeHIgPZnTSOI;}+1*+7>WZO};}e}5Zq>vt);avC&Wg%iqgy^TG;-b;n6HiSV6K3@ zPwhSwkG*Xhn;BI&QYZT9lS66VKE{vPWFt7S(YKjWM1XPxw~Y2DQVVYdR)Hbv+kb)< z)bj+YhH)kEVD?n)njOqrTqc4jl&YVW{iWd!dd6%r(0j2fD)xiLP&1-2ho?>vP5l@A zbajKgAkRB^Hpil?m5-N)V=4T^n+OiyQ&@G-wY}DRK35?0jP?-jtn975<9sZwD$--h zuWw7tiEpn~X%|{0SumKc;Pcl&WRh#{D0N_>oEUQS^2$%MI`LfIL%T~dU<^EejWa~I+zLz_K9QKzmYoB z#X8g8Dr*h!(hWnCF^lk3EY&j2yc$4_^m4w{cxp* zV!YGGdrC^dhkaJh_ezWp=q|5D9UiUuTXub0^>cmTLIHivIK@eYrXY&rtExS2o`Z5- zJ>q1K?;&vEqgXpvO26d#p&Y0|qz;2f`SIeU*#ct&d50)79@e%KCg{l`#Ud9hi z=0c8%+4-6vnk9x_K{mSQ^6j;a$v2v9NNeA%{}BR#sJwu_$(l+o%tH;ha0(f2UB*1f zJ{Ss9hR&sda&#QG;Am1l2hqa;I!fq+&re+1IDO?SR7c?H&iHli^6a{IC3!~OD*lRQ zL49Z)*5qOi0!{cdk#4CB9B8iJ7%{{qHQ^5E{u@Hj5vQKMnA9$Mx_b_@`r$Ps6a%8=YZ?)#fYf*E zNTPt$`-rVB{QcqFl_RjL3;*2zd{I99te|XR^7uO8Kje65|1GYM^`KMsT}-Ve&5e*1 z(mc*y6*H&SQjVbc^n1eG%4m+f$yK%tGwW5h*M26gD>$n1`Qz{jZ5DwedbDspAV*ri z@4e2GSJxq%pFxM5hBSCYr{LKbhCrog=gj#?Hz_+lg^Sa{1pduN*6jVTR7~N7+fLiljM|A}7iW)UG!P_F^;weo!9<%nqNz z^rTySVQT@m*w?>d18X80CsK%sEHX7Rz~(EiQm@dH(N(Rki`_d=t2^MQYc6SCP?qvSUMBwJAF|% zX@)7SvpEv$6G0|(G9{!D;999?G(%9dh<5@A>_c)O4%kd;~F6*gPk8CD8$qzu!gRuOgUDX z>ps$Yn`uOWT$4GPp$0rPnx8O^gC~501&8@&;^i_X_7W$K5TuvH$42cL2L zR4|5_RQ=$dm@ZmodRA#=8WzJIeH2Netat{i>qWZmu*MIrKY_scQqTK~dTTRnP#$Pt z99{OVqYw^dqvai4xrDU#Nj?d**^GmGqXCD(w{1zY#-w>LB#Mg- zOEsH>cvlq0)M||YvRPJ+{^ckYR1h8{%rV0}ES*8z_z84Gx`xoZqw#G1D3jn^{?hyf z#m{q^nbLu20UemPVIrC-H6Fy)-&=t$UnQ4=zf|(s0Cub(BA>mZJ%a|Xpb01>KcV7E zR!ihLVg5=V8LS3yP9C%#!=_^K7!@p%F=;TZt{ywK7Ji9HF~22GfWI!CQ-&45l!?5| z8y_S(S>LmzB~N#=)9x`2pGKo?Jr~I8jDiixX1jYU-v5Y0++4!Za!?53=2nO*@QerB z@;zdQhIQAtWfy~>8&!OF_dZGyU@Z!^qk}?|HLJAUlaQscDoaP>*>qZ`uPB>bdvYHN z@h=``pV-_frW=D%MY;mEb-(HdXVei*um87a_y6}o`~QZ_)IxcHpxrAq$ewi7-ORK_PA1oDhx`_lK1nGZ#j_bnO9s6ALTM*I zU}^p+iqN2^vf`(#Rn}Lgt1f#CwchT0WypN0P+Y;|tI#8_u5{I6M$pHJz0Cq)@dF+; zkjxQz6G0CD0v#G&7yOiwa9Va@W8we;#~@=)`IyRMnaq=#@ z5j1Jw?c%`Db(}STg6?0@UTLcC#nw!u#h2<5Bf zBkQoQVtM>mx@dWP_9z^Hms%MQQvy_Uae$!T7C(I`UK{w@vj-Cfo>7mAntC%3fw$Q8 zdv(Dcyx{RzQiN~gUsw)8HOe}@Moy?Atr020JMP(O;EH1;Cb9P|-ny4OBDsm%y=5t9 z5@98!SaS5l8hFSI9Nn)YoR>JlxdBkoyD#qsuYU~NsUQ%GO09~Y>BQoFX8omRJ%L&G z3}!v4yMv$5jMp$Upxh=IFhXnVkRIt73JmLdyMZ{yUO*N&Vclol)Uiy*UMnrlQiEu= zmOab_v(F0c2QOH)4*My^`yt&HT~^J z`B+T<;=d&yb8!PSM!*l?$CHoKs9g^bL#+(VmXGI|)yl`WnRQe!tH;DDD=vBK zi{2Gap<*QK;Huu7fiwq*C?vRDeh#riXno?tQ9RN9;KUOf=QtbUp}w1#Yc#(*p{hsJ zTu}8~gYCUtX~IfZy^LowY8`md`!n=-oOwWo;kNOv3D;0n0u9>)ocbY||N2je@pIYD z`OTXMd&|nTn3+SZD75p!;NQL+^;OA0j&bk~U{TOwt*J)qTlwtqR4mM?(`GfAr+V2e z#wX@7Zkx*TyS!Rme}T=*U$2%}_|9W@zV_;E|9(}@Hd&2}db9L(XrF|%1=kNh^Z?kt zx3aw#&t^MxRptq-AHJJ;zO8xw!aRf7ogseV>xZS6kIv`@@K$;ZYrIU%BcDJ28csj< z?%t_=xP8^}Bb2pdXM?$mZZwqGwlrYjY<2i$P0fYUpOO!^8DoGk`bamw+b*?yL&sXx z43-| z&zl&WH>AWSWMnBML#)at@fgXcqAS~S>Mz;8fos;jBJfi<`XIGx&}vNPlygaII8YXn9QKqNa^Gd=h1w?V7pa9+;Oh- zsQ$Z~>A=3Wu{}8BNUE6O1<&3T9<4gaBrviF7%l@I|`I9b;+%?LaAV4Rn*Efc5 zGKZTgnkK_Xh3kYN9!7HJIC$qco(28b^Ha9(fblyXh|K9Mb6;5&KA~+yObiPqXlLeN zcvz@i9vAuoFW%4a-i2S+pZLx1XL!keh8O700DryCyz)8V0K+8KZ9gibTXYOvk)+EBw*IT9S;WJyGoQT%5C0V@BimrjMN*7g z`aVAoU1U#fW*MW>ys6UZE0;lwvg2}Br<=^5UALUcIB9G?ezp6~t}}QxucL0!d;U!5 zRk%;CD+=p^vCLCA)WCd)2%%>q5MxWd@+x{U$1C+_=DTb^PHC4Irc8-IXVXotp7AVe zMej_Q&Hlk$cJ0%ymDLyO#vGa9urOx;CQt3URSMZ(yf5IpS36%!TEgGl(0~>#M2c9# zVfF>@Foya8<{(Klo6JK%kJ=~+dw_jzw$6#LsXgmyLoTP;TguYFnT7{mON)LwTiS-# zo^ujuZ##hnu>rpU&2A}YfAOA?cY7qXQ0m9%5kROtk)B=Gfv;w?#i^ZF(%c2MkV9@o zhm?s(&6=9<6zSfx!IaJD4nUDkUrd(&Zw&b0FYlOG^hPW(>~TTpgwh?7%O)CShXWP~ zGd5X&r^KH2#ZTAXDujpQ**r&LF>`PCC$A*t1)_!x_E6svof(~4{1I>ecW$4i)gt3D zjlFyzT)Kg>e{#I;pByj#)%dyr99-0Oy#D3TO!b3{z)cBgvIr)RS5-)nowUeKQe>yP z0n3)#LSMK-=pV@el6hy%yt8E9xo$xB@_{z@=PtKc(Yo_ln?C;5fBc*O_>TYhiF@>9 zX2-KYLxZSExwhBSfyI8EehhW(xPlUkNH_>M5h@SLHrI73US4JisjSSW@LA?~`+elN zf1KxE39;u<`}trU$_{=k%s;Yz1Jw@n*Ex(Rl@K8ZFVugd@Sq|LNOSgEjhVIj`>i07 z;hWTB+*9Qi2DH$eG*eqO_SXLEuwkOY;2Evb9sO^%f&2e_Uo}Rm>?4U?dyCNT0+=__ zFQP1t71KnMAeK09rQ{(IV)cE;|277CAxF5XeE`fGnKp_vrA5L#a;?Lo9#ej4?QUcNuhT-nBI^8%9_6%&GK_7$myz2 zaQUR^5m$g=uwj2JuT4(aLmrCr&pdhyLo-Bg0V$j>{H}a6r7&!LV8R;$TMtKG-%k_@n`@FZkLdznbAiG#9pyo?Px+Utd1!IMk55HaN)Tl)TsN=66p zH}oqsyg;Zt)3{ZFwKm6L?2#EWi*u^zA$Z{`#-m@tD^7H<(cz4_b(!^N8gUwmR$+lb zzq?}fEQZR^C*?o14K@SEiE1a?p3d8a8s=&))(t{9RbG%NO;)An!5 ztWziZy-=8hMHy8Ze{(ZrNLL|)2{-_17a|@`ji;aX;i)+J?}d)nF?;90-|?wFz%M0V zAUVzmI)4ixJ~ZMTH|l(G@Vua&s5!A%ok0SYWeTXDei_aYhi{w$T?3A~ArC_k8(D3E z`Q}PbMUhUHalw%Y@A=a5;6N^|~`pe2-~oZ|hXP8_{L z`xa4_E=7&`@t-_3j&*g?o=S6PCJ+V->%T$X^4Zph@->Nv-QBh!1tZ9r1bQJQt73_@W5?Bci<-t|C(z**H! zy+sL@9+oNiVQIx8fr)fk_G-_=KGZ@_I4A1|XH);n^c>?`{a^yh>K~%EEQf9>qWpb+ zq*jL0o1`$yUiavU!Ef|?yb&An%xfS%-cFzO8VKy<1)XcZiwyP}h+pg%dJP0KVllx2 zLQ2>E8Xgavoj2r$@K%)2YamvvB-;dm!+-#*jO**W2-)%puV50&P8uMe8)%sEA8~&4 zW#)0O(^v!$z)OA|BVRQ#VPm51__5B%nFkqV%6Irv30|fJ@xNoaf=DWejRXYxrM)}r zaq0O4ez2{piE@MgB6fXJldQLv(InhRS)1Bo&LUJBV(1qE$?b3@-b?g1q1G~twX{D8 z|8EZPf14ox5hN1+BZJ5PL@586k^Cp5bnUM9DgPl2y|t|gsPnG8Kp_nY(pfcA!1DhW(op{S9#pJN zqKWYk^Rywfq+MFp-Z^A+rgIUfSTE{RJ@{sf%FwTngwf$5D^=Aqc<>Dq#11g%Q=tlq^2?tNo+N1|IOvajy4%EOx!ikRGD}Q6I z$iH1cR!m!qhvNgHJY|8kL~0Y}pU)yZxF@Yi!n}rOqOFVRTCg>;_sP+=$>ew(^(Z0& zwY@U}V_#o=VGroWs@s80{j5qp0+m%dWcb>dFd2tXup}(g4<@682FU)b`e1Cg=Ursm z8whH6=)p|@O|h#-*JrDyuuGtkQ+kt?J)27kK97R4UX$~Agud$l%m&GSM;geM7Hi_i zz=;$YXod{fF<}p;#HET^6EQnHtq!{$ENlz}q!VLHigN-4oQz6k_i@I|`>h@2)l0O( zJUWl=E934P`~-Y>iiRguc5oz{l%a1<-vY~!^(qvFRC{H_9n3R)76L`yDLs|F8N7B@ za2~wI<4|@rq3f4THRUN4Ho%hn$sD$t&OabOI-m5ASR!h$6_W3;`B;c*J|WTNX&TIz z`M%AaAiH85aVM}$RMwhG$XuZ4;Q927Qun13K;2Vl`}#=TY@uIjkP?c+R#@mKN^veX85k_5VW~6M zx4Tbm=6hJXSMmacHwY{4QZ^@aab_a;wa(3GkPFTS-)K=Rq`hrn(A%cGCJ{I2B-aiC z2i+x5VKcQe6O!v)v8~8B?V|N^vrrqhQP|u&kq7W4O`JYb*ilQ25L!_P;YLchl$SAP(7ntC?LkKJsvna<=ZjdGF7WwE!tEIo#mi^-quRYEWFJ~mHnD-5zZz(?e1n}9P_zVu>Q^%O1(6`_5 zp%+FL{@xAcojiPP4ruf<%2~SVp-$j56%6-}T4kv#e^Z)^r6c)hSz`k8b?S{Zm^~jY z2DKD@iPxYm5{yrkqVBPK=Al9`OTmLoSQo`n=B!bm%-Kqr3-N5e`)AgNoHz>@LS9s? zZGFgHvoy~5D*CoObFM0qubj%jFG0@+es1)MLX8Y`sQC@O&o7ZN?*wdJ77L98=QQEk`-qAD*uje>inV%2xiqf6yYn>!Yio(~{I zbZLml5A{(DwVfQf7q(qU+*WVrRoZArx1S|;^hEr08FDFIbvvHTLC#bu5J;0dbLqqf zK)&M2(P8aBWXlW0zQ7;};v7rN!bk|MiIH~o3AuNp*w~1*g!b>@gc3fW3d>(a9R%U$ zpW`V!hBgT97r6LB(A1uC%V+aBSin4i{Q~DR&o`Cs$1=|!kq0P;?-$59Y&XH{D0Zhvs=cWKlkpUomsDWBq`=sN9{0Qih;ISJRq9gh&EBC^34B2~a5{dv z?$uV-GV^=S+st5Zqhg|^k&wm~Cu*fuMH#RY|Xbr@$f zt&}}w1Uy}}&ZStyy%SYms;IYu^$>kIZ-o?Ur!kX*E3n#^^CWId`oz0XpIADGGmfq} zgiUQQ|9YHUrACF$&=W`+O!Z^I#EB!qH^p zK)uk7!NAicv{TP9E7N&XP(9Z!vXKb3&(NEK@CnW4fAGY#>d6d3BfT*Y+=}*&jEW=B z*t!jkJMM{@p}W~Yw-%-TcDHLe2=+A0j0w_echRlPU0{P+*yC7VoHZo)l)ym|O*|ec zNiYes&V$)t#9=|WnZlZ?91w&hqgquZpne<>cT%^EyK4~qa^-T|3E})u*mH{dG4aqPY~dI ze@BGOMVaFRr)ng}voPc+~^us5Ms zz%$xQeg{B*^Jw`)`8}!ILH!%0P%2|?hR>`Wh8eeIF)Rz>ZQp7_{U=2dOP<;4l&MV2LX5dGv9vmMBuA{ap(D za6XKMkL_x8$ug`e2;r~kP~tU$c(7UR>|(FnR%mIU#-Ay zC9vBSSUZ7b=s1yBsCqOVC?%=kpsGBw9179t(!;&5K{dG5(jK}J%XNeSePpaUlP-*FN8jETT;+?TMPeYBp7SkFHh2hXa9 z;NQY6F@BP{fnFmKjWd+oSbgb*QqG61 zS%1N-%)mn>?Z-gd&$plBT;E1m7}!c~72qY8NpIMgO*~YaIF(HtEKRhuKlcvNTFC(` zI1=vphL2Pmmc!aiO|46IPC#8 z0F*Hs-9YF;`LSO`->(u?XgT$n_*EVcaS9p2MqDO;7SdOU7IgeCKnIcJbkz-@iMdX6 zqOjbx{X>o%F49)aW~O##^04a+$ivc#46>pAAm(a7Y-}oHCJ6HR_&zEAO0b~zsL5>K zh28!Ql$(%umnk|25SOL(G$TP9QQ}VQmT=#CmnCY@>EA)<@Z_s*DMcK`d*Rf zm4Ar^EO9=NA0Nve`6a1AH$){)gUb309!2?6b^vxi%9}kKY92$M(0%nN0rJuDfkY?) z_9tOP{*jI;MaUc!W8BY1f%)StflSQ_Q8`ZA4Iu=mnTGSwL<>78ZMXgc-+@ehK+PVS z29<7BDjkGplL9$bJc)j_VzG2CHULTh0y!zOidfb273;*{BX~yHQhZCovetb&!rDbl z9V@J!aRci>^88$#5iFg5Q5$ z=XLpUma{nCe$_lLaSPrl-cHC<%xGKW&+hBMp3@vJNa+DRhf}QLy4s06z=}qzwQUQJ zXBYX6QtKo&^8x)oqO0agS7B4$mZ zqoLmfM2lQ2WZp4lDZZn$FC>016b19Z&xwiMr`2pSGVYc(w&Ji`(q&h%77_vpEb!~2 zyi?c$=C6Y=VRomS0kL6Uc=wl{urN|_*~Mc1GWhA*LfbxkCfZ&p3m6E?S2j};oiIMq zjQ}ZTWxN&EI1s=Yk}d(a z1Tt#CP5cwmJb{>rOasI}0T`-_Miq{^ z&h15!!6Lf8-PZ#Te2f!etZS+zgIgFNZe)O%Z*eTP#SbIj2rV=I4yJ;*8FMKx;ivZM z2P3F?6Fz4XaH(o*nIuSNyQu~J-LF1;MS=e%3b^rRP0j#Gx}$XT-o6|(m2JhLz4Hg< zrty6aS=`>oP0+*h#%&&re0Xxd5#1xg8b0aYd*_BIWHH?t(cCZA4^(LD)yrd~j^pZ zrFC!C%3L%jLXXb5_FV~XT4;z-c7bH_evw~qg2Gnmp6j*JKU|HRviAOJt}Qu`vApSv zGbs08zQf6r?bZ1W#z&S$IQY*tJbr&Uf+B-njTL5GYsHuBtWv$}|FN*H+7%V5(w0sj z%TjqUvf|~{3~x*3=NB!femV^b`39@_{NloWkfLx(U{%P`@Qw5dyY4#QLF< zN^kRc^)}C=x5-&63@vZNvpF!Rcx)k1sL26Jc?%Ysl*%YJ1kDe~AS(ZBe#zfT}PLS64-&RcIBzlpn_Aow(*JzU_s>(X{})(G<*nax2Kc6;{WB>4p-LhT-pAKUb3^C$L*%=G zl9;w-Y-~F;CLYK%Q{4211i_K+lFU=;`D=6!n09yN>bBRGxk-v|cF`9M2?7 za1;>_xHl7NcJcvRc02sSsUWbN06t`6vZy@z8yx+?UKQh6lhFs<+fXkGBFo0)Q~?=0 z^_5-)@Zq%WIq350E!gw3Ao5sWHxWNwCuuMEK6}CCR7bW2HWu?+#^um~(E`Rlr!Ug5 zwz);(br=99CS>Fc(46BC`qG4~j#P@iY|rg*VuI~yx=`A48h*M?)%HA!XS2vfUR`YI z9R}l|H)Ne?JUCQ;$$lX}R%>QEs~PUawI@^n)eO{m5hOcD)S&IzmF+o0+j9Wh^Bzq) z_!C(6bvXGjA$r0zr{X4P;iI^~uw4t+u<-p-n3nV^SkfT*CTfYZ@zZsdmUwLjN?Z`E zPQqukAD;suMvs1wmQ1!Av zN9KWHc%u7yUl=g*K{z|J4ZnMHroZt5G~k77@f~k|oQCzh0Dx9u6^VA?$w5HsS^u7~ ziwV)cB2U}8$oztrSr$wm02_oZcOHJazN0kW6dW>J*q{ZZW0e}9znqKYV`z>$IoaCZ zHptPtfWDC6i|!}cnz&7f4rGf|ar^{Y3$2)kpRRSa6?fp-Gzq#|)GCgP5ReR09H%lJ zx`c{jszRNvQ12zw8io2DLLC@@TIq%JsD(N&pD%7^#{5W)Kskn`fJi!e6zHGY+iat5 zDmfkOuLmj$&&u!BB*uj3{C1&mb57qrOTS*CU+Hx{tCZ>>rM{_@`YS2*iVL&C^!Stl ziyfun9Llank1dZrwmnNZwwn-r1zXmw#2l`2{|tn>E2S7;fF$#Ehp|LU-S~9fizyzK z=q`2DMNFmc+NY>-4&iZ#{}RfX-1~Po;_#&mdX?e7dF`=8`X#-s3DNt`a73zCw*DGQ zyzBc)q}|EZ0Tj2+=q%)<+G+HoZ2XfiNjmU2v?@+k$L?@!w=0vw%(2Z#+wNl$aqRByh^r<6Yw$Xkz5V3!Od zn~8z270-DShDdlK1HcsT=M&I7XvCfumvP_Y>d`31ZBLCs@J=0fq zuqsGg48MUuCf#AOVu}uZqBn0^8A*&=Oo?jouDc0f{VR5H-t`WwyYOtb5Uh1_7>Mf> zcaY5ppUs#2B;N0@kdWqF4IRQUYBnne^XMEAYTK-#NSUJ%v_GGtWN%4~d)hRzxziVS z09u|DhIak`PbOOVMt`gkD&)wY1g+|` zrjqpKTl#A|rJG9!Tz|DD1bwzyL_00Qi9UP3a6Hg$txf#w^In}NV{hQ#o{L45K|4g9 zDS43#c7#r6lTM2`Tb5efUbJs}eKPo)gMTCOUpI(9_qPM*m<)(p>zV=xBaK`AR!=V<`l3^m{=iLnk3l_zk84J(o|7=2<8n8y*zn+F<^G6zLOf+XYIzbC%arDsUbr z_P#k-3+evd8%Q`70yehftVobz+0Q=x%9}6BYdX4bG zF7JgXmIXgqX)CHUQ%j7agZxUZgk)x|`c~Nl(rAs0f$Pug<6K?V{+9wYVW%H?rtF|^ z#KHVrw$#-q%8+07ZlgLN-3s{}!5(VqgZwoR?0h2S>~K9qB*JIc7!;iPJ*3Si^nh6c|Ep!MVxuq@fq0Q_>l=4VtqBE ze?L1-qOV=}={ld^(^X6GZ2l}5+Pr5F%%}Vc|JLA_|9Zd{eUru8_7O=^A6ES_;q1CbqGIC>GwR+&2Ex!^$v~`oYmfRDSOTYQ6*B-~{zlrk;q^>cC!opaS(6vX zSDa3T1Kc7g&VPy<-z;wO7{HD1Q|pNvpM2apCk=p_nJuZ1kEi2UHPLh50@xBbJ3O&* zy{RD12&`~nBpn%r+!zS&zz#iqaSYJ%eZ0`~4|RZ@u5CT_i{6JJ?ritUp*$sgJOp%n zq;0F-+U+z!X85uGwRS=aPQ8UE6)}i-^se|4PZJk%p9;PW-U&p5h}HMj4%cG40nTm$ z=gl4gEK{g)wZOY9 za1;t4`*>zYlM)HJd_{N}-CEiS7)#;<@RdxNONmWgiZWr*umbT$a{^r1@J^W5{|d&B zcAp0n^?-x@$p#=BH~Wmo?eU^4JELdWCfI-(lM%k6p9wP5SEs6kdC~pGv87e}BX{*< z70{&!b=#oSJEwv;XexOyjKtZ44NX^Vj3VZ*<51Ov=msd~m-vV!CTfXISfYw0I-~z$ zCB5BpUtaAiV=ZlaYir-(WAqupfz*~BTKyPSy!t?w6X;$FbS0k6Ld;;H)I3`%sXQWZ z&r?nG);><_g0Wk66Em$k`$6PJ}!t@Kp)rEk%Q@~2X+J{9_3iK zfJzPc1OB120figF?>Ln$9ZIKcIfj516?BSk{{l-M|34mV?_{euT^(5^9t^#nebfpU-GC6jLhu zOszos3&lf-0*s<|%bx>DOv&AFGI0H=;`%U>%o7BUj5xeN)?j0x1#3nXiM!175SSes zz>AR=GM{f!C(XoYAdkq1e-bw6+#IAc#+s5Gy({XB8t0rRr)SsQD!&+dGe>%E-8}i# z66PC}0NS}IQn4OIzIofR@M5<555xs#rHNl)R`?sKzDQ03ny#-bRDJP$q|0$-y|wrJ z1H7G4*UTbmbnf2TrAQ%^DM*3=;FsRoOPC0dOpLVd!RVaeh)Ap8h>ZMXMy>LOdRR(s z)1IF~ycHlyW1)L*?O4E@gsudt#`WN|d7l7zfF+oo&3Z;80vMk$RCRVM@?cGvClYPs z*(nkw7hBbiq72%mU~w3<#cRH;3UrIF9>1c%{|N>1R7D~COI7Fvq+*bJ%#%T=BOlzx zlZld4rwJYvhflQ2D>3$%L#-Kya&pEcq=n_^OF(QiNp@X4K?h2>p}=s?AD?BNoYz3z zm$&9E4Gk7-3gI`I_fC-Gas#cI#Hkm2OPZ%n)F(7+5xm!BX8N&ADBVu4=_STXY@j(F zmGmwPYIU!4-_G-Qr*P<*UTi_k3;IRiL{NS!(z=LDPd7in+9~vj;sw|=JN{@`D;Et4acot4d8PW+J~qd-G+|# zPp815Y|2n(zlO8(=_}_~LIP?KqRHocDS7qcQTH4NOhfovsyt<-jb$MtJDeR3BPb!c z`1}1mASr=nXnsGTI6me`NC~@h0qv4?-eiI4uE3i2OW6rRh!SW}0BiV*;c5!_#C7`r zmnQ?D40qrf#wzUstojqhhujxGU5hDRI711~X8Lq(Ok8?C#|nu==lZBI15dfAR`wBe z^eT)RS=V;DhnVLw=dNo&BVsZTn=0pt3r^@AlQWfOx1-UYo+}Tf((ynvQ=Mh=*j5;^ z6(_$J4^2l$pAZcZfB*Z065kIE|L(1)$t53jah!YwGsoa}j2wtD23#F8wEE&xpotU5 z^188f)r9>KLYy&u+JgfT&RNvEJdta(82gKv7ZI#maU{#(`^cbCHtp`}=4!w-Vn!7vY*zht;>)-|H0qU+nj-a=&k74#QN`3f*u# zsqX8!-@k5J5eZpwtu4QG?)R=oX5sNXtFD>H&>%@&ky-=o7|7vn6uHco=N<; zZSKcy4OX5+dbXCcg>^i(_&ANL;Kz%`Jcnnq6YN+Z+aEOBpPB6k+U59c)}^_VzE!MGM!9!OEPl9wYih zg>L%F7V;A&nqeh7xQ6B7UGu1^N*QkNMr&>`erOMi4uk~o5lAKW%^p(JB?HB-bx zMrMbI@bqRdLsEoj|Nq#zOlyckYCyj(`du&vT_113SAfy5ivdR!_ zGvHaAduyM4l_3rt2u|ij8e+*YP+Yvs*E#h&egi!fb{n(_6cre_uBargW z);Ju`W?wE8;BYXJo-HF+^WYGYtkV43&7$7VjOWNixsEM4+PktD1*b12**%LBKf-%b z4#$rO4U2f}|LQk~0tjbRh6VU##=}%KdEVDhO*jCWfP&CqQV7A9QvRr`IQ%=0$br9m z|H3~Y7ydUK{Gav*{{Q+;4*r|>6~5C)u`plX%=RmwlhI&?y9%x-s{zI7{|z6!^7jk7 zwxPlF3w{1@cn*F3)xYq6oD2U|2mi*h$WuZ-t8(H0p?~4uc~}m89(C~hh5iTR!oSnO z?-%;~>(CthuUlRCx&9l)F)krcZ@?KhH3(vd8s&ga^W?o8c(HzlhxzP&#V=U~Jf}Z$ z8%%YPOTeQ`1MCa^T1ta+{VNx}F#dM*Gralz3~#~zIJ{;3jNkGy;04Fm7-!W`?A+3+ zY}ZVBX)u3*0X_%Pc7!>Pu_J(Oz(8&2_u<>>#{YuRgHr2nywj*_UpflF#Gu}DkYB|u zOf6YOK-=V=A02AM1MT@+ptAM*p*`A!3nu$bc%+X_D2pG;fP;?=QiprsIEaCaU=U+t zI!r_`*$dv@Sc|cXu&+dvz$a-oK1n)7Fy!9oiqM-0u^9rf=z9*IW-PqT!)J=jP|G?V zEh2ejNKU`vC!jSViN`YmC z4!+gz7V0zKbxjV3nul~YJxj^wW3zShqW!Jpe)@p?zZigj#ae)W6bFbRaOtW; z)&l$u1NgrWc`2K@XL_@Lwv0NgFtWTUK^DE((q>yegO;Z#uo)l z2V)YukZY@1UUl8ahXh-t+v`uMDm}YU%HLD{u^YACYU0QC{fA!L8kFDW;5941Vh!=% zZ4L21X$|ZD;(1P! z-+jFKsR9eoIqQLrK|gqe`oRMDA$F*AZL-6w>C!T4(n?)_E7YDru04O;S8CbRwWq#} zT1vv7aIio#X{3|#tO4n}d z@UJWle)af0ac`-q(KX~3m8B_FCcje$dHi^LA8pUM9=RIJsHGIW*fI9}mh#~o5Afdx z7ev1lywLveIu$^=w!>9$Uf~J?eUFc&>Z={(=(C;H^3+*gGe(qAOG)@|9^lb`{xQ;u zO#GC{*88Yo)#^PzK@jd|;2Y(Y_-w@(r(V@3m6jqXqlJ zOVn2V-XF$&vrzkvUn}*!4BHn@&yOIv{olUV(aXZ?yRc97Iem!$R-vtksrE{{svf>k z9NWooUEVOP1gzf&suVAYPW+{^@14m~Vu2TLeNS$62n6=aeK(=|G0b|H7wwFi0bB|1 zul*-luKj3Q{@P6Q3PTmgM(<+DMau`uLdV)$`6j{UUqr-W#C(s_YhMGw1s2dEdmO8k z;*n|FnG}Fxl?eWI@jrg*t-bwMgSo*0;f}^KFc2w z%yBX+IFl8mc3&re3)6uM>+gC;v(97I$;j#^czSDJyfM4+eZ%JmTFFmX$%k6WR95mf z)_nk<^8nnV4by?oTxR`9v(9JMN02pu&*U6@L_g$qPBiZ--K&y3j%|DDYdb5NTE^k1AkHE+D_txm0eFuG*@q1<)X$gK-WU8{}#ZAdg%q5@!| zdqZ0{UJ|_qsRO`-@xbGAG{t-(@+5oDb^)k!1eAZg+ug|1bqqb^bk%*5Un0cZKrok` z9l-1DMl_Y%bBS-ujMvidw(nCbj_pA0=;DZ}kW5EQ`DW6N9=?rLn1diK=C25&kwKs#kBp9xmi*-^hz9)PTqWoWDvx^5s!qi0 zY>o+0s8P-W9QUO87<%ksZdW!}iCtf&FW(xc`I_vRfn1;eC`YX*8%LU(qrvT_5H+gH zxPK_mFA;!I>2q5-4Cte;KMSF9ONN8#1F0Z>Y6JNQszFL4GrwEaQ3f- zG!Ho++&s!Yf_}8ExsOj1X4G|{S>7hcJ0_u>FKIhh;n~cF7U%XjX2L?uH}QX>Q_HX| zxJs3~5@4MYX3j?Z#{31*gYQ)p*& z2$O-fy$rj0#>+w#<~zua9Uy#_hLan?+IH01jv*mM0Mb=wu(oZf47Rp<484$EV5^+g z0@lZ|xPqMssknvDb;kn;WHUwiU;_C&gLe=BY#NIVhACGtF^Z&=*mw-oAo&8TBqhX1O$|$(tsL@76 z42o?K(1}GI6m_&#<5C*;SZ$l)M6oJ@fl3^rXlY%GxV55IQ4vYpf+7jl1zc;jidyZ7 zgBJG%bpF5Jd(J)g&di-9NWcI0Jr6vZIrnVua^CGM_grBE77=*sTFlu)|06x>Q?M2Z z8qbD|vF8}s@dE(bFJ_G!9EpyuD?4|KwCBq6w1fTU{-QZw;HP6I zIoMXd(*cl$iDtAg+L#FXFfsG|wog3meoWQMk1JSiyy)gxVv#6RUR>y2$2E?0K{G(W z|7oi`MP}Bu(@Wd!hhTdb#(;NSI@cQpkAz{Ntl?F-;E9qpj-xGjnt9aB(=WKmnSOIk z9L(@A;DG&xD0{%@(WyQ~4MzXh3Ifde4Lt>N<{>AE&wrXS9(}R(%u8~(*M#ljN7!1Z zaR+i>B*VB00{&om-C{)07|qZ~j3(=`oW`tmcgI}db2myM z9wcVzTOM(a|5-};w56TsD|6I*}iblF6*ci-fu!jYBRX~maqEAmk9WdhR=uVprMFAFlA1~@g9#(1}>pjyecBP{RDnG zw&x2PZRMLmTGgeT9VCKJuOTlP)tnk!#B`$kA^xPEiLX7E8n_XGI|V=>3ttlM?iy~(fq;7rW6+jj&xCpH&&Kpd4s7JWMh^V{;=njW z>zFclgH%|0|HvB=X(i1hOmdkTvR7`W;~^%!YqZP`x}ykM0MmV`6!TTdk@uw;aNed5 z5aqka!U5@f|4ve_oaBkvQo0-=VlZEZu8boDGN;nRiGKK$S)XK|q|7WTtqXR#mfxc9 z>+m6p@dTj7IKjp;2Pr$7L7Pw5YhjvroJa~nUVb{5^E|zSt+zbS`|MFtAHl_VXCq8$ zLKvn!bXzb!13M7l5pE+!aJQHExX>ZV(m*?sxcz6l+xiDuzA9<%`wZeZ27<^9)U~rB z9OFHxh~VS^cGyy*RUJKy;<5H~>t4w+_EH^n~iH2*72ke3faO=9o z16V9y?po~R~46X8O9sr));C?!${;{Wu4-` z8Fm=8Tn}YNmAY?dZ-e(SFJpt`vyh3A&Z5qBT-{rP)nr-Jai=c6_-1C>`L!A7Ts8}< zrXtQ^wL9h~=KFXs&S zPr%0UYgve!3@`pU-v{Hu#-`h8Q^ozS%do;}4?zP~tphpFiz)_QmVI@ZU_L6FPKql@ z4!`s6#{!ue6&&oKnyuhg4PcM`qc8|+w~E2lka_s*4&N%}nhPJ;y}h4HKh)v9Cl+Oc z@@08Z{1&)`3b6KFye(+O3xX=Df7|C@_bAk7A%2nHM~h1X(#s-)4Wn8*!Cyw4Wgx<_;V~Q^V7;*4pv5kGliMyGT^pzpvwH{jI;rZ`1)7a_ZnyCD5e&ez$sSo5d7*H zW~^d-tGQuj6q|9J26RCj1quBS_C8d*5BT;8#d4sr8ZitwW@7v%6LQCMd&2Y$M zMzxDDtk4f9hfoI|1|pAJtL()xV#w6RTe1U$uhG`JsH{+{i%erKSF0-17^(rcgvQL( zU+1b;Yqxb910B%O6{*J!6e6W0umNfF(9+q64MYw)8&(>AfGlF(TcKv>fu{TY-f-5=fZK3vJsyoFF zMQP)6bYqeC%6D7&{-eeISm`eq-#}{RJ|l88Vhl$ZR^Xs_#c&*f{yL805VnAh`8Nk;W%H5|t{2g)Pj1 zOwM?GPNd)HC34sxY)-H2&x4gW_=-RUok;ZZOOpLQYfYmSr&Nv&A_N^06 zM-g*+wkWNw@JsaSd-8w`(@f{G1=RtmS~*(bJ;0Fxq*wiO*#H3G1h|mR?Y&*FzLD3* z7BycHj?^-WVFcapKjJ%g$4|$e9LBct-~F>RpeFIe1c1X5$&^%#POS>>Q~-rEij6%% zH=rPEcRa`WLzq##Z`^~8-f86pfVt-@(kYM8d}eJQ+n^VVMNR6oe)OLZD1@gIsv6ZX z&U1v%4Qd>p6Z#8}*j%qh=45WN3qK|#mh(4a?iG{MvMsneC_pG=clUkjQ$X6;sO~Da56~`j)l#80Bn#lL}pQnS4M&j)Qj2Jm@)*bn2@``TNt$;x%f#*mQc6<2g6T<47{k z>)8GwuDRk95*{5@SJvCufr?@J*ZL5#+aOkFWd7zlHPjo7?`xjb`FY>tIykVEt#zf$ zT&d(KF}`%4Zr*Ikk`hlG?R4m~U*Oqj#J97l0!hCM$HbJot9=fp;&`$DRT_9TYZd>IQjQ{ z(rD!)$I_uA-vdCItb4=pW7214{Mp``pI`uNWKKxjtUfwjnE|CA0-eP!< ziLcpu!5Lr7%V$%HVpz43Q@1g7ZGUImDa7UZYIPFkVB}LwJ+KL@OXf9_$%D2BaD0vh zZH4U?apk{a*W>NP4IrcH7kvpGwiEbM!cX8cB`C({YS*S?)2Vk8DA&K+oA2==(6rTy zB|ci$J{-Sx#xtvG1+(#ik$L!~O!G@Be@A*k+jRLzR}|OiN*&?U8Q1`eqGT#+2=C;w zr;qi!`nN^VmL=*KD;)>f)7$mxJuHmO=GRYFp$_xmC^Q1<4^&Bk@Me`WVlOkJUOhqw zEEmk*{{sw8JIZm=jnt%nhf$A4@Bc=QJ3X2x-G$+}m>1`bqsk2Pi(%Oy@5fpB&Y{oL z;k!yeWDcKEO#6dMbGLWs%LEVlYOumt!mzaX$H76oS3 zVU>&+_?>e;mzb52V* ztYK#h3yC2xSad1V2LTIIfaEcdZdnpm4^`+anTW;IxrQ_NM5%bZ56D3WkiA3b285&LJk-=@e$VLjm=kq*DkY=UeM z_`WYHbLS@}ODl3T!#k>+d!EZNv=_N2C*ps;b4kb>#)1g;jz8)}u5y_mWmf`&s4FbMcFqqAcAwTz8e#Z+yO zBZRDa8LB!VHxHq`i&G?&V!Rs{{Le)1A++PMstu3)#8P_*?V5{y523Llwk~&jN)Dmj z$qw7fU&K!;^h>mG5201eXhdw-%ayEb-uvZzKmJXE9u&#HLr~Srzgy+ZgXZ78Kl9<= zrB7P^9d}Va{`H_D`27oZi2Y#vq(Xb5#s3KZGUwNsbL8(^o~|$cqK(+gPKfKd>2n1p zdb7>UtV)glYMq{$)nh9Oi+J7YS83(DdCQ>~-Rg+6@_sCLm9m}_gDSP_m(nYzZPg235UW$W2vFHf9A2#F*T76KGm(A%;>tx^S9+^%qHu) zKmQzc@%}%Lf*pT8?sGs7P3i+ocCdutsTV?otF>HwP9jpt@1Kg_YOMpW%nY*8Ij|F! z_rkET)YvS>E3AI>H6Wn@&JTh2aCqt`W^u~RIfwzOsgs;UJ#WA=_P*@C(aG0sx0bf@ zq29)3Y|FF1zMlS)BfX7yV2L%}qXC8O6TD2Tm6GS+MLM)g7QY-Rt9UFj7zT01y_|ml zj~XG&>^$KCoz<09%vf&hrKcDyLQlXOe{?U+qiqj*Z2%=s!ZvgQ1)CxGB+sx-fj}iG zsA=Bb&C@uWx2?l!-gjiS!L|?xUYbGfYztO#f(kjy6$JehZes#&F8eE`7$R<&4Q1Ro) z^4E+HBNn4e4P1Hm8|zkS=w9)SMk7yo)fBa|3XW5};x!C&-{e#cFlteopNEWdg^myG?+*6&5nKy^Q!J=hIu(V_ zoUEqHN+;efMZKQqaO}RP3xBALHL40zL!+LxtX;1TgTfM21hoz6&A}f1Qarw0jQ$C8 z9$}8BlR?!e9!RT{}e zVYfOSK*6cWs7vPX*Tb9@Y0S9=3r7CTaYSm4tnyk+WTH`T9%@;Hwx2y;*WkpbbxSByQSu%XyYYbh^U+4*e0MXYSxe<>du~Z5Of$;To%Pf z+5#hyl-)0hDW!9ih$*g4-lAc(1tzav=gB^2{~h7HR%($baCin*DxE(d zI(t2nnt;HcLEbB}@i8vXZby25IZPE|-3h=SBMoEB)ruXtYV(!dPZ;zG0vnV?a!Ivo zUenV^6zt!?Fh z-rmz|8dF+2VRO6HF;?(e^vf1M`HwSFIW|TN0)^mnV9P!9m2P1A*#BP{UWnkdm5=jM znjdwcpcwoQ_w+w#ef#$}wQ#fMeG|&m1DM*+*5Y*}X53uvE)+9a{EO-T-G55|dSNo6 zK7jTca6f{|6a&sZzm-Rx`%{>5@ZRE^r~T=9&bJh5PkDzt|5itw*+)DJwWmBCO|Shu z@b7qQ{o9Le(mFZsU%9(+)W4|l*!_FpLw1#GuI)Iz!FR`x6|rObDk`FWm>ss2 zpS3f0{6?dNcgODx-i-#M6C)M~gyE`3MJwfO5tr-qjbXKPI9yw+g*+7w_Ux$UPNe&H zf2DV4Mw??>UvG-`r=_uYg&n+nPEe)8w&NS|nuR5#+w?AU$|%->5m300*xgks4dhX*pxphXzY@PM#YR~S1PcZ#J8$9(730M ziu*JUe8;T~hExma?!L?sGM44{!I&MW^4M@w??qPR8 zUm5Ei(A9cCuiM(n)s&Nb<%$(CpleVO13H8qww0fPd08rS3|hEyRWakYW zrSa%MeMhj4NWVJ;$ky#N$-Et@X6Uv6L%USRXGbe1AhuN|eV|rZbBaG;cf1C~Bu?02e%_H77vKdk&c?HTuDM|ZhBp(5n3ogKE7_YpVq5?Xl2-I2eTv-DY`ZEl!~3a;sVZd)7m_%XV1#PXs2yy*4S z=>2w(MepbS=&>TuTZxK5ZySx?&Vt^)8R%VL?On@;@ozbX$!6Q}0w4UAo{i{fVT*eO zhw{QLe#lJERn$-|bCjuoseY;_&%1JMM0aOXETW5*D7bqxPLtfnY!9Z5tW^Op+fcN( zu@(5RtJqcvXkUkU6JZhJ#pVFno>^7Kd#Y5D!TqzU?20Pp z^lq(lHV26z*i;r^MzrrFdybM7%-#koKAf`r@z*y*-R9^Vhik173NL@=fevl2Wke&- z>Bkx(jYYpseVl`Qs#aSG6JW^P7l^`YFV60~l||(64TU9T>OCog*m)a#$LW~20fv?q zpk+y=T4)+*F*L*TEE*WbqSp79DK8s+>_?cA#`oDhmJ96QO!RPI;$4aDR+FfUIp*qw zW5)+Q`1lKqk3Cu*QBOXjnGXyYX-zfO8J3&3Mloy0mozJijyK$+^V)=_hwA=FVQgS0 zV8w9m0BL|1JA#yPDid{fB37xfnrBw;fVcR5ga0fbYeb73$Q@7J>MjwY6>M3p&iR0{ zV0O}~)ghobnE*cvEjxLKFfE-;44FZB?X%<^bDZ&k<~gARMn(pW0S9bA@&Nm6VvP!uA>`C1ckq(z?a*|f z0nz7G%&1`?CxD&PUVN(cMjWuVjCbUMIrG-uX3rB{u9)CjS8Sh~>@R6>kc~Ux%93)8 zRm9d39OY3;-B<>X+?Ft1yd&agdLHY+B`&TSXyCwU5}ncSUH>%TF~c^$)iiGq4`%wj z8b?Sh9y$7Kbo%_K@jH#S&!3p)jp(yyHgVp^jXG-SliqDkOaHQWF2IP7DkdM zn7TS71V8dH+eCxpmluL}BZn|tEDXI(B1~DMbG*q0CT08rhR7fAKzkX-Ez`23mYiNA zB~lAndIKq{+i-EjTsuhbGFW5MvIL10YCEhCONS7p!FW@yV#YmC7JXYVfLr5xiR__k4LZ)on`4Crou%6{)m@Y7TpObu7bQFdrl7L5|Xx zi&cj6UQRLmO$K{@_N`uuHBp~=f0_64orejad8EAYw7woIXXQX_cZ|8>g??;U7K00H z$~osrb9fsc{#S9p#(NN~Ta;-%`uxNyPe3|U+;ld!qQJ;l_K-t2A6 zB2-M(GTXUpkrS})k1@me54YA^dc7!;9zTV1hr;*LkHR>@E_bUd=>UDB;G+BcxwpRq z^7NN~|6LqZw>pO%0I|7+_OR@DedXBGTQeANk}f244Hg=-JN-34+wgly(1(i|UVY?4698v-`*+Zw#Qad`dbz1cv_S->P-IR4S|I zco>KT?x1F9$2>FZ;BurqlFVuy=kb{jp1=>P#^4bDBXX;5mKf6HpESO~f|vu*j9!KXcpXz#Cf_1- zUx@B}954O(+8mX{w0WNw+N&AbeEG4@7<(~K!^as02}FU8L^hSlw{v3SS)cI5MT_gO zM|?$1&@k|G(4yO~ei-dxjN2Z~-H$_4x;(}xkAAnDMYxryzx~&SgN~`tXJh;G9$Kyf zE~EM~lIDl`Y6BJ1FE>FVGH~xe$1 z5X(|fhcWBfK27I58!8-dEk^23}v3x6THWypUIT(V<3J+ z78~7%(z$lT1@&uE~6nXR7olACWNC?sx;$AY~2eQ>O5O3aX@y zdhxl9rULSVc%cwOS={ z9GH|mTUB8JDbcCrg=_Uq>&e7C?6&*``gu&9Mpe#63Srgi@u!5dq`;h^d}gq%QpOM9Q76g~&CX6t&dASy~8DqRGc4@4J7m@cAfq3L2T z>0&$9kE-dgmF8x`@~u&zMq+9reMQyf+?W8DVP3f{HdVYoXU_NZfR_()X5F%ZwwcoT z<1dfycz@ARNFdYugO2ztGI%$YV(M4-lWyoafOE6+2AENKhOF@UHhD%;-$o=OUa#(f z^vtH{)q@qiI5}wbJtu1NRR(?0V@!Rh{R0<0mfh##DadN=hoTb86^jHP!7irulChM6 zhjf9s^fF!d1x$^AyEp4ejJPL@{dbw;_`M>}6?~I2^iEq%*TvBYHPh z12H509KuRoOe|}lZRKAhtzHxH)K#OWBD`rUpAp^^z2iDOJ&wfRbq2NS{>Ozpoeds! zt1hrbI|KQU1I8Zh!N>^-ZY9nVG@=RkMC7GnxKy1lIm&SrT{W`?8X1JdUqUd5K_&Om z;XTdAFOm%Kp?xvcj^$NRwpz8lth7?QY7~e&j;mbLmQDp5HoP}|=9yymbG^H`w6w1* zE2MdPz}L6`Ka%X0Lbs6>k3R>lR-I1X=Iymue06`Jg8f)H0fcWY`S*5p|L!Nm^TT6YAp7TwOR-gv=uDI^{6oJ0?jjkqOFaZEni%+W zqO@#Ou+QXvfhj+R9({d2Ji%F*qP2|u5Xn^Y@F*l0BJY0flGujT)qe$NTZ50H5}G~1 zX0I(lRw8&OvkFj=WhQJ!4*K%X7rgdk9_UK9A6Fqr?dT@CZRNWRfr?!xhjsL&tHypD zv|V2NF$o?J=2166?|tL@m`F>9W*5MR7!v^pjos_h3G*2Z^S;4=xeH+e&nW!7ge>5x zHsG;nVve5n(AKDT;cm2H8tny${q^m1*Qc=S6}szV+4a-npfD=6Y9;J`k8>xF7$DlZ z*Y9(%8_oD2nsRRq{wX_qNq5$don4I1G-<{lnrIlIg=5nwvzT*rs~2HIX>buH@Q;~z z(Rxlrk7|PKTtmnLtR!=YdNs}TACqm|I8G9Xc%ha#6wp5mDZclI_zUM#dv&j=gJF6+<7-r`lG8>-)ZF=$RT>#_Ug~eK+_U9o z5y{do7e&>F%SEI)FuhUMjQa}>`O{X5K*HkjrDTtLUxi2Bmvb$C>EV&2gXcer5rUUl zav!m}Y$0XI=%i6JKHq_wc$BC>Zc|~38p8s%UV~q?91{zzf2XnAi0$_7yL7h;q_(+u z1l=yiv+cGIGMsK%z}8=}+ex-tUWn6)ELc%}Ar2q-k9UD|;ur53hn)^r^+Bw#Wy@{S z>9^4~j!G*dGFzbp6}B)HzGH=I)rbmmG!jJ(#9$SARd*79P^<7@Gf#W~p2FctsYTxK zVh2KET_Is}(V^+mK@WM=vqHL>BgL%7Om)`X4g(LT!LO(MG?}-^n{2Iib_RGfrd(R# zFZm4vVDOzwlhDSyZmz}LF?o$GDEkWu=~iPg?GRzq*N;TyjMOYaPo02ZH#c2}phK6g zQFx+#Bx90@%M2YmNw%RqBrT>bCB^@ZSjy4S|K@L2d@^lyC%Fz)tHG~F)nr^Drt^?( zRP9jvAXnj4j|{t&Vc1FaQ`?_fi`yd{UdR!J{*ol?JH(T&+4 zd*flR)dReTy^al1FhcjfXRmOav%BwMFII%R+~j?+F4}<`PQVC2;Za!KHezJ+5e&F-`wm3x2f-USHFTDKzunu z`j#lBG%XVmeu<#3LY9ijdiiv7U(i?DAMW}x{NbKZ7k$A_Co29g6>zWrD(EFRCeA#N zicOA3spYHq(Bhd+!Etn}k(yC%F>Mi8+^rrIIoD>M?H5aD+EUY;cJjH6X157;3AQ`N zr+W1+d?k%=3`RIkd1|6=%#_;3Tk%2t;X#UnF@RqDknWb+bBssnRtZF3(KaK6mjVE8 zUb+Ky^%=WiG^cDK$8^c7_RR`ZG>ML_&W4^z=Rr8hn;kd~!_)NjTV}`b1AX%UXav9W5 zLV$`HzmwK-^uqM}Z+ai}BXR&06!;UkTYv{<%K7U#=5{)QaV;DrWVKQE4vh^*GVQ}G|O(x$fwWmxqmRc>{{uMCiGwd(^{|X#@|qmVdA{0%&=3nY93uTDm7_pV_mt@(Fw$Q5IIvX|zWB$$W z1S2EBYw?2ZNF^dMh*WK|40P3!E+ABk9Aid?XmqU=`EwVB>2RzT7imQtY(3{ye7S1w ziXz*tV2G4G^SAa9JoDKa#Awv0xf1qvivvd&;dG65abe2#7h`{ZM^)slcMecPx=#P& zRp|6vU=?)wmA``@@0iWL2D4^&Rastzehgl6wyUR*o)_mjUfpgK{s|p-ysLryhCnVB zAaYuOt8qUyFzs5}mFNTUqP%Kft4;#+VXz{Q4B6GV2nB8Y7&F0)>PcLIeJS!Zu5+Mki#QTna`qsnY>5@uiI%;7|rGiC|(bef##t4 z!8I6#;AeR9^2N9PKAm{|Abvt3c&zT`$5{C=?~Ilof-JQa3;?^q@h!tvlqEkx)k>*)3Le$x=LayEC6sa$e7Ph0I0^+iZ@)zF4P?RU z*VHX$huHpWD}NSQ>P9#{z8kSMGCHN%P6GN4r1{TFTbjQ44g;*wU1%7Uq2Q3V25;bV z0z;EE6e5ViubNB??MO-@7)Ep6gPSDQ4jU^Zv)RkW0J7BccoD#`@iT1Lr9%6M$i_F2 zcG&n5(x?J99*8nw<8rCGEgloW`=mI2P9nHRb3ax*MFSm}3T=Z5t-(Rah7df0dVx5` zD|F(QSW`%p-z}ZY9puO|WIh~-4**vWWeIj+)XL@nkU5Mk@vkfRR2&u@|J-pqKsvdO zon)wiRa#&9N(8^&!OG`&0)Tv8Hb?x#DW$}8D=eWb;R45~;L*s4Z;}cfir>u9<@fGj zmVBIOrb=*4q9zfX=E7ZxB;i(QxQFsFzB}N~*KqekMtmE71P^bc^4N*pc29+dqAVL1 z65UIDJ|1fMP-pPD4X3GXN`ym2|;<@+9CB6y$+?P?^+%@tP)b!+(;-v*=Cp`|5>jCg-7 zEN>z0M;!Dchkbl)8%x%12GE%r(DeeyV;?UkAlS!CeSx_9Qw&P-Q+JvRbRm+Y?vpEo zx;+Ib*R}7~)NN0ogL$S@8%Np$O>19YG-wLsUvKbvX^3S)mBHr}jnD0TjC=6;12X*a zxez5ecu#~1>1)izVIGp?$(wD$lidUdkG>8e4vu{om;-Dfc&i3mnrjTaUuk$t_~_N2 z`;cMb-G&E0G7G_La^ZC%N&3E(~3>P?Ucg;DSEUfW9svOdcqp zJ^pna%G3Sp2gvcG+|j43`_yuHp5JZd(vPIjuaUTtIK@5mUqU3Z>2LJGq&WIr&?g$u z*Ih33ZzrHV^baKHwD{s1w8%|=A^DyivQ)1^l2i}Xpyv}4ue@D{3@dN5azHHvFYLl= zGVtzc6;kUm;f?UZtI7$_dsSYJp}lL6C2}p2ByyAn`Vw)7d-%H)8R`7JA35vK-x?S6 zR0I04nL_W$0@`CAk0NNtK90))wJ3RdcuPy~00VTq2KpHvz4ZPI8R_)CfSi2v7Qye) zF8llVMdgpCs>0N^)=^d^? zj}XuvdUqsfhu%RspcZA1_i%w;Xn?j~D)cTT`CfV#A|svNTae?&X-#jA`B8hhC9|J_ zc!-90f$6#+TpHQg6l;rYx|7v2gaMgKLtQJL^|^TOM1J>gZl@EQ!fxl@Ia zD@m@Gz9q=8^ex0gHaDMnCjSifmI0QVpCC!W29pCcCLLsqcRl}3WaJu;D^TXo6L-BToikZmKv?~XB!WA- z`1}S*a^~I(wR{LZdqY#r{=!bgXD7&L12+@7l!lM2%WKKzHmD){}!43_c8r{1O8@@Sq}VL-Tq%glKk0P_us)`^77|SWRO3%`1YUQ|Lo*~ znPk8$xIox+qJZ)EpTh{o_@4uOL5Wi4hj%~r#Ec>6W8g(JyidtR?|SsR$Z+7jigY&X z1fDD9#~z0pM{wG7UHcZ;^s@&WCsZ{gg8R8}FGrH>?4B%iwg_At;|Sib*V4`!d%d*rK|5i@2QbSiWY`U9I9-C-+kyuK;1C?9H2o_U_I>r{%9cf5W=lnctwo*I$t z@pAE@IOhm0&UVpx7)f&Q>t;dc4Gw19<4>PKM!G-kKu!*2f)p18KN+y>7;WHRpy8jE z3ICWZ_y^{N?c1!O0`v2NS0aDBoXTG}7gJJ@7Ag@&6f;2SCFwG@&6tAOXq)~`SiSREwXEnB(lRbvUP&2 zXZ>RYk#*+NRT@%8G*qPi?%@Kx&;V`!jd18C4tU&SKb9gR-G1DU9DiM*6D)pz>(A>f zjuRw2`#XmaiR}HI8XruGvR`|+pf5C_+na>`e^Fq(^uL6Rbo!q_&IX}> ztxG?WLcd1hOhLj!|8YbjoBoIoCVumge34Pu#iGezanG-X6+M)U`2KLlcW7t)8ZzSc zeR6MP#6w58tBJ4Mcw(gas1HV^`?#F=;k}=A?OS={1-ylo2u}URa%lyUW9q|)miph>dZj+rz0Vl~B67U!#-aOK;UdoxsaR?T8dYf;!Ol1w=kJbR43VjUG z0RHuEupsiCeSS;$n*;yOuPy$S2LB^|CH%dPk{3V9#lIC9@j3=Oe)!KOc6#QO8|8zu zP|j)pbKqO);@f~E`91fSg73!^4==uNBjbDGI~QG~vsmnhJ&&4Ca?kk6a&{4t#PqXs z1k)o0Q(XJx?2jKnOmX_ci5KHlO-``LJS;vTT9vpyq!DsuR`{2(=u^o5e`yih-XM0O zM(i<4fY<-rgN$_lLos!v*5aEU-{O^^$hd?NVy$mZd^-{SfuElE*0M5B5Vf6)@Ht45 zlXspioD2!VJA;!8wG9Xo;VnI@b)P{^)V74Y8Jnd-6}f>HnE!rmDeYt6 zMKru8DJgLeya$ls!21i*IhgEcPxB1*2p55ANRri$pCtqzDhPPi1NS2WIo1P*q6~7D z6@B#jJn7A!S%!R!B=OlxKNlC_>&C#k_W%vd}{f% z1WEEMb*A8V3*|}NwZZRdWE6|v89DJQ6u++e#3EE<5c-uyXow)>iC_B>At!!a2kG-0 zu|n{gTzH*Gk^$eHAq=>W^6cH8xfvN2-kkM>7jWUl47@8eyz!avMtR{4TTghiKeqf_ zg(T@4s^Ps&dGyk^5*e1hrFih?XA$f3+qyu{HbCc}E=-LJP|tkeGy<*k&Yh3W0a@Gs zqT=xz)>xvGNRsH?H2igxskjIJYGkCt{|9pXDDMUQZC&_h8~F226Z)rT!jEUcKP4~x z9P4lM|6?is8c9<8BMosIf#_L(>raSSf9r!^YeTH%&F^mxbirJ0z^ph`2z!{q?DaQy zA;a-EHzA!3N*G%BRPJ*hS<;3Ym?vqNM+(fHU_uw^XyO3E+=9`B_N#m4hO0*t`>7D; z{4^io!k>#IS^LH*Li4MX?YIYi7c#u?AHs7Ex*hn1?9+-5E%jjoeWHebvOwPf)SL79 zM-jSwwFe=^xE!Dh!E12gEkKggesr=>`!yvw?t#~X3=8iKJou4X2;Q32mcEFAH$}s{ zDihu`FTA*cw|8f{t`>qf)rGeLNz%8OhPPWLydhqA1J@H?r3ZR{(WLWxM!o&K|*ZF~^uhPIfLc?p#gm-}#-Z?qp72(egbAjG~B$@i!L?LoJ0qXH5 z0|_+EpM3+d_M^18@m$ek=?xpu6E*1hR2MIQuS16A@2nh9i}If*xjN8P zkt8SHI!QQjpkU&e-|S6Ha_k3el@q5z_G-nyEi1wXfr%P{&JXlr7wEG{lHLIt=v*qBm)}@_`ymssgp3nhavh$#_I(}d13`D~n--$C_^nuF z851`6P1N|+W#TuYm-vqO zu>Hi1om6NTAc6Ul(>9y(@jo|xhreZq;7>@BzSn;u^gTz7^wRejGX7`iI|h*a=_~eq z!1La;WGzFIWR+^NQq(~2dD{OV!)L$r@5s*7z?|}(`~1>}|FnpVHHchzwD5O|ATrUF zpI;G?4S2p|sHVWPzmpUH-14!fi~q$)iu@lX_%EZzdgWsgGX7`eV*()g$%i}t$!8T$ zc*73Kok)@-6%&%yQp3IHvy#Xtd_Lvr2>65gN0z1T$_ZnEmVH ziCNnIdVYJon;(uj<)Pkxe?9Et*MuZFch8Z+xs|jX@snKqmLMZO!QG!u+m(c2rAFFg90M3v)gYFcrx)~5X3vhz8_;Z^m$p}vKK{kv_rz-!zby=Y zF^yjfEeCF)xA>il421F)EBAo42dg8puyV({7<~42@wpU9@~iW3ttYeuUVM_sD2UH8 zR7&TP-+rWMZVvgI_jk*oHAoVp;Togg2u7as5hoKP&-sX~!=N>0I5W;ic!X}K#@8cs zF8?wtO>X=7;j0$qu?FP}CkPXlQN!csVNlKa)kVmNpW%%k7#TQ$0>TQv_yO4xGE_sS zcom@>`f$`}o~Th~^l${=U>vfd2fbciXpXR*OVBw;at!V~ObGgxCP5~cX8->~WTfrC zzk!V0(KGeASpWXVS1eoKN0L}nX)G=gEOrB1o%;@m#gMG`W$FvyLj5hoi%J)Z27|@i zLxmS5f`w;4^J8eVouB;+4;TTvFGE=Tmt+1g|7A<;*GQ7sA89bR&`!ufcys>xYJ$Oh z^K$%VbK2FnqV(Sdd#M52IbNvSN5CH7+M}Hbb}yKt#v-ic7ceFkw=|pCJN&MG$+G1g zB*~Vr#_tB2tGEZh1Tu=oZ(>gTia)RT#fuiVgAH!eqr$sQ1vk%my)R)FeB#el$j+3J z;^Cj?!e53Yc~h$4Ur8(Ejejrkhu`P}zmPrzT&OVv^@>A;>Kz5DM;|sPRA+y`)CU&z z!O!02S#!5lddu86U$Wk}LWZ{h$-aNPj&#BOWW&LnB-F&2- zEAwvFO>t#j1oHoS_+C)bgLF`H4>GuIFCU~r zBc292VqB!Jis`SB(QMu&ydg$4F3;mfcLXX~f#2VH5BEB;Z7u7|7cvvwsnA=?b&vR* z;6-Q}4?Cp{rfSXGYAa;tEfqQemF4>c^9;%eRP`sFNkCTwupD$EgKZiGVef+#ig2KL zGu=^ZaIY0Ghw=mra7#V9t#)rAY=53|u;o&Y6+yLlubH;`80ob!ujMOHd~AxQSG)$73S+(R1us0KUIUdE5pMUU_^A)q5$A zm6`HbnF=*^Wy|BeAlk^|%_vTj$KRt&hRDdHj8<2qOvYdAKpbD~7IoX+w5`$K^3-B0 z^-)f-s?;6zR+8R(HlHN@8l|bw@+H2Klwse(lqaHY*TaAq?*Bv=kO$tk*MuYu>^*g2 z-%qB!<$TnhS3m|<+_h@Sho5R3#-JC=bEQjcKrZuIGGJf}i?bW)#|BkkzZ65SN~v=_=m zpB72gGCU@NbEPxS5Th@!M>j%#hoaIUz$u+ z`V1zEs&sUQD&2^hIaMj_5&!tE#ya7X;zPWa9dw)zKp>iiy~8o=yuDq%>7EMJ)3kXF z`?FM}Jl1^czxY}6%}qB&S@T)2CE3=zOw-`#EEUpg*kR0lEIS0;Ys`P~be0N+U1Pp_ zIS2jV$8xFQ_mIE$Fm%?QUj#T{x!0cm5?N}@pRN3zEU4V|DCKWF(r7OByiPa8xZR5~ zk-r~G)tPuq1cyqo$lvc^XVc{GJY+F z7NlOw&T#N0BM`t@i1LTJ$ga&F@evw#6@pQ51C16%)MK=pTy{D^DOHQ3O{$u7aN4i0 zM|chZybR%~L6%D0YK5mtQ2N!w6rO=dvpZgkU8dzT>qM5ia-P+DvW{*_;dlDj3gErNW&+L>i0?aBBD-jDc6<1{;lQ^l02* zp4EkrAx|_;cL|LfoC@8p+f(Dt^l02F43p8g7f~ZkU5grf$y@^`I(Gx=;=Cp21g|;`XBf51&06489XssvGy_NFsmSjsg4C?vFPK8!J z=!$pD5$|!Z4ujWAsL5(0%$s?gM;`#oa2}s_LYSZUlNIKF;~DWk@aYOZkF+DqT}Y!T z#(Q6si7-DRRX4+9B6yn=$KmM4F1(GxJPMPI(b{?3hKx9te9IHk;Jt3Abs02WLftg6cGe$WVz2YK`q357Iz|6AuFStYa=vcq>eZSK}NHQt#tI zkS89b-f0gHa;k2h$Aesj8U;N_%y^JkDm28@FEEKf*ke72)4DztdL0&k&~o}8>p>cN zBjt}goGsDo&9U`P$HX4PlxNLL&Ub1ZT&0I>B=lt6T z$l&?6ckr9FJd>;cR=5AxkYxW`>;7-a?Eh-h|K+~@+xd5xTIsX?yRY> zUfom3`mKQRjL$C!2IF&zFNCyRMPHg2Kh>x?_|DNjo!J-`+kR<|_P+b4Y7>e1vUgj} z`H}Byl;bk{tM`fQKYu98IKPi9_23+9od1F6RH*818s{gFc8qg}ZVJ;?jWRLL_e#|g zJSKwINHNaaw2b}o21)`?1`XH2(7%zvmDutr(%|oR(KzG7pjj6BF;wj70z`@NV^aH0 z)P{w=54q~iQf;9Vc<|1t|BRYBEi_!d%%Nn%=U{C@TWSN%rO6kQ`&zFYi;gO5cGe8- zc96BWAiEBTz+!XN@Orv_PLcZn5%~~pDzwvr40HQFgLG~1;eHr*aGgUUYqW+JK`}Bd`OB#zIV7TL%u7JA@V&}8hmv}F8O}{TFCcr0a4_;Noqfh+K}(l zkgLAKAI@P9!mn4pm!akcm2YQz$;vlk_|5;CE#HrTIYzz@M6pMcDyGjvS-O0Suse&@ zTE=C1itykO7zvT&{2NT)M>21IAX_qFbNVAY6*}!N{vsJUEq8bfH#=dwzWda)w!Kaa8ZY@JkQ8j$st(K!%4TgbZ&)}0bJm6@l1j9&h1bmX0&)uCHCq(1+8ukzB|O>3TbGrFPo7Ou8y`n}XRR2ISfFD1DzFIQ$dPNx!2a?TnE6HbAjjLk zs6c@vsj^`PB;jZxbPsEQ<(L(1d22s86^xSV=x7vAk{}@*O?9I}bu_hnLZVwf-;Tp0 z?Lh!ii`Rl$#bT|06mVNRp_QnW+)M+D%kJa^cjGeDK1=z*_ywo{0T8tVpMQYP*>5#@Sgq;}7_(+mG5u*& z=fUc66WXR%&1C0oq33_Y?_@F*?*TEwF0Jkr)4v5+&_3=v{9arRRL2429cY#u(4`YB z!D&^Fbb z64qS0PiGR-mC$9=j0~nipUs7U0I5wEixY$!3tixrzG6!OE|BZ4f9uaH37d*)l*ive zo!tq2DZk-P$`HA=r&xRDo;t89diHXZhAw7lVnY8iq<hUvUdm`MZ!jLS;W1?pTPcU^N`@i$2fV!>vyTpC{u#tVW^y36zvLr zGapd|{=sA=k@RSB{KxDBfsHa;UnOe7zi=!FW#(4uP20<5cujHhJ`Il{r3FQ z)+5kiT2xB=*^mehL>HJv%(&jW4YKwxS)t=QUSZ)E7$jkL%%_Ru?Z zQ&2J-WfD3*C{;JbV9N2GD-rwR2kw6hwC@U@jt43slx!%H>TUQLw4_(A!EZ!M(boJai4W#apNi?X zg9$w=@CR0|iVVST7(c!~fcpFTp9vZITQObyjWN9}>O>I&$12#h@Xk-{jM)R{dVfY| zj^$&m>)AnKPzjRfBbf@Fbv;Nz)Cn&mHsAuOKpVh-kb0^u$H|fpn;GTmM$9epv>j{P zCPE{0eX$>I_PV=YU{{#^Y?2!Y^(_`{>qN^>^Tawlmspp??TpY)e z=2I|cZ?vLXYw%J2;LI2#6iOx1b{th+z0@nM^RkT*fsGv4$bpR< z*vNs69N5T#jU3p>fsGv4$bpR<*vNs69N5T#jU3p>fsGv4$bpR<*vNs69N5T#jU3p> zfsGv4$btV09B{sOP?>S04X1CyI7kz2EyeK`oDS>8(Ti})n5w`Oo((URGY4f%;qU>E z_ml?WrJv4DK9hND&G)>jI;I2%58BGVd$lZ}-uY^6s$x+m4>--_*^T|aSpQGnACo8b&c z33`%~BguhyTKjYJ#1kR7554>wWT-!Fy)Jcs37*a-O8enSO$f;0h??|6ctTI+c?z@} z#&&CgddpWOc)4xB;2b9_eEEU)t6xF;u5Z`!1XCFN3rz%O57!Z735TGwy& z*_trV)`W4=X`TQIoe9{i5>AEgHdSz<$(#cY>*LN9j|E!38WAtuyEFM~U53-`a!x0s zA`bLyEB$q+LKE2=I<9t(OiQ0x9h>(SYOJ)?051px4-fW{FLz~~kCxXw(hk+tkZ{k- z7la!DZbVXZI?gj<;gWbi{`8FxPmWLzez`W4+@0)*@mZ_v=)q03(4P+7T%2e{FuZU_*5btwr$yW#J_j!SkV(QodkjS%P zYfj6x1#dR#@Im`5uUouBKiZJcLS)KWz*K1b`FLRnR;%wbI6>x|VykOsMHrrKk4H~! zp$R{ODQGJXozNG@-F83=c{A}3AS+7+U1k@NN~6Dls+xJd6zH4m&>yV0>?)cj9EigK z!Sb6{txYASPdh0L-1;s_t)l&DqF&2!Q3lu4`0x+#o+U}s))Dv4sUUk z#Zhl@tG5_d6Nh^}#mQfS<3B#m<@jDEtvOz%Ies8A4aZ+?_T@M$g5&eqx-E41iNf() zF;J<{U(rHyTu%AnByq(KfnV-W8klANzYD)loBxYo`+8T&FYg^eVH9<77Ieo)=nLy# z-C}m)(dt`2M?jEE0n_BCfKB3{!|7vqyVdIm7xEe&!y$ivO)a6BTg*Tx(U~a!E8O?# zIOV%by)aXd%)*kgwynQ9)a29iIm!9jRLg(0k<;Kp&7 zwuD(;AD(Zp1V6}gx^KrzF;(hjSVKJ^ZRHm~hyG7Tc%gk40wLCT@e6plH}o5fPu9a6 z@4mSVS{{}5jepd2gNS*z8sR_a9s<^PyzU2Y=twq22v}W`4Dv@J0h|Z+ggYKcI_ZmV z2Trz=>t{X-uJp@`vu}I-TG%v+4x8v#j`ltQP6g)AFNuceIuCXlgwIRau`sMY2a&&nf*y`vLfw%=s+NId ze`{y)3ll${d-u!_YP3AfrU6DjHR?o8G}h@kMmT+9WaP{Chql>aw=gsjVLStP98!n^ zgO_eK3BrSHycnjl8+3Mmz(zJEYIxCtR;yY)=?qb8UA@$9ncNhZ5^)-u{&qrtm_PJM zZi}!7UNw0F#_SzEW;?*Os&^5+OPj7py%r(5hZwpf$L{%rjE~>6S*?Y}llRAt81g1rG2Y)c~9!>txHXxBr6B5%p1?mcUR9l*MG2QNGrd`aw ztJifJ__hgROunn<|2|VXKYWFN&QlC9LfNQ%aTKT~rmg}ilnWGBA*o-;;rrfr2A$zi z_&t||;@vq~7wTx+xRS}_<5KWZJX`Z_J{2H3n6`GS6%YyjzK=Ova?|8IawD2Ctm*iN z{gna%X3JvY+ZXSZ8u@zQXvo)a= zh`7n!wQRGhrs9zwy_+q+`dL*H1YRDN%$7&wSKmVL01Sk!E?PMSTF7^aS=Y+$NQ~S_ zJ<$4_KW2FY0vMs-#n-Tr(d(n2M>-m_GW2i6U>#=>R$F=BLoJ zB-sW{N)Go9LcRJL@hcrey(TsV2^7>SW^}>c(uRJegWD^(n0Pn?JuzHOuIuu@zh>I% zc|d@2Q;lEi5%G319w8Z>_z6t05La~Tg@Mo?{Ys=84v+BQq-s{vRYgmf_v zg2Nx0wk~iRNxnW-YktRYB=8*Jvs`5nk%5vo8v9nXoQHC-10tmnf<>8j8uUb7>)#bC&)dWZ&*6?x+yT+sNxM6I@pI?JDydJ4uQTg7^x=o0&&sb$8uwuxzc6D z3<81FaL5DkT};X%JD-4V(#SqVK;s=`qrqxqYxEisdTGtw#z+qw;>e53abv<%bc=fW z`UF1kA1Y174-jnXc@+tvH!+p`thg7V(HHWD!nlkx#BwGIkd9yHNEN>g%FF}5ylC2K z?8d1nCNHxlGo_~3k6I5jpxJbZ9rkrU1+l!>0VZxRi9HF3y$dy*{YPHt~+H_*1So9BE(qTQ*L#U@kZELD>wR(_SGT>!HbLoN- zylVOg*4`beE+kc-NJ!3@Fmvf3J)VL~_n8z(9N5eqgd?C!VK64`ZEO^W`<^=jtmK=~ zgNblNf+z-qQFC8fMC}Gwr?J*ZJ|-Ik8QVAR_tyNmiXOq*&XXuapCTMM1A-r}cnE)H zbh`HbN3{395=&P;TIPSGzh&&7Cu{=y)F)XE5!eU$sn8#g)2={}`hn)Ket7;(Z5wOq z7L-&xbPOj$jN1<5qyrGE1tRleqw8YlTjkl5a3^WFI}`2>fGc%V-O+2Meza}+5*h<- zG!A_S9eR!0x749?bq}N1!#^>R^zT8>!u2j0xH04nbOrWsy~Q)g#f&es5(9>Ll-eHw zGBGPt@oJhRT!N&&Ww4ra4X03ODzj2}JoMl@Ax+0G=b+1l4HyN=m~&?aWUzGx5XM^@ac&8I{r4aD^{H3HIx%J_(@3EEClLv>ayxK;}yh`c%zQ)pBh zLxLk^=v2RNuZ`W&Q89M6;-}+k)P}BIg0$Ka%N-cMTHNE*N%lkJN|#WZBI@7Zykx1H z-y@|k8?+q}iXug@-z|N>bF2)v6POyo>ZsJ{auxledF`F2ycN-HI&=RRaM<9l2=A)S-d7+RXiswSt5o>k^yQ8eF-!4C5G_iyx@) zcnu&CsU#8zu*V}2NX4pght-&K_sCVsFmR0Qw|#O5nDiKfvPAGa^hpYvv*X`D_GMyEL(#KiBYq{%*Grc=?z%TnI69E49Aq_a}>@?Sb;acr14=zM#$z3eaX^q?^%2#q?R|0ri_wn99?aZaYrI>m>YioW)UTE1!zAT1X$r4t3a|k$IFM z{kW=O$ZtY-!_KWEzndT`!_aOv+fC47ISkcCdfL~?8gkuYEDMQ?Mp?J|Oc*XUXaYW4 zF-lf2&)uG~I$5_(%$7yXw(@_m?=@JFg+u06xHE9i(C?p0gNT7^NxzGbL54eCT>o`e zt1<9tQ)DRORMJkRron;1;In7Sa}m{#(JA>u^_v5jCgvQa14lPB`ZX)>rYpCw@|n8w zOmt~LahAe}^IB%DxUWPVZYHw0!%csao~BJ2%&D4+O4Nd|dW>O=9(=N`rLznk)dVuN zs^l%RbSAP9R`J*L7|IPtGHI+;=fg&mxAm5VHy;BG!=e&op=YuP{<+@|P4_Im0?*?r zI2a(GjId68|2i;3E)K-Jf;xGE(Y^*=C!}r$&SIQ)j?4QuUE50wQT`{DLCt3 z)cFp7{g8X6l&7q!2&KB!N`SZgy9e#G- zk<=P?q7*h`0HvWaZDuGS0sG+0{`AaHYw+6#iw18&t{tVh_7SqwCtNLnYBo}I8q}m$ z_{fzK!^H1k*5!baw(?aHPhtcC22R~m@nuv@Zq;))G$SqlQTheB4)jefi2YOVTgK81 zXS&qX_{PJ)F+#Q?UfP8W4*mQf>y=1LmKBLoCaG(_y=n^Q3fg}^NF zVo~**orPKN?5~-1;P$-b57P-S@ap??v{b9UqDi3No{AoMUDM9vlT}SEJ(7|z3Hteb)7F+x||^9ugF5gB3E;# zG3yHH-!*JEvHxdu|9@luN1}o@nq9%??kz(JVbj1I;?c(6c=)Ih-`DAwB)6g`klew} zU(}ty&(1#)^#sX`r>CM|=FC<2t2;As6&#_&Rr|bR+4C*6E$6eNM%FRx9pqG+z4}Z0s#AzBE7R>RQ@U)&6BpRIXq zDVPGU0SaV6Z?cyo7h(<}rYtTe6>8N=L~O}JX`LYC4Cfva>%^3LPGYdImXkYB5e^qO zAWE+oPaQBj1B)KQsTjuK8R*iHM}SK|q+x6Ey_^sJKAryJ#`94G@fv&U^gm}f`JNe1 z8sgK9Ijxb+OIvSWj{Ez%-gwpIC$cjG)CO3}*;qLGf9!n;d{ou-{{)B(OH5RPQCvoi8uuuu zK}3fNIw)$epiyg$6*X3Cqa+#^Fqo*sVU)J0xKydt)}<=$kg5%c5UZ`I)wXIaUDO+g zx-W=m{@?F8_q};<=FMah;rH+F|0kah%zO9Ud+xdCo^$Rw=WcJ`3ngKPpVto)_0^v= z7{sAHQB+JCj1;bFEvEy(fr}Hyu*+FpfdrNL)a^vTR|87>P%R!}@lPdS->TCO&9gz= z{;g6t2K$;izHJs0EyU1Fm^_+U1MLNkBb-k&AO+q}oWd0yy(c0OUAJR$!ucL=CbAjz zFcFlQf*&_)%Oa+RW=>9(rA-IZewvfwMEr}<#nW&<8$e7$78gXUkRWLEqDZH?6^1T~ zPjQNC>>GkIMP@{_@lKl@_B|VRW!IZxP&uSx#5|)3OmqNS1Gf<&04) zBC0L})N1O!=z0qlk(X8ruYA%Md1c{Ua1y_i`JLaQXN@?IP>KWWIt+cd*G;kdT( z*9ZYSSjbhph0v0yb)G;{%tR0E2YN7()`PVHUZEK@N*U>0Je&J~3^<=}NNe6R9Yb=^$wCK!jJ)gjVgyHFzWVPpWswBxYV}HP8b~IolmL5^0Qb`8v|SxWCF{sDKQ42PNe&NBa>5@!j~!6)IRoh@r_mh|$bJRmU!?WYjJJwMERUdCDnH ztafU#fZ{97SPbs=+x)bz@as&Oz~|uiF&**Ij7ra1wh#pJxhV`4$e%_CX02W&BwQzV zM_B!bHhjobFarfotnozYuD^2Z(*!NUma&77fK3WYfo4#k>r7D*g&*DJ#Da)#iaG0m z6S#!1onjMNY#%ActZt~NnvuE$?ZaJ>0}5e03ZbhNo+lPiV?IN}=gOCOv>Fi#f=@-( zcRmoE{1wY4iYDUOJWB`D!f9fJL5yn;mP?U=UhAh$g%qITYv0DJosz>-C|LqVX~qM> z$NB)-;5(*q+wo(+I?i~LvtS@KAvJ;nCMFc-2(z1uAYR!EQIAvi4H1=tg$B0N+hAF> zNDgz;MsRMx;{^@`1vSssZk{KhL+u1h#9Rpqc1?%B(N)2q6^X+(r&LA70U%;nxV>mR zo=vk|>>*^+`2;YqFb$BL{k%hBXClt?@f`P~tF)>CjTHYI8VM5Li68z6iTNTV<^xE~ zACUNA{D4|~3n1|#CIgX0U^pc)Q_Qdzg~VA3t+a*i7C{mcOQ|aWM9DXX|5#!!?Yu82`yA}`Put0f? zhh1w29hQIeb&{8oUPEW#SVq2QEn5Zjy^`*=a#~hL>93R^i)-RA)C{?*veJFz9V*=w z=0UKS$WwK!&apC zh8&@QY%xNZEP4U&BEjtvHZ2MYq)$pZ%68-LMha}Cz(xveq`*cBY^13~2yx6Bt7334 z=hIga@1*K?a1^-aV1XpbiY^6g{siC2`Y@Glz~mncqlW(?K1|c8-+!i0J(JeHpt~qz z@H=YRwwO+Ea>>yR@Itf^`_R>>(-!QC@CRDJSv(|Sj-2z_Whva(J>~dcjR*&uNjl-` zpOEgM6}|Hh?W(S7Q_5IPqIBzdj=R>p)KawCY_&OOTrp>WfcJKjfO8FI>zWabZ(k@K zQN(96UgnFWqmE)AN!-ZoI3;o=C+^eS$R1je`rOC%OqKXw>5)Hk+OyE{GONvQLG4+{ zwYauDd^Qch+OpK8uQ=^_i|yge%KvnGu9qlH=_NmP+Ow*|?VrV)1@zCVBUA0+v$<2| zgQS1%lleqO8oo~vd0XOsem6hW0X;mq>pe>-^aH>Uo=^nhNSNB|ZXC2?Ksg!^N)E&W ze!)%y%0a>sZ)8I&^7nTzQ2O+gor?IN(cu|GHAon08&IzIQB}nG9M4vRzd;s%fGokK zpa|x4`*HH%BOiKxmvx~ntJ8xML>S=`hA0Z!zv|{eI8Yf%mNHH6TCC|nru?NIrJ!4{dr6?K^vEQ2X{u70%YaejC1hke{geJp?ALeUkpLP4A(7 zZA;SHCxshRg|oG9#|PI(|D~3F3e$t7%IwDQ0CCjy&K{PQwdpMTfb(f$c;g^8jcW7x1f zgSs)D<8(jJ>!ISj>wEVBr-6y07r(RN>$}KP-;ZH^^|^gk|K}j()-EYCsnruduq(cJ z+96Hv%aet`J{?auAXY>u(JN+i>7xQ9D zV$KzLmR8H7;m~-FrBN<)fvduvc@@iBdPT0({-zxK^4EJDsEMLC$9PyXv-?wmZJ&DT z4`Thd99X;M!8X3h7B)CauSX8nVWXqEP~xd4>Ez%5hbB1|>3Jf|yk_Zgnyk#&3+uzJ zKL>p-SdpU7(7w?JSe0NZ`TGaC)a|3bCOsp+1wL8&CZG1Kf_?aH$}h;Ld>;MDdrTXK zXy@%G{zglWQ(^hE@4jd9XkSnGk)y7HU|=~`E~H$1dV=Tj?;NP6xw;82g4L z!$!lOs-W;EilTTnQ#yOBnxEPpB$%~84f zmua4!P}Dv4Ulw!9ZZ)H#c!!EBi=wcU$c?`T*3E#0^+ma%^~8@Jco)K-#4gpm;pJk5 z5Z1UqG7D?md(R1O`zr&-AM`;K+v;@WdHE5LQ--DZ#Bgk<0t)e9j!+VN2=j@u=L< zZhcwwt;l;UY-ZdYEPDBtzYW3YAmPZj{8d;j6kOh`U$ZpAwKv~?2vGLm_JnM5>K`9^ zg15(S0B@)D2k$5-k=T%2@OsvlWX(!*;^VSRvZ`fO0Rqv*?Q~l7%k{rN9nb?k`<_&( z`PO&aW7+E4d4tqf@T+X~J+@#&wC`#7pdgw{PptX0^qjCk>RXrAK3R4sJ^SkI8=`%! zu&ILEcg6;(@6TE5`|P&fv@f^*Fx%eGm7ElaEhws)Qg6!#o=XsmJLeMU(pH_Wvh!&+ zu5>Wkp_4s@T~lS!feQm?{G4sDIrTuS8uRlz#h-J_SL!HyA||nPq4NUrRLc@Ba>PpJ zi6S>{?lLSV z;D-9?UHk(Mq1~tWr_01G`V2qqXR^_WqRHp<$Nnd_2h^)9ejFa!Y>f(I?3W;6SmGtr z-;#w}WIx6@tlf)DVKa?%%;f6qt2$->TD%;GJ zFVe$P6RIfzF$NRwe>5*wSjdxyJ)#Fn6S|G6okj(brA_Mt|FszFdk7=AUE3G@&;Rv? z#lH%jx<%Z&cK?uP6T*GGR2x<7w18Y&!@gB%W zHrPG4KcyKeb7}_V0~a4OE?}z)fK`ut&fG{14;!J{yf)uiICV4$U9C3d8rqI6tNJlA zu(_nWM=n&+m1DPQW-6MOY9$IT|T- z99YaeP5DJKR*muK4z8Q~$S#>1w}`PVn$q>OHCc(d9~Nfv7ROvy0zN^jvt`vf5YTR0 zj_)W2#FO}GKSy8`eTZjs8Ob4ww@xE!^8Rlg!vU9DWT zmRz-qo${_`6&QyI?m=4~GtM&f!K1V()z~D55Ida*X1vOu4A+aP^=@jL&QYTj&W@n# z$xU!_9RM}T6ww`hm2V&O;w5i-&Y<9==y@JrftZ(`C)q5Po=4d%UV8o~B2s$%4k>!x zi)Zs1RXFvdhn_=h`4m0B6snUNK6?JspKR&*f}5I~o<*#t&-yZxp1Ie{aa80qq_Woy zXgh3^2T6|0l)Z+W03`ec zSGSc0Z?YX4Y!6So?PC~Yuh}4dlQ~;TNvkUr>Rw>I`ZKklgaOz6uhZAi+%MV9Nz94?6 zv>7cw>>wF^{IF>%$@0S)O!*(@hrZhTH*hq|665ePaHhW+p$=mB-lKym#hpl5>VN-f zHt4Rs0znP3&s`I|k=g=%3g+a0cA5^V1Ua0;F&$eJc{)uQctrimEj-5e{fLIA9f7&k zW*E?F#f%R+k=on(%x4$>O5&e>ZKSMEqE#*9fttdO9o#^DuTBF1qR@ z{Ip-8gUM}pHd{+L!fnVc2+{j#{2^ydvNz*EPcu$u;l9$EMECi-!8|0U1kU);cJ4OE zd%l2j2SO=Lyt^Sru|v{I<&Se@HX=78D1AqEIMzq%yiyl4@h*7=1vwlCa;F4+j-Hf* z!!;HlnOra_F-Hz}nUrWPH&R3opi$@e=DDC2zBKa-XF{wmNehPCuk5`llhBpwmQK(~ z8f2|87s4Zu?N_U$w45HH2$-qjpmGN1YKVsl{j02?#vBiSUfs##O3?QgGh{Gp8C$bn z+(|tj*FBWm8P@;5t?`@&)twmD1Tn-C^$+q|x;C+9~3vW0j94fGww-w;>;zZHM zr9irS38b^T^h_qT1o_P=d#*S1zin$D{c#HTa3BDGqru-5)i{@mT``vw3h0X2NcVKb z^VDvcU9l*{u0UULGo)}pqV!I-4qb9$-06~o&`D;HOm?TbWT)25E^$aHzpeyj^btC? z8C`}$!5402TTA(|$>w%+wydy%6ydi#!n}|$SP0yl!1WrR+)I(G8u{|#fp{}bh<+_X zgL%|XL45pxs+~h93!)81`C8f%a%Olf9#E@lEzT;|&JH`p`mtEcDr}LGLetzr)6xq~ zn;nCn%b?$2(&Zx=J+V`he7b21h|-dFaI~dLYJZ8VkF)LpwfweKH`o}Up(-NWE?H}L ze3q6B(>{8vjrCnt-V|!s-j;d$n=oXO>M2pWug!LCDx1tsit38WrRa}t0Z3|K(*nR# zC5KSBWVFo^g@Xx#BPineeCD`>u7aww^%Vj~y+b&;wN3f~Pyh#Ymrlk*cj?J|%}{^3 zABd!&BK+rg>jV~L70xA7PHjPrz zQuG`}i@vJ1p>vMT&n9q6)tY_{KTs2~#ypAyn8jl#!4#o%%O0m5vs?!vpgPRs*h#Sj z0N2!_Kyu4*i?H|Bx#g3Y`!N82jSAnkP*bLXvBj3YMdbYLi?DrA8s7Fn5;%a za&LjdZGq$;AUvsihaj+~8vdXRd(rH;W+0CQ)!(TyDgEW89I7iJ&hE}XzC@f|T=FTk zKaSrJQ;d~S#gwE#2UYKjk7m>oP}Zka(RGNF zQVZtg2H5e9s4XW~@HeUu0-NTJhp0T0)l_UM%^??qqX7$(szFADhf=xW38~FHVpesU z7d{7KJpjxH-X+Zig)?@es`<40Cudh|AO* z2o*NZuE9FBd^*I}|A6i@s#z<*NzaGkA*}eS=9~iBCTZ^H5;9h$%Ew!u&=}iy}2^~!GLB5N49iUA*YZa zZQs)W5`UtSjrYcq5{0Aa9|`#rrK^rdpF_V#%=5EA@xq~lHwnpwUgvah6WKkJ>BbZc z4dnkoe+7l+0H!$*U5w$_2ICnQ!(_*C_-Q{GmMwD4WT@G&Ey**Ii`k!VZ-4)P9Pm_To8=A~}{xyzqK|*8OZ& zA3B94NY&#IfLin>e60B5OF=Bkw%{PhqA#Hx;sa(m<#2IaDe~ebV>Dx5ry1qRl}scD z#0y6t74(SAa8F(S8uHgZT{~$920Hdf#FfNIX9AzTt0kW?_~J3o>fvA=I~09OcOJ1- zAXq;IyfI>?mp5KTec+AZsH**9t@b;t_UWHc)sNi^CAd=aD7KMT$B-{KJC+9ifC7S* z8|#{QTVCbJ4*u+?Kcn>L0IDA6h?@HD(zie&HQ)xYmPZ&z0==r~J*{k%(WN$EaEG=5 zzt38Y0T#vAEw1)9weajAww0~rmD+>{hXDAa82!-65&DQYXnCCadWq6I@DZ&zY$)=) zaUSY~1XgvI9`zrf1|OLrB|w4avKvw+CG=jkSYn=ClM7u`fe*;pfn5A!sZM*Gepa}mJVUqxndE?5J|yn|E7af+ zW4@$Ls-BG&weO)#zmrW5fv$^WXTv!iZ0o5n8>3F~np)#pDuVa^ZF0wz2y$*UZ5y z3U}cHaBmsAJ)R1RuYIH+fv!kW5zdo{qiFl5q5438O^cssEnkKFZH03t3C(%xGXFsG zHTb~@a5*`xwR|1kl3Or8pU&)d6P=_nh4&BGBvhOr4>@U;p`(Cp4`2o)_I~jba|BLz z={Ed^o?)uYXKMzeu>R4p@C+C$HZ2-PaVER)abK4R%RDI$Yo~44EGK(QQ`kefhQXkm zy&Y;kp3T8@F)ZZR>6l<^SrcvCl)P33RAlVi*ZGb)Qn}DC);32rsTDsC7HXBp*ek1W z1})i+#he0ck-T%ogo`~ANng4Cv{fG6kNTx8Hni&|gon>ujQUFpjtYmsi!1^aVULX; zz;+zYs<6M6?P!3N?SxsF_{uTVk_9(?@uc=(r$j$v=RAjxrH|ud+knBc7>T{g-5uJi ze6qdDM^c!-V+#M=Zq9%yYw{O=b*2Q>zhZX&E~K z9|~rkhK?Evc>I%-s`(;<%!hbk1i3&wzRcJ^cDg=Ueq1<_HB2f9^XfC^Ks}>S0U9)x z^_bakgny%Yw47?}90}IVQS}E^dSyL$XVx;ZdayRlFyA&NY@|R=3Z(pxeBwcmT`ERK zDjswv67q-#ErbMR#)Hm>IU5iU`j}#1<3T^Cs+ooa*h$g8@Vvi$5n34-^}GfZEMZtV z-6a{ppv^CnY9l$T?OiKQyaf)h1^n@#FR44C zS0iKZr^hqmL0)=+E+xd&jRFNw!e_4{_xkqF9e*VqG|-9wzewA+^yfTR)Ae{M(0ud` zCsBEi7>zvM+EWWdhofl+-Z9M^)=A(!AQrInimyBvpBGo)n<@D*k}!fbOGbPDX2bRz zsydj2LAn{gX80AFZvkEq%g1oTC;qXT1^k6M-bALAfPchWyNw7St<%f_>v+DE;F}`% z2{18FP^M_;6ANm$)OL_=4Tl7_X$%(LAym%Rwsb^^WChL&8V8ee zWeVa$XW@zI35u(EDq@!gd&e_-+R{l$OD;+QnLK-XeEtfii3 zDeJ^^v(y-;lq>;6XT{k5n0XC;V(PNj8uN!H?wcWYu@%t>7yxvTrgfU{PD4!;uWKb$ zB8~*{;P-8N$p8B_NTyWt+la|1#s7L@@(@K$U_vgH#q2NMXA?BMIsW334tw&2x^m`+ z7osFG-HmT5qQIfh(-qQ%Tfpq1#3m5Tpl za2EC~@!q}SRA=m^o2&0iMk}G$l^+6djJD@HE{L z=d#2-T4Fv+Jc<&E4*KLK_Lry(Tr(&=c=W0KEDPx^jqaz$K%XuW97QvQHSff;A*eNI zNOEsxU{@;s23rQUv2W42;`FIA;mV$2i+y<5?HE@s_7WUUkus4lY4k<-~V zg5yhd_@-Fz$~?9!tcHDg6SSDOp@K$l;n^%@-)MFy8gJ8Hp5asyhxy?QNRX&*R0;I8 zHP5g$UYg#`8alOx7g$5B?R^*7rZZ3^eb7TUQmeG11hmR41oC?Y@*;tZut1boyN>;u z)ZoPYUm19)VK2;Dwg3f!#HA1NLhcfIfmBeW5chG!0}nNl0w!dyn*GlA%1NEs4RRxL zQh|OGN;nJxp{Qv1E~y6<&ke=e=pt?O;cWC9tSeD;BpbbpFc-U-1y!12rkNI~fUuHR zk(|b^uP|qdZ7QQQZh%6h-Q4;;&8rX^wpMOxke-HywGA*F#L@Q{Wo^o9ND+RjGqcVF zU;dJO9QE_@_bxyw@G+VqRcc!vDwGb~8*7Q$-`BRhg(P#5) z358_iJ~CoaC7Bs*MP_C6)1W6UVuox47_m@;FuJ9ZW%M>w+Wv(ia6J+Dt0fMyr!~I| z2DDb3)BI#m$s%9@$ zqo*$-_DHS9ATMKvw58Phg%z z(Xa7rMu-(kh2P0>`Yf1%;BK;Skiz%+tJ!8vVP4jni!j7{`T0B|g3hSbUcT50J#M%} zS_RYTNSt=#12UbT87+hx`E&{jM`R+k}~tooO)gux3);WL%O7q10{7qb>LlkSl1mP=TW++ghz%lQ=qY6I zb;L)s_aJXaNV@jw-)vfX-e)!4(Sij;)Z+~f5i{qVg3Ow)(hKBOS|sQ`+hEE@`y3_xB}D0XqNL@(c(!s> zTF=(kv-&!It_wA6<({cnXSSdXWX)yO?9G75Kh+Ui4uFn09u5E$;#{N#C`51nX5C~A zjp`!4WNCeB!G;H)>pf(($) zzh(k(;Mv2wP$sjk{bg|NJ*TUl@|mcxnfPhn6F8vKEAVVShee8BwS}$%d)YU8Xho>T zlTyNNfByugB#Mq8&U=FgV7KSqdXY13zVYKm3T&jnMhg7DP67XT5~UL7z~Fk2{`KP5 z!?5+O7jH#E9`l29VM*(t?_Do`3tXgt`N5ZAa60pYi2{s>-?ZB7{o}D?S7vu z3kqrcL^uM$p&fu&KK`{V%UmMtrymLg+3%dAI3WhRWf9imgxvM_RpvT63M~P{s>^oc zVM>(wCgFky+62#HgUb;yTO$h z7(GP`Ys@xo3%1uLAA78~1*bYilFyS@;G(QiQ*IfeW+o0>H`cpWcu5a{(t~h%!i1j5 zeg5-EHbnl6`H5Ucr{}HH`;2u~nkNp2m_i_1?0L}@)PeHl3~)yNJeaROL0z>_9kR~7 z@S-1Z-EAejk^A=r8m+Uoc@e9?`Y-8T#eiXrYRd(P9(Dav&~npWfqZmb=TH0QXbgul zJp~71BuV}V+Zvkh#%@wn${l~_+UJNYnY{~(XAxk#9Eo;!->?H^0V-q7EBr|+O}Y(Q<<&f>_vjUOi#H*Odl*}XWGO^ui5dAK)z2c0Xk zCY9Vr^XKET$Xzx{2kRPdN5=sU+>x5A6bS54gHgWF+5T>cO*@MlnENO zKJYh?to-q}_FooVLNP{kVXJJ0S^&O*kVxUEhXxPQaL7UfZ{PM6{-PuEgYWA9U@j4? z5k*!tgD#A!k$I@FHNt~2-Gx^ZmFlu?gL0eX)>lieWZ22$YSfcJ^*AN*Y7UNKVb0<7 zgUoJ}n?FrJDln?ou<5^Gi4m_D(_%P#L@tgPDLK;jts)OD(E}_elDV&fUwaI|qr((H z>U_z1HyZjEgul8j!>TaBO|+>oh&8F1cRzzwcPew#Iy%a2Y#$z6`c#uX!~FCC&0uVU zK4%dDq0hOPU~uU3=y;`%o$`1FZL{VqEz_1&{TX;Zl#Q)TJMpI6nz7(evMB~lX6=_8 zCYjyCZ%#nAAQ1L1R4q5XrW?|ECD*)MG4$-`5~wV(i&l091G{%`IVc&0_`)1 zXQJo^vcp;;B#p87=ZzfbYVkwyk`=P{C`~B)s!uNEpY(6aF&*|I12c07D#EYbCzCp+c^Va-QBMsLp6-du{U!#KO7)07?rsXf^FRsxac!6&XD-9*qU z74%aC{SdV}X&sBlO3ajrL3EZ~$zCI_$MuJtnBtTU51-+x5>Brzln#eEU>-b}wwJT$ z8ADWsDM8F9mf)H}ISGc7P`IWRpPI~f(PW+pgJVy)W=Yak9EI$dBqI~?m?FYv9t`2O zA|&#o`2aE_D~Kx0OB7F;XOFYxo6~*>Tr%VKZdSwV2lUHeN zc8$rT0|`!?FJ*tor}uuSN;bl~Q5iKy&D{M-3@q>S9L#&`2O^$N;ivsR<&iJ(Y!YJ1 z2z5DM$9X@X3=;rNZ!FPg0o4`@C$ZW}tXQ3o$BH1m-XrFnS_EBLv&@Eb&F-Z@MrGg- zT`2z++*hoF)|U&3s*DOOWlqPp;>TAKM8s?&szQK`D@oRRaKZMy#%ig@90hM)!5^v+ zFZY7q(t@vW`sd-v4r%0kU}-mGqj)5eTY|Nv$`vr; zP;)3b7^zjVr@4xyxcXuGFs8{iG?1=h`S^mOQ1UNQJRThiPzR%QR7x{vR7!KR_QCRW zFrFfS6PX&q6?yROrPADyxn3AJZ#*uo&d-zpiX6|VL0m;56~Q}DDxA{-_={G$vhJ-4}KTMfHJIu zGL!{mBgQQ&tPxpNF6J?AcDe9qaqBhc1N$Kg{nD|Gx-w(H z>XM`J+*GmS;qEAF@8lFgz2Mfe&=1zksA zP5^7VfH4X%eFXGWatEi3VB!ul4?r09>*(47Wx7L_zm*3Pntqf!qGmzgPy_UokPjg1U67+~2HhJ1*yN~{_$e{xVVG9Z zit_~{K3YS;MWbfnCpp8Zk=;?|J8Y@bY{_tAhbmN2MHg-7&2G8v4{)5diUIp|U`>0= z*lpVjWiv3=%B1?b3^qr1X@7iDp1B)5v#=moP1@0EBFtlJa0GBOlL>_~P|4tH%^r~NmOLk_Y_kLK zg?I;8ny3V-!N%|L0tH}g!kX?9DwZmuMqd9cBN*g`D!;{4g&-Pr4PttGmCUZ8VEC|d_Ggg!n0utl&fwB zGQ(|f4>YpY{=e9*$OA&zt(3!{>#3SxZHj@7Ip|5SPLKjkjRA3`>aCp_fk8p(Sj54W zeVagCHhN_5WYVL?rAM+tk%^EV5v9lZyK5R~#GM|Mqg{Ntuz7Fjbbi2@P)lr^D$T7J zO4z0fGHV6eln*ZiF-x!b)osP1l-cgK;=WW*6YU?uwX;;`JbE1wPH)9)`+VzGc=#FN zF1V%LexXQfUynESz)r3g2QWdon@qbDe(nM=sfG<##_*xWoCX5Sb-uvF7BjlZO@MEU zGdJAVXjn|@rj$ud9u}be_ zJX(a0MCJ|n3%prze;B}9(=Atb-0lLpn*uffj-)D(lMNKe$8?g`w6Nn^T zhqCO87f{TzKRc^tr}jvNGAq67SXV^p{f1O!+qiDkD7F=aRzw*;*N>cjtD0@sj??hg zU<8k!MKFT7mq#NZ8Bm8BE%N@+sL}FJy>tJ0T$appMBo z2S@;dn|?0rmyE!p`t3V1lCH*2dnExUiax}%84vXgiICDkj|mZ+`O*ja z(BEl* z8l{>*zE@P86c!&Z;;zL$U#O{h`$(gp@kc@v7~R$_-V_A$geIyzT*3ufKZYYJRH%+6 znt-ccqad+pFLb4)1PB1-f#G|AqwIs}k^i zJe%)0)Qut-B=HU5ff6d|h*<}HGFSNo-1nh|Ns+-uaqY6DBjNbY1)X}HNVQGr8Gq0E z`$W;vK-A^*mL~Y8auKe^EE@K)_+<-pRF~KmtcXS((F7Z%b@cGV9fzNNU=agg!1|@TKwhmYXGOM|`PSEOZ-0Hw zAR!e%rP;q%_4(sYJ_8~@&)C*McR@2v5J$YdmU#3*1q4`v#`tuk?q z(`ThlUh|i69NT;U%{(3u0;_gqcAEEs9u08g{PcM6ElZCFX>8@C)qBS;^R}{)yhg-( zT`u}J43Lo>$-;cJL?-_Sl3$iDrD~XZXg0y^8@#9J;|3~%yy!vS-WvaxLXT-|^LRF& z>1EUcr@QHLVvn2uSD>KlRi8=r@|K$I{Tjnpth=<|MS&GLP#d>)(xtY~_Hz0~*KNG@ zhKw5@;HUjiYSu(i|2Hvi6vGVAf}BqPhjT7rrRst&cgaQ*{|MH*w>;@t3A)uXaskD} zSsGCASCr|%KJsGV%1a;{v8|PqzgKU7`~?AX%YzVE#`}15SR2N6fQsNz@B56jjT;uv zXTZ-3ewnTsMpsH5cR9u*B_opt+xP;1BNw}gKb7{wbMk%opA97 zBSsMsTPMsXuoH|OJb<3r^}5qXIFENlhbK&uFFgjdtQpW33iyWZK%PJos~ny`U_?(m z=HeR&OZW@)ijrPC?gU_f4$8tpg>>=3ot}Ze55KLYC!gKZf1VgbNmhR&t$Wkv>$4#3 z1f3}v&-^TlWM~iO*M9JemD;6cxhWbj{(@EkD-RKdf-c}yJ`67C0*)&4)8R_vS`b}g z)m20g@hW>bJkHGTWFJ76un%}mHN2rJvvhNm!(a&uO(Na=Si({<^9YSXiFMciiKaDt zW^_|^I$<4#?%_A3?{ZAwb8_7A@|xf^ia%eECIO|DM7YA#izfqQ>*27KK?-GYTN7S} zLN&;=a})8ek53$CmqlPrfYna*&`xY9U=J18O}boQR8pqrm=UxX#VnbHkTMj2NAZaD zD?3tWWUYM_g9*(S3?`et21+euP*y?kwDwWOt#a0%O>sa7k~p940;g&hcz1`2VZC`- zhMuv-w`oLDd{dkP9>Tk=Km(eiya7$#2#dR369=P(4dqQXRJj~%Q=lXqg>XF;RaJ5r zQbz-C5_UVAiYd`G+U<+EM6_Chc-#OMuqdF1K3|`oQ~Z|cd3++#+*G>>kP4hy>WXc-rLy3{b`D-_Wwg8WtZP%w zj_1^mzq|s;xI-o5Q#_ly*>WwijTR|~e^l;SggT2weyv5GW06KBR>pwp>fb>8Y$Z=5 zJb0xR=rysD2epz0R#K8#Ne}R$sXXzhIL@UUhfbbL28)92N(2F}BS0XcyE-zLIyNF- zIm0guH*!l9Xj#+0xOIu7Xh|&2>4>fUF?y-v6 zF@ns|Nr?$iep6BC=&1Q6x-Zr;0f&yoCq(fTY>$5!oz?ByUiTkV;HUjf;+H7elbUco z+%}ZQSu5MnQ^vrPI2tvS{tjfF6rWIG$QJPx$P$Z<{DXOab7W7PQFH1kne**E&tJ)q z2b3@!$PmvgfKJNN`%3fsVxsReK|S_z&hv`xJrtpoJ+PFmyikLZe`G5kh7+oT3OJ3Y zjdik*8b-u0L)bxTfNij(mZ@aPzayLj^-_)35V)wi-;9&JIhE`80M`TBG-Q~@n=H5j zahjg>(?K{bOCq}UhVgH#>78aC!GyPJgy)gBS|&=Xlee*kP;u)&SSW=LdKLDTo$!B= zrfce(Jy_{7y5v|M%yk276n{evlVjiyIUh4NeS*MlI7F)J1h&J`YzDPcy_R%aQpzms5YP zkSDMNULsua3B|sB>}+C%6uQ??82)S`Fd1-vBwlwMm)(T)6PT5L-bl)W!ZV ztnGYBE#hBi+ggpPB`x!>!?5Q7leTZZsf#RnPr+9?KmcCdmr64?%z}ze>cTD!3`gY3 z8}=5_WRHSXBhxSQby_Vu4brJAR(^+Wk>2LoDfoCo4|@(omARx`zaqa6`z25wM4|#T z0rvKr2$!r?=rsQrhRt?$(EavnrrYPyk6GtW=<CcBEbR)uD4NwEAzS(R_^ z!Fo!T3YX5@R1>MBS7a(?>r}Y%RT*TT>P#=~bMq{RCeSMit~b|hq7(s%#a?2Jd>v`% zu{_qN*Rm1TMhg6QP{7Iy_I62+}*KcY5RPe_m|AYx`l6!rTSO2pB^-r$zR6Ovr z=$Dk1m@||jE*iyW!+9wmv|gj3KjBlb(Y zvf%^Glx6x`2bF;XoVEfrm`OScR+5}iyp<>&@D{^cnWc&zl-nR`pJZAQ<%*$@esxWp z+ywLzGvL|P$5|RcZGKb8!97vb`UiJF$3T#^+uWM+fqwwJ2yIiM=-j@-2Pp&M|1eEb z>o>F~&fw^3xgbm~cph#ajGnO-*Ju!GSNH2U*`+-xUU=b+g`j%rS@MIR)HiK~pwEi~-n4H#m$nB?(gdnI&eF$3`gJm;${bh( zo}-R#%VVyKRP~ORSq4D*1y#*LzfKdPWt)RB@_}~_;68RdI& zcR_}FQWID7mDiC}wH#b)M1{gwhs)bAP^K%Ah5V68RmxV%nM|==)~9UvRTICA<2+n< z94d3D`dC~|e8fuecVc}L^unLfsML<*Bf57E>vfm-v51Up+Y zy{N3{gugIj-LoNB3q&_AKqzum>OU~ zEHn{k{9KAKoK}TfG+O1v1MEpV2s%KgX{*iCj3BTRx=W`v=z2Ev#~O_iZt*)1XD}_X zIA>97Q!Z3?;m_lXufMV?g(_z@9r$#b_Af2Bz@?F>Fi1p|@Ft=?WR-FU=kBM0^YNS- z5}flIAMH$#Ke>wHn=S#Eh%Q)+>az%dHqJt#iw> z1Jj28n)?ANrxNH1&^FutYuFMwMql4}?)AU3t#?No=v|_NI!zbahA^-dvc_}FmrU64^Ag9QJNsdGH>q@sGN-fm#ZhrzW*W zZ5B+tx$Fz5F9yWzf|UlS51Dsq+zQsr6zEInA+raJT~0_%>AD*fbV}K{^f6G~j)Du# zxDX)6Yj_%v)8=)?n>R32>AcQS4j9X&uZuwqW#@!>iI`rvF@=Y~begp=fq~RiyD;Pi zyb`IY$>GTsq#tjQooUmtlG7@+W1fP7*q_sJ>s6_F`qx8$kGzcRaMWjh@(Ky*H1ED| z4Snz3N0UF!EwL{A;I3ykYZ&0%FQl`|ky4IMn}VTYN~qy@{uDF}^nf)w(q3VIy+PI&Vsq<^ksxC7!3*ODs(`kpQ$HG;#hcx!Un~ykGmWN zT|q$v0WzcvSnp)l;EwdZ+rlWD~2WhL0CA-`Qk++p&I{XmO-%f|u$KtU{vmD}+ zl;t2EAbS%?RDld8kTV4cm(u(e$7`8~b$(!d4XPgg>*>+u4|Gtb6=}Lx=Vq@LV2y+s ze?;&2me+#x>*c;(tS=21TM|W2y*w~vehutYCyh6cgYFp02BWWo7PHPSyapwTA1`dd z5Aqe7?-YkZ7<6$VBtabkLpMlyr`g-G=&z1#-5TuU+Is3k#o~o;&qiQu0?FHDTL7Z|d zFQEO5Yu~R`olqCFH%|1C(`l7#i08N`0N^1$QJ`XXVQdF!z>*OjLyQF^-{DXH2I_{$ zfQB=Zmm*5Ies0WTA+ zwr(-6c(ZH~pHp5bsf90IWx1lY9E(;$_JhH8Lh1Ghjdk9fx~FIeumHI77N`k7*MObs z@ZpN|58RoH)b_FQWu35|2Ej}^G)_-e=9i-t9 z{HFS^gwW!J#aFuhw+i1;4L_LXA^1vJar$ovv!k2Xe{cLYOaCp+_yEciPfK}fFH)XK zq*oQ3a6BOsuYk3gl(FVXk`UD##!SGh3YpAsgd=LoC-lTgv09znsbk73&#m_#&y#@`UEV~jQ4c4;jzzhsEWPLS4bG;Qu<*lBm!K1`e6}?2>#j|zbXD& zDXE1IUI9oPHB?;Di~L|BB4pwP@}RTg)~A^r{1pR#T?)Iw*Hu&ls|4P>^1nz%Mq2hH z3;;ApG2LhdX$KLUQ~n`SuD&0O=#T=FnpJ>`r;v)3R*0)X#aS0<+s9Lgn^A~d67yBf zA~3W-eR2f>>k^KH1}4N~=`I}&K*|!kLIljyjPxS=0)hlf{3R(YK|*<0BAdMCr01!# z0_nMOpf(?4jnY#rOiRx_aF>d&QNi?llQ4j_(DOmWYm}Z2_0IBA?<7!fjOCnTa#62U zXhul7mWFaZZZZE4qGMORL&trv{?lAYPtZfhx$gzh(Xj(19-0~?-SLa0Bb+Sm10%9AGK;Ne|Dd6LrQ;B5Sxlis(_45W976x|AKR+3;7X&{BsMr(t+F$074dWr57?k zy!#9rlfv0E$rS7l)9IBO<))9+0!(t;=;=qI)gx25X{)zk*G1TM&xs{Hw4l4R*lEA} z?SA>j)s$^h3c|RzIX%89_(a%$>nLG1>|`sPBOhDKn+eVN?gYfJ-_U-Z>;255Ly(2GnOg*#hB$b`tAw}Hsk{2r+)EVI z0*HyeX2-;ao%C?#7`*C71%$S~8xaXWy%R}E*^4C&UlHYa99p3tIM)Frna7#>It+G+ zIS7+JnE8VJC{)A1a~y_X#LZSAr!Vg5^l{qwm_5DNNhp;McKt(4-AA-;(crb>lg~>ghMr^`TfS{%BbG{BtDDP_$j{biUc*<#ZrD^*86j zU&}E`6+J_rJayj)|-m+>SsvANdba3&Uv$z8rq$LXdl30SqFt)VZ**}ZbXww-3<`=-1`T0D# zz@c{Ue_j2OwO6nPC=9aL3DNV|99$66gp3P44Q+ z*rgd?W5!XM@c?H0m{Veeo*M1)60u$p75eh=!n1xe(9yMC`OGpEI}VpXxZ}V11;f|C zvI@db&^TaWL9)hqrAup9Wt@UGwx6br8+|((S1OI;Xz&nX2HF|hN)b;RSPJj(}1=<)<^o+L9L7vQm-< zzk8T{*dNm&^{)Ji3?k%%|I9z7*P0UX|84eC3iG}_1=w4`BSFew;l!NZVA++5D8Hd9 zpgySnZ1&w-7!OnS-FN)gxpK6En&<}jNw#xF3jBa8uCmR9|ncM4rfNte@5C+PAJ zlFS6!C{9uLD!tQ}X)3#V0CVRz==80t153H~Mb=*T`b+(aTbqFp<#4Jr zkVo8i*i+E1_=h7mN}iu9dESDww=ZIY6GfZj+3fN!hdf#u6jJ(*b4d<=>X1LI#SL>w zy&VsAQu^?|o5xu?S1Y(DfeJdbf`@*A3Z6wjiHESBT_l%&7y*29rs}3(ojV*zo4LrW zGOz-l0Af3U&;&2f9E0G6SELG*5WC~2oy+aOGY1gn$50Elbl7}|LO$3_nKOeQKYq*4 zkJ~EjV+ebs!hU@&VBe5}E!^z#<3k-zJ>bV@toXXFzt_J%NI`EO2k6lX`f`FE1JFMI zevs1a^J|dtV9j_uGj8tS08U5p{{`ic$xdnej{=$A@BdGIN(q+R|KI<1H2z=O_}wV4 zrP81D|9|wf(`CW_KZnR~r9l0CeE&AQa30T2_6HVY?AcM-FET57Ysq z+n)8c#uZh$yP#UbC*psgDB^@^4=BdqMzE&nO#3B{@2qrXWWn&M}U8S z;$O`-Tn>q47|!Kjo#t~IaNedCFsrn}27^|>YC$XX23Pl{`!Y-o_I8ZQgZ>)tCFzXEF#YgT)qVZj)5$j;2F>#!Z7Al&X?UPJego{@>c1-qKj`r ze@vyPK+yDxz|k&S*8i3O06Te^`<=9Mjlv4E`|nW}Hr)2!)Z@y888UecPaz=NesGO*WfI2}?eAdNq;(mkNxX#ypC|!J| zv{N&5;y{niep0K7m)%=kVQxp)sP@6C$4TYmP0aR{#q2(KTW$qIHOs|NCdoGheOfD@ zq7y{~aKf!Me1E1mKZ&3ApDVfR@oXkpyQjV#%Y4CYon~KU{t;dKazPo%V)nxEd2o1z z_Bg)^Q^;{J)4b2a^)esA?9HQwUp^U7L)v;}mp!vKszw?$lGr+pirNMt*1=X)vsL2o zR+#(JTh$x-3EYU;%r*}sgK+z6h?t4#HTe53pg#0XK>W=K8pLLCF&##l;;Y{mhGc_y zaof(eOJ(Iq#C-uyXfP5Bx}J3N7dGQj$Ax1a0i__hI1HDNu_ALP;9WTs=WI z@p_0ehVphwxHS$Rri5>YA;4BGk)=VH=tv&1SeEBMCIk9PS(s@~c~rsL;ZzN3mc+`= zl;XyoeBGg4jcv2x?#_^`bS&10p+*|Z)t0!VK%0t3c0yaYD=x6Ppvcqn zQy$V9a0U=gu`OuaTe6#h)~!R{*-{KA{;ohym;iFDbFFzB3N|^$t+C3jPMK#hjR}fD zgtibYq)zjuyJ)CivS0E#Js-Nx+)7O?;ok`u?99PXtmvI;Z|t{cfsb;)Ux5*o=;LK# zIJQS;jCTq16>LL9qh7{tFR<-e^b$%bmw-^_iXMT`LdJ>Ilz^YzAQJE>e%cd+mniDM zv)P7?&>EF|g4ASS(P_SLZAehOBBb<;n(Im4Ow}riqKEdoS^)r~p#~8GG@4>0I8gA} zdeEZ6?8SPdUm#R$HcyIRH)W~YucFa#P;eH8E>bQSRcgGIKzX)sv#6+u^|j5&+iPbxO77)uk%mNNH}S zN@ATPZLN70s!eR>;#S9LLN3W6nxZ0B*oqRaX1w5Gt155-(0;HECQwn-!VwDF16V4} zdCxKn2b`8AQ8ua~Hf!nKQZ0-|TI=zbtP<)pAHzmU9+nQI%Ip>dq{9LMD?30ywN7)+ zeOhCMIYpwX)M>+rsAtRw+>c_;V}7NEL-%FME$QJ@m)>YnfE9)TO_sVCN3bM zuy2PB>zx8#Mgcd|2h22GGYw&;%b7`J?O7QMR`Mmv6}#*j#9!nlSX;$ESo%UO{WT4i zaZ;N3;vw9vJ0P|EY;0_3O6CT!CH?v_X?O-lQKs?Hm-sF~x zf(8c1r}b+%i9fhC2N#)yy5!^s=9JE}M*m9m7L5Kc*@1TvWTL1LaTBu};~kPY9<3#n zP{~9*@P?kQF^~)WNH(yL-cd-!jJn*XkVX*F->vNl0DuG(L39<^h}r#tR4tKMVV%IW zv9@XVukG%7Nm`kP^P5@|@oaxgYub!8HKx}jV^Ec;qCOF`RhBb+y@i8)JSFAJIZh!@ z4VBVY7=)823Yu0XXdOPQ)xJeiEzxQRv)XUO;&pJep@%TOfUf%zM+y7m9&2WPNix$i zJ%MAm7|Q1&cxtv#-Blkj`1;4AXrK}UNg++bjvzE&a3MmC?V9@#u>dm5@YCL`n3vB( zo#UlWj|=#)X8ad3&eDt{m~n*LL~sH?$I5IopVIOsSur&+3C#s0eeES|YQwYn~ z=pw!nzm_H#w`zC4p-?_06fv+7TOyP@1q#~X&HTP*9!Mj-O{?6MnJ;oP$E!gjp+_ug zo|n-`D#Y=qt>3VOhggVH(QLPfysof^5VmZqNEGcP?YFQ+UwGU9krvpI1%9H)9Drx@ zo`}3e3c<##5Jgo_K_l~I0MuypR1pi`poKSM;rp$=fKG0f!53Xw-6$iiOEz1!izJ(` zWxvF;Ip3BAGeo1*Y3?jh{8w(2I&H*q^-V6;f~Z*e>@*V`Rn)27y#ejV9~w2&)iCpML7i)o zRvH?FS-UA8crib`i!tNCTU7Iw#Et~8@DF_Q;8ITo!`m~`FZ{k(lPr+gKYu{Uu6 ze)VYIR!!p;Nf>#Xv^yMK*!3k1PjE|xIpTJXGya@Eq|Jh{3S*D`pa+)LnPV`f&UI69 zVyMR72mcPs@j7C0i$u#3o+OH^vew>_3H+VkDDWl*P^}x$q=O=c{ifp9XW0&XK_Wuu zKqE$JZwfVF@|LM`FI#Il4Ta=THkeQRBVJ-YVCQBt-xJQ+0ge~3CYCtA%wBpMOE?Fj z*n@`-804(_9p`BwNM{d3T}Qp+I3g__6|SMN<8A!FTtd;Vn_tA+%J0=LR<-M7NT2NcA=$${(>NS(h6Ld zk@N`t`6FP~n`;@knYD}@^}py;f|R^|8+rKr@gR9P@K#X7VS6h%Cq3qLob$nv zheLiDC=YAl>IBKddjZ1s8jU${`G1`}+yIrNBVe#RtOor8qQ*AIsTFd*26R@w?}yG(%g^1r9<)4-p;;G6g`h;^DagSZecACi2d&;wwHorT?u1=Y! z?%@17AD@vK<-GZO+@AxZle7Dy!@aTiJ;?Q{zV%R9@9V?l{kLhId!8J-YDKh$u9MV0g@>8b^O%0!0FsS?-Hj36<50`(oVqS|rFd zV_Ogtl-mZ>eF*)63Q|&=5SkpR&kP?TmtEHhhXqvVG)2I=WlcX^!XgFYu?kamy;FQe zw1Nc6>PY$7$bIvXE=rtB0L7hyU<4Rz6#Vz4`~)})wbJfO`3-^%zWY*c$C7fh3*7en z?@Mv%2T2Wf%|TfBVeFVI$#mR+U@ZlmXT&vZm66ON;=p@o2*7yrLwVN4 z+J?!}ms-;?hkXlBTmyq)vC90E!=i*?b!)efqSky*VxkVK1NOu-)&CgRGa8V2F z4IZWsrti2;TGQh~g~0yIjkZvg5hR7o8cBu-8JCPcMUlJ$NV$j`*u>R zaz$z{o^vPw+gZg9tOT2wz318n`fDD_nt!SgBZPP>A-426sOIPA?Cqy|(N3ES1&23#bP+JU|lg$&em`!Y1f^fk~b6Q)fA8iHupaP8lE)ho) z_9Y5?FT%cuY)v|uf740qd*i_aeH#zn7GE|s9=yu;;QPbwxr>sXw<(iHe{`1~Iw%}M zgt17LZ}8f!Ly%zU>z&cxhg)=ZRmzQ|Zv8O=O6CGiy9k@%5=E6QZ=oGoXg4kN4fVr8 zj6x}IhPz9j52LESlibx0VDik+Evt!oMlvpwr%)e;Pz)8tU}{vLriWNW@BlXhN%_-P z`rA+BC^5-Ciiv}<8Z_fjRU13IqZzf5e$}$}FfqOWti4kGkQbp@)$chbFmwM-28W%g zpJGn`r}zo= zzQ=ZVLxyDP+eu=jJfv=0gdY$npXK9K#iTKhqQcQk_;~E)GMV4D)woy*VAJ zx&-4R?pd45P)keZwS(E#lY?`D%pVR~PfCn~%~l2i1?{ejO0z9PNM4!ptExb##2h39 z6XKS|HwoMWz}2_WKrIL3usYO$(hQ|2^C?G_$SOm5#-5S$*m~qa{Wxtq4vvVHBu6Iq zH4$nZlyjC!;3Ho6`Ano%CI{p6yHH4;i=PnNFKre+c>Uf3ltT|X)n)A1h*@%N3J-9M z2p{XywNsmn>_`?{%U%SRwsP9Y>(Zl=^vKvHgYl(8CxkchjCjqg z)y)Ki#zgSzI%0F=-T-q36+x;QElp*f#U)i-Tw*uDP2u8_YOxHon=N^9BL7^#4K*OJ zZDFnxRU(=-Y~H^jtw-wJd_#W1PqK^5D$7=T_NdeVazZ^+ndhLHCqa>b37uIc4t~$j z{zZtJwewy8!NRUA`kk(gYgW6Ud=#*8)^#u4{GR=?bNr6q}cc&BKZf*CCmHf(_@jjzJ04 zB|CH9!+1_C&05xsycs^1%b&IE()2-suBBX+NaU&VuoWT!EeBqcH>_!TiSY*^yNSZK zo-2_I)C3lhEcnGD85JF_rjtIPBQ{q$fq*$){D2m%>y_E%`T3HCroXSY?l~ZMt9nG4bV4t}uW9%SE z@w)z-y#OvkdO>HWL_i!V(K%R6)>xq=t0c)zl3|{o;vqPdD%w=dldoFa?dAh97d9(H zAtP5z1$bE*@)&Si90-brie*boT*sycVdM7yD9z4g2ze!Fv$z8Z=3;amtV(^^;c~oC zh`E3RE3K)kq3$tmuxBcBh+b9^J(YxF;R@wZ zNd9bO3pK#prCK;nx=zgPm^uDZR&QslXD#VKK3N@xdl=0Tpk-1Hw5!*W-vC403MSxF z7mHSz8`v(l8f>gm!zflL;8Zd3Vn^8LuU`fbK+efQon%#)&9PSI$Sm24#SS7XZ^{Ye zqxmh}gb3gtR1n0;*2t_f`&&{+%>!W9K&}k3>^G)uq`?1v3IzJ2uzSb<7`h}tL1)W9 zrI-1!YqMAI|9yUN{vO76sMdN8$ra{jCt}BL2R6yK(xxyCvZhML&}O+As-kNNa&Qp< zHsg)@F9wCCYI^%|4;74C#3GqMr(XmuIYiDlDl|{Z+ATrLAh(OwE@IwU$GXbw0!=26 zi(9`Ip!|X%4`ZBLR*wakxS|6V0;Yjw0PHlMz!ay677G<$DU0dxjBwUL5`_mSAlL(h zhX6q-RM^#w0wX&Z-aNpT^AYoOjEl(1P*ealM_0UT8_~>>;Y7 zPlMLLXzeuH;!q#86hHYnhK#M%o2&H=>Hk$i?DtPAIdr8vRccbk%)c>idDmn1hi8O$ z?|RH<7!gP}VSMfFMSWj^TfOy7)B3&jeK(K##J&U%>Fkh|->mOxLr}W?#faFbyu!p%1k;y)Zn% zO7Uc52GJ%lbKMMPa7?aFb11wUh8D}6gDVF766|&#QsXP&nh@i@Epl6b?Y~FblwbRw zqal>5{kuRXdE5VDJgxn$Jk{+!u)}{%`@^)D^X&g0H1yE^%NqUdZ^bN`um6{E1JZqn zj%|SU>(;Gxi%$Yqc>)wNs$flO7L~gl; zl7Uc%4$^LZ)sWh_u*A~{n2Be$jl;s=RB<6tTdaiNIg4s02{|+~;-nD=@bYM-)XM_P zqx)hKMH@tQ$tzT;UZ`diFRY+NKBouF{nJ5XE(Qg|y{n7=UF4rV(^@leCQDNGq*QI2zvoJ`pJDeSul`xjo=Eub_I+uFkBFA%%j zCE5V3dxZVYUwj`zc5adua2w-HfW>U?yR7K|pujtZddIGewZ6e zF!I0M$?PWm-gO~Xq{|ym)uy`cxIoh*=w17S-SpbM5tBnJ)V?sx^&+EVed=e zqpGg|Cy;@##6%?+_W^^Adj!-7Vka2%p^2sn5)~ziXk3es6x@juD(N_uDsI*KbNf}R z)~Z!Dtp-5|Y8BDC){6U$L5n*mI{)wY-20Z9d6Uc_T7Um0A9(ZLUCuq{+;h)8=iK}5 zlV|cx)ZJ1@A_zGUV8}cEVtfVL&nH}4nKah3yn+O)vuGhUmy9sw`AdiERpdEFmPcl! z&N^CBKqO3!W{6(cfc;vuI6HQT>dfR&mEE9-)#~*w2T~hwEWXN*!g?>~tppyQ_r+ zcD1?;t`3_Lv{6}pQ@AjDF!nNc^zI0>Z;Km;J1*(}`V~k&SCjrW?$xu#E~84qdmegj zRu&g;VJQSql7EisS#YkSdTz&=OeBM4oRx8j!~a883Tf8XBX85p+5{h@`{BQb1AhJt z#fXk>R`%@sbZ1+z+ckg37i%JC=a<5?x23W6z%Z3q7gO-p$CK-5prD_U!(jq}YHZ zNP=~E(MO&f(i<;26G^V*_AvL zT`J98&%k&v_Uz%fS07XIZ98N$*s~K(*c|rk?!P2f{{egUUSY>(wrBHC0yW9HD~&x{ zk9CpG?Ad-$!M^tFBhXYHdv=sJKD+l>$@c8)=q6X$!vNCp*-0m-wP!1t<=C^k(ttDe z?C)v7{ZV%Q*l`I0G%t@_hqx}Lz+y9H^4B}}HxiJE&)z^lCkh~AbFKkVsJw~x?8$^} z?AeD2JI4$A9dH`3)7rDA>b5(#XRicU*t1`;C1cM{W=q#yp!rKnl3kw=pM6w(z8K@P zj~(W*XB%M9c{{}~K6^Z{l9hahXHS1W#RMFWB;@@0ZBLFiXKzH33q4OA2N3}M|7mj; z!ocY49q)M%*4FG*NczF7+4GOrW75}}eW=su>_gfSDmN0%U#~AqnRk? zQLJUG*^_CWj!&wUXw4qW;>Mbt#^Sps6_@>bT|{SxKrcZ8GPY*_jzt}m&&rxT+W7LD z&zil_W6cVG_ux{pZOyJT?PXxiZluP<*sS3g#S~}&Dym#gFSnB&E~LYk3W@I z{RiyX-C_ArH;QiCT>A}19Rr}r+AEDcdp*LSo!PS^p@eFZo6nv-ft;!Z)3G!p{5=6G)3IkmCU%g4J-eqdGJgbnb{jJT2-?t+@|!#(bZyVx ze>6xp(b^}z(&(kbQ}fnmOBfX!}vi}8P|@dCy`{Ytwxec+)GO`msUyo6-j{^{x4zZ%w& zU;F!J(7wCBA6w@mYc1}x*b1wS&c0!sZ-kd%%a8;OhV81d*QWtrQ7c3WckY^(6YJCk zm}tP6vPfGFEb}t%;;jSuR;NDZW^BK?@;}@DrAK_P_Q`%okI&I4U}Oztu2z3A0vm1L z8x6?BQcJ_kGUCeQAK4MSpq- zGo1547ljkf1Kp9;;0TnecP47eYxLvg#x|J)c=|vHOFT$|r}T1bHS;n!FM1Zbg>~O&y- zc3qu5A5mm^y%l{(-k9*HVbd3Tpp(Sqo+}(|Qv=6=G16<-NtWFH;Ygc#zo8X12+Kbh zvoo{i+~&u>9ur*x{);$-1?lrf8H2D?%}0ztcm8(Mf&a;T{Au7n8Ph-l{eDX^3CWP4%6VczgNPxhtV4!%tvcIXPf(& zqu_#=uX~@2d-Vo81B1x|JSY{j4ci>iQR_*-?swWRrUdts;~87oO$`K$SYRsETdftm zi_q|l@yl94Dm!YiS^U*SWH{1^GD#>Uxk%C@RfqA^H6y={^B@(T;;@l!ztMbZFf!(t zBVFVLTz6mh%}0XFr34(yzYX{5OZc%>F_1}R4k`@ntzaVvhZ=X+@9?@HA_QU1$hn*# z{t6K7d*DPkT@s&z{~r@VSc8V_HqHva$m^>+9FQ?W|0GnsPG7JN`o5HXn!3G+SX9#Z%?tpD-Yr=Ki8sc{- z<=^1Ilm@RuSwg-Zzi7~vnn-JT!F>JQgE?{<$`yt_Oqs+v#3Kvq{hRri_JxP89v#Cx z*n=Y)7GDx*MZ4@*bgtp`Z=e{xmC@r|etrUu3CrjT;IoL8rQQ--#*=EoRE7w|0 zOV&v{^W=s)IS&UjHqvK#qp!ocqxwY99zt(r`Sq3PScF z!N&5B#=SZcQB7OetDNE6pL=CR{%H&VCMaF6wf)AkP-{9@cV!Lh7Qp)HzO4P9HZIWq z_tB?!!>>&0z(&i}r@zC!iqZ1>N$b;tAy&4C-Fvtm5cflD)aw1x>eFWrbM@(J+^at# zj_spQxy>MrKK&~}7=5~cASMIE_tB?4WlO_m*QW;C-9fKx3aau>(iZ zS^G)r(=W0_0yK79iB>R7BW_~_GV7z}Cj>Foqz^y!@hu{A(^ zAAR~)a@wOHg6*5dKKeFesSNB1O+WPcfIQB{UbRs9u1pBoADX8^= zxNUr6pxZv}LaBZqecBhZSyBgHbFiyVAHco(h;go;v_8EVVr7e1f5clIr*7yTY4z#H z*e_%I`=8=o^#Ykb`t(%@Vj6w=0znvk`Z7T*g|qm5^yy>@{pQ!F*J11?g8hN@>8>da z{vi7FyCf4cD}DO=y%L#X^yzAb0la$C=+jTwmNz{MeR{+`srvLNgppG1)1O0tl7{28 zBVB!Z8}8Lf9$}w|<;f&{+FuCp+NTpwK&^@m#Vk0(`V8*i zz-8EUHEfx?cB9u{s<1;2;k|_@XvMG~tX6Q0;0{qgzw^&Rst`uWT*j2>X>)62R3CuY zftj6pDrCj%aABKz72PXg@sOG}OiQW+Nrye7{z^grKIfw)v+Vov=gTKD| z0$dBIkrV9swXRb3c_iszcc%rFa4G4_@)3YR|9t1p+on7^=bteBHj$eyHt zQt|R|A>0Vu_)bkfsrr$%n==1?6td^1SpFAy8^VnKjHYAH_hr-ZFOh9s^J_DfkE|`F zJ%W#9dF>I*j+`PR_<|KS$lWKtAroTxGq5$^P>;u%A`6yB7FbEX>(QommPiGvEf4P& z5P-|WyW2N==$jScJ<+nhhX|G?QkKRbOiE7Cm+8LuAv+=(`Xba5!s4^}U>$ZZ=;o=D zsgiZ6gZYc^Fgfce=hnbaZDiqC{sp*K^TnnBd7Q3b*_y-8U!^mQb4AE5@a{k}z`&Qx ztlcEy@aW1zr>2e>(H*9<(vN5#euXFlc_7YXlj760lnN4NK%cs;9tCC89bukbNHWtm zWJpPA50w%p(XQr0^h^>Ev7{|xoxBs7k-Rc{8yx6bPi^J16?wZI#l7NIoLK%_zi_0w zj>t^KCsA+^sRc@9hmU-=%>o*PqQrO)r$UOR3q8tGzdb82V% zq$Kt~Q1bD$DAU?Or+4)sWMD3_;)rd;P90~&0%dHww16_pU9+20E(_^Q#0M<)6CDmP zkLcVCw~7dLDJ-Dh$KUg9gdQabal~^(hHd!AlhY{;m$TBE@K5YEqaMXCXj3m@*lUjC zo5ETSb{3-+wU&ps1TR<}pIW81s3q1lR1XheVlh2sfBo4MG<%Jo+t-CF#G-DSdK)JC zT(%-O<*D1@)tH9>ljl&usVhZDr3fTIHc)ZgMwW!t)98810F-|V%Q^xQX+FpjHC8xcvh93BY&)yYczn6U8TNu_{L-cG_<3ODX2c)e z@i1a3wzz>^yMbK?h>YQ3cRUQxx@r0@B3W&!r%)*RY7*1*;2J(evZ=+s9nd9;c2~-; z=C|v($#W;68LtMwpbZc|1Z-yFi{9?R^^i4h9^~#k=j3`jkEcB2j7qy?o;n+$x#SNK zJ{HdjEDmr;#`3odU?c63SgjNlq2Zh6@o92>wzK)zTLFqL&e7H00SL8?RLdY-l)xLm zhyNUZsu%g=xomIF`R3b_#m*CspQ-qCYXWSrrTE&n$)>039$d zs9@|&fNxXBBa-Z-z@Wt!q`WC%raY|8Lh9CA3s}6<=__F@@ImX>bZ<%O`FZ~>F#o2R zdNZ&uJyzd23{|Irq*2Qb-lAe{8I-35YUqmvdtM!nyVHon2Pc2)F7kKjB7az?`RU{Z zBOSe%rvls95nn{FB@H5B6|0>G+39^bvq4gfCrqJ{H#0za;uk9&(&VPA{-xCwtq~g; zH+gC?-NsIOC?j}Qo*K9%d!|iYVQf$U?=vT-Gst%Tcl|w5FTQ;<#TN+pxMD7^L+Y

-aKDBr9^BC{xSe!xqL7QHmvIRztw z%!FS0YMl0F{~+zvLs@#;o8`1e$~QlKclJux-v9Q_roBY_BJCiofcXS52Dh|Pp_G$$lcSZ2}TwOX3^0vYc^Y6DFslX{D!VvtrvXR!n;(_GuG5M?@_ zy&bW0ZW8hL75IvUL21#6wUyCIN+aAEz?1Wy^rzatJW$S?8o3Jo>rK!kuy6}TAiO>S zvk1Neo~8{4dE8{)p80yxbI+kIeklZP-Hzr}2clh>wZ(Q5+pD3H9sppT(0QF~C)M#- zyb+3J4_O|ohb}F3KDT=JVC>yQ$J;N%PNjs`aa#hmrIf4N`WZuAYU!ZI3{=r7T0R5I z9H?kUp(9I*`R3g=W}Qmv;4|vFS~ypWGZ6vJg#IgxHT|~S!MlP1V-Q#Yt809$SudwE zyHDhguRIayGPQqyn;b0)>g`dT_nu7aquYQA$(-ipOdN%tf z13@?JPsA^+z6nD*Ob~fDX&#~L11t7y?2Y%l2jz4G`6KrJAg>f|MKmoF<+9>6h5Cc- zNTGiDcRqUzLJM>|a}g2*C28rVlSM3A9SD-SZydk%GDP7{JyOR%2d}#V&I?f~SLG7H z6(yN-e+FKFi_-VN=NH81GL6rZ&jKID?{&x0wOR&IlkwS@JG8Y!0-5FNw}=1>_wa6Q zc8D63GsYJymSN9|<-5Zmg~etZ;a=qeS-rO=R5q>7gyVVfm{_@!DcWoNy|9)ny)Cwi{8DkcK`-fe^mvKmg^Xy zj;$oZF>GbL=w)O|8has3K8Nh!NFo_mqPrH$Qb#QR!=WJK%bkRbjK$ayAcHUI!N!MV zY8-!Ulk+!q{p)cGS_b{%>JJ3Ho7S!uwYhN-&TVw(F!SA0Z2et>WIdDVqb?n0LkG+Z z1qzz?5YcEZxr=acQo%y}LP@hfp!;j^-<-YhFlIC>X5M#C*c6OSz=eQP6}4nBJ!Hk^ z>9^uBSB2FYOcQerIDWEETA|g$U9e#j>oOmQy>@%F0dUO5*DcYw-c)=oY;_`ci=^{B zDj?E|2lX&aCIKWH0Ls|CdZG<+`*Rx#@{e<8SUnFV$TpbTF`eYGHMKuNWd=*D zP$|kPoYfKW4M1M%-GF)>KXZ#bDMcu-SZuC}wl3BaSReKLcDe!b1RM;zRn9~evUa_m zyRX^-a@-;$bLabdps^_%C>+V&JU6aV!3VX$N^B89c`J+d!6eu zX+W=gHR^RQ&eLq+pFP;^tHc9Pt?M^vT_;GW!)5CQrEkk`k&(sh>*%92d%=hgzBZk2RbWp$wD&VIL z+_O3KfPY_x)-Y(Y3$i83D-=wp@tp&$P?o_Rhn`OOF&n-pWuUAlej}alB<>0xP18s# zEVedvF{CK6zDJ|1M}wVKSpDhi^qkk@-{ptQ4|8NBY+N)LzmAWi^(1xQEl5@~zuRC8 zpB^2*Wa?c^J(S*PG#ut2E2WMt`WGp?EQGQf0Z4CYb zrJ;7EzS)^JMTQK|_c~yEsKQHV6E-a)#@|B^y7W_jLDu}v+vBeM%YeT&_j+_-DTP}) zKv$_+`Pl}svw*v-_13^QWBF>qmPrQvKjx4@M1SVCK;KN@;UxR0E3Mp>JaD|-fDTG0 zSGxYtR=e3m`q2v;7wrI!dis7A9@!Q0D;|v=hwpNR*BZ6i9Ad=jt!JARUBZ5p@*^dt zF#}&o$6#%L9P3pCB~nH?ao|Q4a#{@(%)S*@yS6$4LtXQHpp(`tUP&u#M8{|?-yOE^ zE36pHzX`1`Q5oia{Dg$H9vvho?DYe6kzxdW(>-ZvywK zf_PD56IfW!V_`Fog@vP>$HLP?ya!nX6~K(vB5Qn>v7GMN}bcmi~%5 zvG;ZZchfFd|*sHxS| z)EXRX9cpT)AL>w3tEn00Y3CLJ!Yvu-n{wG+b$E2Z6IROrh#d!;rn#o$RvZ2Pi@<3M124@`^US= zop_hACM1+lZ$b(6@tV92S*NZy`+N%7JDzMPNj@V)2JeAyJGZIJKOxiIs934G0u$O? zo#aF}jxg&%^(_^9a@THspuxbVnw4=P12_yp5pk#99s{o4*=O-%$wCvBoQo3omo=XAn_o$O$6 znl}C2_4>0h2(Dqzbxuu2_B^P7T(zkdPDUBR1BDniSo6c-%9wx-NiGVU zH9csQJDqsNAWUXw=}v6Ly=o9g+9}@FLnMgJc8Rl>!x@Yx6C|Mj)Edw;vCNR+1Wzy8 zzfH}?h|`|GgZD$k`xcG&GsOE*s0j*0QfRk8f4V{#Z~pN*fzf6`=KgM0O!$T>&DR}@2t&m85B z3iDFO=a{=~o0q^&*T7~H*gYDUMPS$4g9c6S0h6KcB1SjVLi#yf31l*XNzWP3i>L{V zACW9<)*mj;6`wGQAD$evkGibD~aDKTJ2Wj?;WGI?A5r zY5&qN9)10?So&FW_ zkc@#&W*p4$K*aCRdZ#^q`~eh*VD;uE$e^v-Ol0zbouK*;APZypf5*LAVpIvds+q-s zh9ij*?pq7ATKVk<8{!A`bf>&qS$~AC{}Jmqx%FE^LWt;x* zU11ByuzLf2Xr~r9sRxh<4GL>M>{0W;F>M}TbUEdRn>BYd9WHl^*x}u`-Uc{VcweOY za6}waW`0SI9s|wWyEA&nh{_hgCX&V0kQQN_wMnBIC%rD=;z|6t8pv}^O3NU~MkZu#?u4BD-O?(dZ zW)^Y~vGB3TLE#xJ+@Kq|kcAhR!th|j#gsqb2jjV=qL7lILAdd#T2`N-tCQbqrrnmO zvm3Y8Riib4!83H`8~2Z(3qi~lT5ey1ssj%q>vQz$Bu+M@Pdm=rz6XYDp0OgRDQI5E z)}1sWjbY;KM%m3>ZxB{XduE(`C}B#y%_I+PV57;K(!ea~=C|S`rht#8f}ch3GRK_)!=eMbU7V(VnMj&FmJdw@GEz#?k?*k! zIDRq>|A@~Xm&EZCPOp?8O%A{Q_pJ@wy;Cylq7n?%%f~<`@fP9ALAWKKsb~r+76rlf z3atC%G#nhC-=-Ep_lf`)G=F7!q*g>IGBeM!>VDXE5SWLwz+8xXbr{gcmW|;8Th{>V zaWgh#zr%N+mDf-tO;e~^RS%;?8WAo7K3++a*N_MWmFLQ;IDpltF`sUbMtX4b!Z<=_ z>H_FVHr*5R%ll_>e(6WOENZQzU%coAq#0#KvQeiTx}e-;f^5wxn9nnKz=5@9ve68Z z7Z$YCE;HgT8Cuj2Ub7+I?1}5nLY#c@GR>B=88a_)^s+P7`=ZW4IqulImvx@hb*^Wf zjWkvz!qY@|&eFE46LxpzC-9~-ReCmbN|GwNflbM>E#kFo>UYuJxToCl585R*>poO*tD-in-XlLFBl!eT#B~Ef(E}B`YMN1*tomXbWFm! z2^7sC=e%fV!Vls*5h@`G-xlC#e+%?~iEoqBo?u8`dWWTK>kB)Jz>mD9Nr73zb*1~L zKNITpmq<-`P6fjIB=X^xlW?a2pQzuuVET1nQxXxmB;sJr^bm=7)Ywf4sO#CX82J$A z16k$gx=J;xw3sSbLU3&xU1f}}axSZcb(PCliI-rrPb1+7&t`q% zrsGaT;hjO{)1Byje@=U|=-{I14{Ec$RK0>JVXigV?C`_#=lyI+7WK7}_7qSasW<*L zYzss2RI&F<)gNJFaLx79_A;&42Lq;>Did*Y$t{EuX`M)u@G(sFnAnCpR%KuGW_i37 zY2~;Z4rii#`Y)Jfp?#?tYhDrnRL3Bn#;FhG!|Y&egB?_flJt41u)s@83{vXpht#U;Ipd@hpUUE=KhLaHy4<`s%>3D~ zhfcT!OKxdV!G<5W10D0gw3%=bILV)fxCWUv)7j;B5dFp5#h&9XKlXEa;c%qn$Gh>B z$IjnQg|)QzD~3^SwCtU}Fae%oAI0+HxL3!J=*fs#N~d{6ZUS;fBC1)?+#-ESzk?ee z8ZOp09{punN*%KE2^NBRHZ~#7F*7wnjbn8UlrGh!VQuNsxS@5yZ4nKF96U-j9`u8a zysUl+efVTYkB+Mo?8eR218MjR+4J9+gF#ep@7SFs+zHd80yQF|rRr74=v>M+_}W~e zi&!=4P8iM|eJO>O+V^#E9wq7$O1vg9KY?PY<3P1oiVc%=o$0dTt0Y!iOMM}GdNAB- zy*>8_$TyV5^*~IIQQmQkQm&)(+)C;Xz@hydC`O4ay#HtDj)=tO%tj)HoFYq{MebFq zOuWg^MW>znz+n{Q?JKegs_DmXuj~B~I5wbxGp;{>GO9mv zb?5c9=mjW!;4wqDaHee=(#xQX$2V%%$m0>+buwLt?%LZc5 zu9U4OS&Uk*y{SFl&hbwe+MS6L)J~urxMyP=gHawUH0|)AWn1+kv=dA?`+aHZNAjgi z?e~mkQk3S}W|QbTMY9qK$g>B*X!8d>O36vfzx1Ih~im&iLhg zAl2M7^Ke+ZJ(tAS;3I|-pmu_v+hUlu(~eBZ`5K+uezRZBJj{gc&3-vgKznzbW5t3$ z+bc(6rlq*w+T4e*$W^iM3s|+7w{}J1B5aNg#ETv|qIUq+d1KFK=qtb^L29645ep*L z5@YRH(P;y4EKDr_^1X9V<9DdRcPtvOk=G~?+AP>P@Q1Oz0t?Ll=keQN{wJ|_vp@QD zRaz-5g$T~cEnz2=p@(HhP>4q-aDwcyuHgPHk&2};x%4j?xumhrLa-swQD|(pg85Y? zIdk%a?1Wtq>B;KG@BeT9r91D@UthrUqT|5NuukQCP>*pBHDxTeT6N<}33&4@+A{qX zZ5i|?l}W^Zc{WAxTzfuIe}(+a1)5nf(~+7B6U_0w{JQUn zi0a6GsB%(Pud3~53|=lgkK%JwJA?>}l_GH_!(+mSs_07o;+@hZ^nIm5Z%o=0otn%; zDCzJA3O-IAdR_2GYxNQunro{QYLZ0T;=&~w8E5Iy+bzz0=KUiymaF>#wv+D38r~GM zT_{a=r;g{enk8w?2mIDZ-lS89cJx(OxT{Y4n<-MN zAMZ0oN;yH84KjZ|TBe18$6#8xTu%#U;$B_LElW@m*cZepMw&R+)GrfPh>cD(DNqJD zI7>c>!Q?TC$+FB@tM+kHxkp@JGgOBcd;lHoaPJMHDkCsIp!Yf-RyO;){LO@)81>Hk zFEt6lwzHq&7>7i_?IUgttN4sCN~y|0mUaZs2*MF;MOZEqtP}C6qZTR#-#wwGscdSd z3OosbcK^;)pDbf@x_=v_e`l&Un1&+Kzcbadrik>f*)IO@QPRKf<7e?zx_>Xh(PnBs z(Uo?tms;Y^lVz6=T^$^)qNpOweDKS*mT-}=e&_G3S% zjxWqAKK`M*9`T!A$?Flte62h~|0>mf?W(=S!Q}Q7XR2)4%S35IkCmv8;o)Ei)p5?L zQE#H(lh%vA1@Q^%MTOAEZjC>i8cFL#KkEnmd?Z(vvWPruNf3TzjQ`#V`0t#Ge}3;I z{A(6_@&BkzQ=EkV>3x9zm5eC=!1$jFKgH$dB!i7-y>7>3{O2z6*4WD*|DWXn|7S7R z{?Pc}m#}&`nkJ{Vzo?cWVYO-pNV688o%Issb5L$B$pv3I9El@h?cg|0ugkGX8fE|0B3clwBWWuP=5-h&pM#^qb7rOS5n9 zLujMK_S(M9vc1Id-IYS*(HV&iZjtY^XwNzRte4_}^AOVLi{+^72@n z&p;WaGZf6ko)x@%VD{6lOA`ws$V}{~V_}V_Lo2mE16l;I*tyFRS56H2&m`avX-Qo! zC&HzK0dDmfnUm%ErWc>i*W+HS{( z*yjY?brC`q^JH1u6pxzf+yh`kXyw~fdJkp&eVm7!4ELDZqY^W=b!zPQ7mmbcIVh2D zU@X@gG;=!g%pDd_9eBm4Rxsb*0okV3CqmuMf%>M&Xa$KC3%PmL!lo=C7z-~SnV?9y z+i*eiO5p}y7c?)KMMi~&qb346_(qsbeqOw&A5yug40eb9Jh^)7UQoT_3BAhGrvAui zsh-_M!?a>F~*#n0mTY&n)c@WmjBQUV0Q~6fRR!Al=<;~JEZFHVB6s`y-))aZ7P><8K+HL zE^SkT??RLo6CHP6lLu7uA-_319O>}!6|zqGyZ0Y6=5LA-o8|?Kg!W`Zg118zt(vaF zn431fIil5s`!Q^6aY`KEjBcKR=D85l`DpHZJn5nIk_>=RGk4R|(9RXIl@fO2jPH72 zCM}xD9}c>B(Rh?eFIr+Ux#*e|QjIn`J-Lci-(1e$c;x+aa{CWrWvdF_kzvZgbxF%ugXdtuG6V zp5?X29|j3&wWN#Trovx*%MT0xMm?K^y5j$);;$aa41Y7;AvuLSEGq>aM<4k1iWbEc z7(|@bwXnPH^{oG)nCVp4l|R#yTKpgiw5bQpL`0eAD0JQt#kZ?(c407p;K>@ytPDiJ z6$acn^!QFt#5U{>*pnpcz?Vfg@wD|!-WPCZ{xb1UQcR)*{Il7QH97+sF#Kp*tG~an zaZx3@(y<|~kaI5RA<3~R`co+kOuYB0XuU7-V}CAX^_lR9u7uZ(DPYJh9*;<>auz5= zste23iW~lHCXt+i*}b@u>9ppVDz^Cp;JUiE@e-#L5@Dno1fzjO-A{pmy@LX6st3G6 zXkSK;+V{sYV*9?hI5oB4>1k~+8?!-rXMo?`WMkoxYCS{EW0pyEgA6;`w1pZkvJey_ zE7YIx7KU9E2ieja)$PuLk|&YaJZ0t$uZAD$X(3_G*j6&QxYYo8mCc`~rBD8ZK+m5& zW7EvrzeXfVCZgWphTe&^=Q`=oQJ%pr9E?hohDWx*ZG_NUs;n{*=XH=B3Nn6(tJKs_F($h-%>PiIx%i2u>N+h7?3W zn&44Tj0~ZR@S?Q5Eqz4^qZO)`7s}rmetok)moem~%FUr%9yAxbBhjs0(vMyy9(-0y)npdscX^FcS8i#@a z1*gTJQH#MJ*$BFy*38$Zp^^C_v$h(LP_7lV-(ST`f}+!n)U70s+@?%?Qa89t3K}?I z#9izYzoE7(=?4?5oZP61Nb&Vqp#oj0tGB6xQCfPL@jwU5glJKEap8| zmbca%;CNU(U2|al6mZ}n8XO+jy8fl;Gy6M06v2g{2O>Dnki|z#CuWp;IW~`jNCe4+ zX@q1C!o*v{Bof_XPG@`vV{9?Ptn8B0iO?>-2puW0FD(stVb}={uTq~~O9G6Xd;iFw1*{^t?ef97>Q>IPLfbpZd>mUb& zR{`J3CT-YW1zhOCr6aeaw&Pe9)0nl=#L&vPx2bP35|r%rP{|JC?V3ihp$Qx)7xo2p_y>{ z(3HEZxaa#~LX5}HVm&+NpNv^fjY1HGT&vOg_9gO)anYfz?P~(mKs1-2VKEkUiT*^? zq`Dscm%npUU038ylx0x0jt~(;7_K9&!{InBTuZu9&xeq7^1@xX=Kz#+h}`PLMAZ(U@X6mB%E1i`|Txf zK#@c_X=ECi^Y+T7-#hZ`+l$~?i6$C-uwCl;kJU2Wo8aQ0*FZuW)o&IgWFY@~0h4d= zF|K?ZgdYcc$bDhg9-`y_%#z7)Ft2d%6!Q~mz8X3{U1y#{8y12p3K%zB9o0x`DHE9*_6~Ws9=dso_zuVON(lA$F zAXnw8zXaRFFc2oSzyovJ+G%!cLGu`4=sb-oxK&1Oy?KR)TYrj?7{Ww04?k7wz<7KY z$bQ_{ zRhY@cR;z~$f|50|&9%HY5Wz1sf-67|H8Ctzw9>G7yd!f8mHd zZu*&zYAAYQPP2SW0v$2{S<)f8N~?c|uoRnL2o1n}K>QK~#`o-x*$z5PDr3afAYH}h zcxYP*LTw>{C*}zzNF4+dt}^Q%&;VwiP%WV>@B1pqRFsg9esJDqw5~7G8seJ_M71D}Y36H4bG%xdws<3hRTGX~FeWUnK zE<#>pUlGU1&C^O`ga=ut?;3*b-)zJ(JeE7$@{W}^hb`LO+=#Ulw+wshG&+DVwjA%d z8gt#^eYMn{4DF%bWBlD7qC%!rJ7A>VIUJ%&_5*IIGoDaK(lI!Eo#)o{2_jlq$*2(o#xqdF! zFXg&ku3yV_qg=m}Yv4Q9>LJ%$x%QH4o?QFNwZB}qkZYk_x034sxekke|= zNvz;DmTdrkt9V*x1a@}99<#HV%*ZzUtz(k1#iwP9pg z>C(ia@`O@(JUU|b%7)#o*{d6dRAaKisfGuY-2aTl@0LIH+1+D#@YX>kV_Pb6$F}^U ze>Gj!WqE$kC@&MSlGt)~N%67#2-2|Et>~v#wEe*CE;-g}!76|Bn}VIgfzwVa*twZ1 z5`d4*vnKZ+(R9&n8;MZkmN=|xL@th}YC5Qt2@UwTOBJ2yaU=57JC|>W$Cp}>6@?>r zx@egd1wbo0k!JbXT(?^mI>{d^0`mV0AHxq8;j}UU7#6PrilMR5SFGrVRUjVxm}s>$ z;8O-)9XR5Wsw&*pR7StDqT}-ppWJ`w=%$Mf)JSi2_~go=M0#}7LFH^nV_klDbllK9 zwHL8oJ~pzvaAafPq9s*PfU$7F)*m!yMVD&yXTRQX&RiVMmK$Hex$6rH2@{(}5?tf< zO&1Q3o2{BIES8%-O&9LK8-D%~K1n5f>gWsVlB+qL9MZq(qGG`;F}F@~Psoiw2iT2w zP$njwkS)j9X!<|A&D*Ck5O9Fvxh3H_vbKEIs9jFM+FRpZ`n>+-!2x*8!Q5_}#KTq( zTdDpvi=?yx$@2CwO(MbN4p;{PbY8gz!^)h3#YRB=n^_-~6YKb$j|3PNBQo`1K6g~- z(j+Zo00+`#zQs|iPHHATNQ(xLjc(%>zHn}2&1vYYd_09zmpbh^UeP)A~Xm{d0ThZn`M*$?J`w(FZiN=pRmCJpr{R{IHE%F zqWmujlY61-S)87unae4R2KDII0{%U%*O8@4iDG+(0| zJe0*FqYlOozBi+(`wpR%x#+`Mo0YE6ZpJ%X9*RjXKEc;#MJ}C+Ku2%fkZ7Jq2rP=z zV1fQEXaDNM%kaI;iR{{EKPS7R`RC$E-HtIlSH@`r;@kPhONnvr>Om8<*?_athu8zmWSI%ufZvB3!9}U)Ql^eiO>p=^@0xg<93b!qJ3+dTMTci1J zpU^8X#flCXQY&5F|EXxv_AD_VPfcf&A6(K!lXlbc>$V>iHyikK(L=sa4oWa6;vM4! zkw|&Gv2@X?XjC09Yt^Qxd|BEQRiB{oj@>(+b+8?titRawxe(hYvqIEhEowfNKl^xg z<5!8sw{|pb549-3R@86_Xw}LCp|Jf*C0TF@F-Wm{1xo+Bm3ppaUOd z%0EV0c<&SDJ@Ur zPJ=>I7$^1gEGf|H1jqHD08?5OPEAGPS$K=?de%1w*As%&!vz+KHLMMDrD>2B%jB8T z0h>?9I^$jD>z5FxNt*wWl4wu7re(l2fxO+J{cj4rp8pf!`OW`?hWVe(B#vASmY`G! zu_eT{HkPM~LW2K@?qLUjhlRW=MBgF~$s?qD+I$pwkb$1ZB6$T1!uXmR2Vtx-2QRYo z;9`^B%Q}na^7H+6r#hbHXye?H4y@mCra_Sw{303Wa^;pZ?8W40xIcG#Thn&5pQ8Os zARy6JNuJ(|$=DF`s91bSCVv7yyLu4*v>%Yl7*o;dUu`GIa_&@O3q?Y-A%+VAqpFX` zf&X0Tg=$Btu&sZIOSWNW3vDIT&1)*J8DnbfA_?Gw%e-QO?1uo0fb1b(GD{;aDdd43 zOU;9!3@e!kRjKvAgoMZu;Lgwh9YCN>NMuw zp95Ym8`_$I>JokqkoFsTt0<<2j;+-1p_Jh|5TD+v5iUS_l2{+6{>~TJQw=M1!UAdU z68JfKVq)oS)Rxoi=RRsJlwc6f=dD(>REo zSS$Ra(^td6D`&HTq4A>i+w~S5P>-}aHFZA`whV<<@LL8~6#Ux)eKzhGOe04CM8_6t z?-_zaP*rP?elo)P;OAm=gu0s^ZIY5O^C#&j3pDm#9{liV_~vRgR$tV|kQ|q-+p2kb zK?!8{1ITNjc{*XNkxarrmLZpVoO?DGut5ix(AzOBA)Y@AKY@k;<}L?cQV$|QeR>(1 ze-^!p(j)9*jg;AO0HJ<9oQpEDDdbiZNAh?km%7QnL2}2X)(W`8@wobfkClRM5;Z0> zk=$WfCiulKXb?#JX}uNwL|%yb><;#>6bUL?IOvmv8Qq_8dtC)uE z_#y=yL{Pj)h=xmsfRnSpY$oPW5#X0oO@>qG9BrSKssOqalGCGO8}7g<1H>L3{h@tg z`RCPu=a=K9<^;pjP|x=RZ5WWCYLrFQe0epJSOKGfJQlr7l|#Dq-8Z;P9myJ?%|G;C zejb3SInXpN@$tqz$&(uO@xBzXModz)6v3R3{^ocH>$vHZu50;Inr zS1Hw1oSj0O66&$>EeF0+*~%;MF9jb_)bU2RSbSTt5{-B4*dcEjg_(~I=U${({s`9E z8rfiQ*Gk9J9>LDyN*Vavmo6Vk*Pp=Ts3H$iLE819bmB$VeSn??dUz3S9&X_$zTYdb zxKD1N6@k8Z(Nt#p_PXhOkGz*J+6Jww56bk&myOzg-wovLLGr$oE#MprEy1lQPBGqd z<+LSu8ww!Q^9_Gmf@dGeC$VyZ5 zH-`%!ZaVzOo-n;ys&YeiB9`;=pR^NEaqLR0)BWNsFfx-4a7p*Qqdu3@7}fc{cBHM( zHMi`mAdj;PnUEmj_Q}{KF&(Yx1{AmdugyJiLgK^b7 z+5vZ(N4uIkn{4uEC!|5_U>gm<7r*k4Y6ImH&J@c1CMRDC6BETh;nZsgGjj0e-VR}5 zLl|*X_*{f^F9o5GA3vmf$X^=Vih6cox+P7C3{3^+NK+w-D>WstQdX%rp#+P<#p2 zV`4>bAJGe+ih{No8_jLCT5_N2cnU1+zXB{AaK`2lox^&S_WZb-wV3f+%5 z$lIDUi=oat0UDyTD?)UNu9D_pY6L8)j>oc4Kc4jk912y{GN7ai z>wJ0D!(Oj~1Qb{8v8uXY^b1p}qB{)XJLuITU-)$THm6lIy&YIr9lJ5GE*`H~-cynr z|6LVDZ7ka5q5Z;7yXs$OygP(bOhB<&St&)2l;kuN@}~!EL;Un?7}YUG1W9w@4EFJX zge|Kfz0Eg-*d~UXdKJUGV_S8kb`m-Y)U(h~Fqb-lcCe)DY0?=<3I4MMv-m44 zLuuQq_psLXt^vvBv1@`DDQ@u`iYYj%R2@goIsrRASC=^=}%s~U3 zxQK#bLmq(BjN&%C++nR9XQ~&bq7kqfgH@)3@<79Avb0pqf~O%wHSDYNutF?@00nhK zjUr!bb{t|1u@s_d-{JhrXmBbtE>&ecer*<8T?vB2;p}dVtymrzwE>Daa49J}HyZp1 z5767e_wcJmV^rW!p%C_Aj{#fkuDBSnd^H90w0i#(T;2jxR|MNof`96X$z%cL=Q)g~ zVXJu`R=+@`&sf@MXcGh|Mcyg5wSmUM$f$B5fKt>OvH|T?MiF5)zGcdv9*u{R?R8W% z?57&V4_UKckPB}v*xrf?NkwkNCM$1|Kcp5}4w*DXYI7Pn#sF-Xz|oFvjk+C05)vu_ zHX-4!23HJ0;@Y$EfFwk=NNw_^xwh%1pGYM40Jfd81tAP%C8<3)M>;P0og^-|!M5=0mvM8o3~xnIj+mg&r; z)cW9Vc}`Y{nfkU7^B7WW&$|FDwQw)v;XLfkX zAyEX-D|PZNZVu)HwE(JZ)$68Hm*JEg)=Z3Gxm&ArG<6g-%NQsU*0tsWH>-4sj2>j;e9G0BDx^bJz*y!Bo z%Xs_wfTb|P-iD9S{vO1zDtdT-E2ga_tL3JWbtpeJ(q3Z4j)!Hl)QahC(dq#RjTJo< zW%G{&0CIXNfGy^IM4|W#-^KYo=qZ6hDi55e1}!!*)A(-xs<01`wUjEpcDT z-6|{@X315+j@`kKz#dW(OvC*?azBygKGdGNoRVUS>tpbuFI&;SD;!J)gf&4l2gx97 z*fVPHlQAzeEh7gjPMUPmi9e9)mcbeMI(Nn%*5oI^s0R+iDoS)-)4Kcm2CAc1ESb~L zCr}ajI^IxP8GWK6y1Kmv46cgiANVLZ76j=Pvd(HUhaocqM7e*dn0tOg6Cfl!*b72Z z0Dsa5NeC&?Q9Cgq`WhlakdTJPSt9}s$6S*6<*0j`&X0@8}`Uelh}hd zl@Q;t(G#$RX?p3{=sTNX`={75$5xE17+)b9rN#`4KZYR~s2cffb#x8(NK{4J#}R_E2%hS3P!-apP2IOl-!!RnhM%3g+){5a{iI%B+k&JvRCh zMJ@ND4e_y&k9*G9*@`^b(~4b+@XyCNjR>2R1M2P1@rk;mFCrrUs`)Cfj5^RCE;VnPY~*l1^qZHa|<#)_}yoY zK+HwTm|-7{ovg-0zo?FGsEodCMPF6#!$M;4kHJwZ`m{P0VWq0*x=L`Sv|>%z7`#QRUwJ>%~m4U1h zdq^_TVs9(9-!6aSQY5wSahO4xTk%onSpKevlT<~YjSlz|WDsn>QnCtW9|3t{RK0rk zmyLa_*-(1rNI|77;zdvWgCVwp*}GEafIuCPg7Hzx^>QnA#5aOR734V#-63i43Dw#O zpnrB8n+nnQ)H%e5#8c4_gj6&B;|h7xgWQ0o!pj+uVm_2rVN#)+R*LUqqpeZ<0ET9s z)E>6lkY4~4P3@bc_Uu-vPhhh!J2)T;D&I6Us|+OuTx?Vaw1xrK`Xa%>$|0_-Kf9Hf zgNT2j5VqNAk_#%I8yj8DrQ>i#^qF|kpq~26l{wfrsXLTIl=8tLy5R&-niL>N>PeK< zNhfaR&39d!Gg+5ldvO{KqWfQf1DnPoko}(?)%oH&XQXJ=`2iE6 zuTpQ$;I7kS)%h^|pfpE#l;*SZ6O`tSJ0vU3-!df@oS#_kd`Y#H=Bv_KSCo`Pob>x~ z(U;YpY|NuMPdT3B^rjMvnzU&d73lZUnxsFE`md0JUre1@Ha7a|Sgr$nijA;pEj+g> zn0Iwm(dVjQ$$-1dt>`~k4>~SSJ+eIzfqNXD-AZj|iLT&o(V7Bl35=&M&Yu%|P!_4s zGDZ{BCpIXEz^WZi0&M2@ZFM;w{i5bd@<}O(}tkqSK z3pvrcj}ZOf4XAIm~6~W))hkvFF@|$%21v>xq#C*0_g!a(64}9D+P!a7vtDKN!l+limt4-ugNP-C=EPC8Y`afCuG zgs8-#>xZ>2S_NtN>~308OhX)K6-Yw8VxzxmlHx?qF&}E#juRz@z@{=w;zTof2Yq7N zq9SJqEGbTOzG-beP^Wz=t)h-ROKi5egf56^IdE9xd_C?yyBlwz%)(7kAP~lFGj35( zaJQk&#dEyqqvv|bHWh|N!a6JxMmzuyaEL1$$iT49i*7yRSSwMNc1&HoXgO|CrV_qK zC3fx+Kn3QZzM8QCjvM|P_$L_Ga0;MFzw(l|`su|py_O3BPSfCi-^M1LQ< zANhiN6B@LDTPR^m!MT!TZ^a0?n+kN8#chOauHp{|A_CNNV_~bgRb-Cbd|gu5K?h|*4KzT1t})6 z{Cy|l2dBXIg_%MJ_aoy+!j2c-Aue-D*%@f9DcDRF253(UlE*w zQcz}y2u{Eq8^<{cD+_L;={fSr)Tg#8Yi za&DuXd|;imvS+laf2iq`GtQkdw%-yvv4>7Pw_(cJ{gzNW0G<90z5F7sXrBJ~Bz7X? zipGX|8;F5C<=lodQXo&RsIGJ%7dnvXt_b8p=(Id)Ol%&{jKo~-enmX=0!(BFtDdr5 z6Md39WZlo>1*2NwmN(S6&vjTvYY4l~Q~6xxKG*c>8E6Q(&vkq*bf2fE8(Q+u| zww#vW>A103(bMLDVSoQJJEw=8BmK})*`MLQ0lhDpxDcjmm!#&{wR`Apb>RxQArudA z0BF5>0MY8v5?qBFyKxpO8-h1!=_54XhXz*!`+uN?DjMvIyGUDZOE3pF$yEY*fO~05 zY9Uh3KnLZlvX;CZvihy!Mtq2yj6iHtUen8ptnbx$DE4K)-;*HCHz=V!`nQy+kQLkU&Q~$OVx#8ZY$9vqo71-|*ctrk$^AjS z;ze71O5&F0A!k}UJzB2Sgu3ce@t|3SVwkUjjSwYBi-AP*zfp!;;> zKa7-FqxRhw?qI>}APj`%!4f>=sB>`-a_qAs^AElcf&Xp#Nn*o!cD!p|Nf{kN?5O?{ z{sO3g101}6@;OFG6JFgLWKH@$3`y< zRqK%diFlb9kp#9BW*dmNR+UupQTDsY!7ZZGVnv@w)63Cxyl4vAl#0<;5Yxmt@$w9E z?|2Nzyk0PSCW0^Y?r})(Ik?4)AO7+QJk;Ug`420jPes1&QE=JOc#1zIVcXLx4&0%p zI+j~gF#8}R#zx007Ec6|Ppdp|TxCr|@6*P#6jnr^k3R9$s>u4{g3Go+p(u7ZRcvF8 zVi>vL@*q-HR94lT5;y}az}>iW0~66TxT`%ma1I9n`Up=GPYvwP-@4(*$M31tfn)ev zUlXY0@7bdR`|@|{@IV=V8}`=eGphnsfzw(lt1hDS*QiR^%ereDcZwJN{%?9jZOx&0 zNS-c&w}GTcbZL7%1ah;8{ErGm;*5-mOog(8799S%qXp}j0-r2QKadhrM*%9AfIXE! zOvnCNq>M!v)1-uPAq;6Drs+7Vg!L7|;ZQD(=P*utcaK1t)e+r$@^E2CpB#lZqItZ4PW)qNnB8Al<52@Cl`$!P~-yp3YYfyOr0n|d!?YzjtD(27+*C*f$k`USm1?G8_&`c2;O z4JQPHLZEj>yy(StS#@fzX02%Tb5<1FXkjuviu6$xzZ(ix>|nC0V%1ob58j58zyN~f z!q{qbwE-XpcSTvE8tZLn4<{u^a|H`t;4j)^jc~l^WK-j>6#r=T3iQ>2s+PtDQef69 z^k}?j6v{NCWAG6+mRW!mjDlvd5~4X{qhIm`ws=vo0W{Pu6dONDng$LN=x_{a^@@a7~M6^zl)Ur-#w z<;M0lAQv4oi6ZknZ(p&NywfvYv|thEo~4A2o>+t@OJ<^i=6g_zprkXv6wW`M>=&x{@ zHMo;b_(AtCWT{Sm^y2X$r(l4_hnyP(0a$O_W z)pA`XS6zPnBZTvzT>mN8XXV-|*9YZ#hg`3f>m0dWAlI|ydXijgWnMy{*ndXQWnlWU$_HU9nOehax4%Jpc0GeGVK%2m_5o!svr*PY}VlIvi( zmdI7pTPpXv%XLq=?k(3cxek@nyoWlk2H+Jyx!kTt~=t zPr2?S*Fw4Wkn86U6aM>heMPQM$@NjW-Y3^vSMSI=`0uJ@Kneol@@ z|L6kU-C#Edx;fCzfo=|TbD*08-5luVKsN`vInd34ZVq&Fpqm5T9O&jiHwU^o(9MBv z4s>&%n*-e(=;lB-2f8`X&4F$XbaSAa1Kk|>NpQeDZ!Ny&CxK8m65Sl==0G(H7%?1rRsyNvVmW!w#ov2v^u1g z2LZoj49HfPT@RYZ$EV>5-m4XNNR@`i=f6@TYm_uK8p5U$3s(-ls77Gl`#0`BSni zKQycIr7Cw$7V-d%f05QViNcTuf9m6XvzRYY-fCLH17_6m--(h+X~vO!KYr=@qDZ&f z^!lxAe|nl}r>pkWvMl&RduaC^vXO`3@uKZ7!fWNJ29zH^V%4>3Fh^Kw@ig*Qs(zo9 z{Djq+S(PtWR~2W0PyVq>f&ukOo=+8jdP~)7)VNvDdv{jlYt;qWl#k7#EFI=o#UFpz zf7@h-|I69%v)WRFvy{)b2WEp$xq1RqT;}|TJY-eARGpGl`Fb@tEBs5zfb#G9$fqT=M*j4-|xUGaqaJM^S;sc_gLKc^@gLy zoEv}oO4V<&^e+y}g5S~V8rVJc>RulMBON}c_>?6P{^e`c=h|dXnMnM~N5_ZY)zqi~ z{f#a9KY!Dx6o35_w)7I!X0a%KAE?Xom1{>-6g>@ z_CT#VFq`qN?YCZjRs7+Xs#i1DKlY)T<3TJW_1?aQYiB&Fxz?{l=u2U}$ed664m=PG zcoS`S+ZuQShGYlNijF~l*5msT zqO%&Zn6FD!@2#`bCog9?KJUz`yrpJjRlZyumQDHm%O1h}zGaRN$^QH-S09Lbk!k#> zR`tn3KS#Cm_R@e1@rlOPQ6B!7VfpB6nzs7<_QJ^j^5HwkNU56Q2f)$H`o@PJHoP7< zH_O2L7VyHVXw%?9QLQ08FZqd}Py1-gQh)L*oK_!~s`IldU#rGuRlZbhn|b-D9H!{> z2Yp@kM~(VN8X5L>i9bIe|5?R1Ytth1>!KYpw(9eONrU<6^Jk{b(!|96^tE1f6-LDu2%P%oG8qunLF z4O#GC^AnSYtBE^>PkKkY;^&m*jUO?w>Uz~9%Xq?SE`en>{$NYgtJ|_FU#@y*8J{^i z%|J<_*K*b`)@)$UbrGN6;`Q%j{bCmh!aM61w|=zViO=_=kf&L{cm_hp^^3hO2AIve ze(~-L0y4#^Orlr&%i8~4>@ysb<-z4o>-r4%^X#jdKRqeU?)nbC*6Q%5qdh%;?nNh) zKX3f{r@)`z`>B*9e!Bd*<*N+%bM7meKkslI(B;p`=m~p$=V_ImKUZK-l0Q)#rv9Io zpF8}9a9aM%5(U)7_&WDx&7Y-=?z#M_Ku_5Gx#xrQ{5cyKkv}7U|5M;kv!BV9#2-B# zp%1Te9f1t=bE*3CCtyRy`nFUZ>xWEQ{a>nLP-&UN52+T{j>-W4kUB1l^3m!#_2fI5 z<3s!5vbVD;kIB1OZTV?x9l!q4^KV$4>NDB$-Ir3z6>%w#kMa>e z#Cyl!zu-5V_tEO%asob&MaNg5*Ig9Hr$fio>0LrK>iqZ60$MNgM^zee@WFA}!&Wwx z^9!y8^Oi*UDbX|}y2y3kb#tJb1Kk|x=0GRZKHfRc_AtO>>hlDwd6YN=y>-H3HW(GYMZWn1IuswLdNAQuSNO4{3GM? z^R7YpPoBxR{AKr`{7S5=Fb?GTCSp4L?_l}A{xhZge}M>eli1CHZVq&Fpqm5T9O&ji zHwU^o(9MBv4s>&%n*-e(=;i=8z)dsVf0=P0G%3LIWt_jM|Bt=*0h24c>H{BP9BT$U zX^evfIPf%#W~#?hyQCT0q8Z8E`Zuk$Mrx$ijEKiuU0wCMyR7=>`XhBaUZ=l&-jCTV z2_4vEwHuOj5<@1t-fRaPqJ$(ggbl;L(10NV9JV)`kV%%XBAbN?VL$fwyZ4;;?t4|Q z>s6OD$V0#Ws^7c!o^$TG=l;L<+;hdfDerrUaDU#y&&pd7*VJ9K;ZyS7Qhx#5((nZf z=gWIT{MY1tPu@yj-%W*=xBBnD_a(3|fx!}(z5Rz{^?fkLKE%ER_9d_{fqe<=OJH~j zJpQ)t`kEvb$D8-Q?@`Z}0I0o(Pa6E#_YVhueMtC~A>nTi3BNTY{2N2UAN>4q@|hhH zzBwfP`jGJ3f44vXw~9A{ZnIEo)XSAlrBM$HihS_w^8x%eYj5AvxJKtb+ZpkzjnWO5 z2aSKKK3e>u}RD2_MpHwt{k{ zRO|%CC;E2+pJVtPY>bw^yx=F}A${iG+KBYm2hiUck^a3s;v;FhKGj61mZ$aRX!70= ze9eFF@5S@~0Nj*4pJB>Vl3&fgsZWf?@2ub}zq1K`ufsiK*&T}CPEczSP5Hi#ZD`2TO+bgqAJP-F@J0NYJN?_&qBPc#1@UY=&*o|kyD$Tv-{uY!&* zbX>U}7O`Ha-z-)uWxp2G8m-krqg4)C0&ZSsCSRUyHI`b%nt#4h4cdNg<-`ZaUw)xd zYBk!8#g6}CrQI!7{i~gBxzc#~c>dUjh_D#6+ZfFit1rKJ=H1^oM<72wxEYFE92d6(hS@*a=jwGnR_GMog=w8KLIDIrHK z;1rO8<5=nNZamK>qcL3c=P!!K#Y&GSfRsy+Y!Px=GO2R$G~#r8=6%Od zD46syg}oHg@!fRlNK9k(GNseSDt6dYbo@@kZ=tH*4E$o^q5ntsVG{MZeUj)heCB ztzxB<8~1NjI?Ii2$8WYOjTZg~&0;H%zyyKLa;1IrRBg3TsbhYLrtXtL*qoD^f?w@a zs#U*I_dCmh|A}tU4L)pw4n+jopi!WF2Z>W@@{O3=H!96$PzHaT>PjH`wSbWy>cbjY z6)=xH0@ANHIyQ$bTVBYP08E(d&y6cyIcRrUja7Aq@A?1A_FJ#seTT`{rnoo7y(aEE z;=V2JEpfL?#cFY(8Wd{9D(1>w=w56E0b&W=5cN_WsZPSFzrY(Rn3fini}i9fX!$2k z`cFoARY-<2Awtrm$pIA#g=V8t?*uK|!F3iEEO?4r`grQt2mX-sg$_gh5l8j|{=tO*jfq_u#wmbep;5Qq$AZ*mE z&aH-_(}vvORcI_0;FYt)s+D?BXrs=A<*I)qo;QbT(+WD>7V_LLmdhyLZNJj4Kh^Oi zu_pOmrLBq20i;{)9P#@`l@`wht;R*J7WvD?wy7EmK~VQsgU*DF1?^r;&gCE}=|ONB zo;AL-7#x4w^eSO_YoTq|i_P{jD*1qN=0EwQ@6gm*1FU+tT5Wc8@CG?YMZen)8@%coHK1cv7IuXNQ1vOB>dP9_Mfk$(a#SFKbr!t8Cs#llW9#zYG5LVR{ezqes2l;^E3mVbb1d@@aw| z)5<3)PxAEb2xmMSc>iI%xAC6En@_*^e@~t!I|Qd`FSgr3tD|F_f|tb|Gz0BU zv3;XZuC$xQ4u(2rK*zxGC)zqEMM{Lge-E#!Z7^@y4IEUY$>(|)zlXkuJe=Sb0X=dErN((xPh>Z*_YLdQ*} zeKHMCA7h4~Rcl~qSq*Lm%*wR*2B)6`oNB?WyG=Bz>+9ftX1DU1R<|DZ z`uebs19okGpX^lIrq%VlR!|BmHv?=mpv%Op-1K|Muw=V6Dx4kX+zfKX39PW0-o$W_ zM)ymd{{?gomHJYlv4HU*dP3F#KIe;-YPS{mN{xQw9R!JWV2+@RG$E~{5Kz{mTd!DRUrcmd)8u~u})JY6YxNwCI z-}*e~cd@vN-Jnvb+gvShVsH9UuGO}R^`(H3;t9(`tTa$Bropio*Jy8)aDmBRGx%V7524Djohn-7+b&t3XUz zH7H_tK?eee4?{sNxcfyj+#_*3$@;2vv(_Vp7dAt2M3aUmLqEosfbTc>Mv;xyDHT3L zz8jfg%6k8=YQM9oXW!p=u)$WLJffL(3bu7>!T+lpz zWBvxJrbvD?|FLoZFv<~Eox|1K-1y0p`9AUawMsj-TC4SjEBQGlt!K41hh;REZ$w-x zu9&)H3s&2I4kKEpcE#i6{)gqX^05h%<4x=q7tBJYOE(4woCwsWUSX%Pi(|fhPDp)J znQFOh3-?lk&65c%EEL;TWFjxR3!T+wFz&z4$P+TOkYvuRC<>(UMVf7siKTqzrKHI= z1B{~Rh;q`u)oP#sHL)y?f@E{5H{L$EWM<_SJ{jK&XxB0SRH7`tCYEG509XOjCV-km zn7mnRn>Bp{nxw1v$#+^kG7YZCfH=0@f(L2>*9EW%VE5F@rhf<_nV)9OIjE!k9lWEK zjOT1b4y`aY?Ajejz&6t4gY?;;ix=WDFsR_@I+B4D0`^zAF_?_!LY+MyHvPCrVpk)u zX;Spv{M1;oJ+ebKlC@Ke@U<3sQ zOJ){d0$!tR%XKmx01vC#GV+B{yM7%M6!M?MyM*=!;z18%Hbu*(4}hfEI|VC23Eha< zPX(>T%3{OTON<_ZVlB2MIRag0fdn08f^r(&QVZL?SYq0RUj^yQbCNF7m%X?;^|lY4 zH>G^YdY!3nSVF-*d8M-|%Y|-vo#P6QfxWaB|j6)|WilsYR%Pa%)&>Zz@`SYN~r9h%>26}`L6&(MC` zRlQr(hmfRdMMttrMU0Z5c+xgdLQk1OClnHtomgo%ykcS-tA~MjjiUllEim{PFP#W*9xqdLXJ+V5FrxH~z6#%{hX&FX!$d_mc`}?|B|-+_h1&qO zvT@ipIa%yNfe_Qz65>tLd)U`a_W7%iwEwg(zqhvh*sfO_OGOM{mTS~kDx#IEA8j_; z7(rm2!VE#ohB8I??UXvmC0Bk+#1E&4cqy2m>8*|%H0w^neo(&%FM&g}f5zagaO zJ%QgN_`O@;=r_!x>n6GahwV;t{vapxX2iX=NBYC?LRj4oUzhlFKfL@;9t}x-IliiJ zPl*p@wa{%F<-uU3i9sCn_{@X`=S&cG0KeJqv2x6bdq&)FKF~+gGcWP6JEN>Mf5*Pp z@`Z8Das&RsmnY285vv5ZU!-m7p^{&{gvNqA1&8$en^_`AjZ8oud1DDK<%X8Om(P1+o2ytYSq z0CoS_MFMtQm0muZf6JB!C86bWD&eo;`|o`T>`P!@0{ar!m%zRRdL>Y}7&kb<@#Z7= zZEnW<*e#1OE-`XCK-W<|BXW{MLS8wDx9w0Df9Iy!}u6%URg-aNdQd@KVpa zcs~|z$JqX`kIDbN(b6-Y5kK5O6oxha?Tq-EPJL_txHAAhO`fxmhcCpkv@orLFD@D&H1>rO7zn>9b$MGrwZJ*XN;wzrMRmK~Fulac5r{noJ1^28VDUQ=b4c#A7ce4K|Bj6GI)_%y!o9p)+4>iS&&j&D zkNg95s7LG9y+nGyJCWX3;a(HU!{kp|yYvR=`9jZam(t*8w}*q@N`c4mi@x?@=YMpx z^3oK1EicdgY*b#Dz8ScOY0pJ8o^0Ez)0|Ofc^UiPM&s8XfS;BRJ4gr1C?7Z9tdBX@ z+E>0?1L9NsW-h8sim+2;*BVUNpZQ3Qc zeG=dR^*2gAe9U?kBB$G{45gt#A+Ot>cNQA>&JH-?n}sDxdxO zo3!$o|EbZ+$J79PZAZ@jVpN}4zh>d~MYwK#N=x5{1K%%ws>i3}0L|ZnKb^>5aX+Yh zRl1tLe#ZFrT7F+oq;ET8e9Gru-jHCd@%BV}l0{wz$z@ zEVh8RLi=wFW4Su;GnPO0kF4YOhU6D0dwNtpzQmuENSb_BARk|d^eg}U>^BpBY3nyX4}dq zOZqG%_FMEr-nu@xIU>JX8S!CZ=|!I6Q#PK!2gfJ*pC3(LQyKBq)RV@a)JLv#Xhsqz z)MS)=7k~%w+ZOt_@y+tPC2r=y3H&xQ#t+ap^%SVcU;X;40&Rab|M_U`&(;9^wEXe@_uccSaP54h1rzIqYZqZz1^Zjq&cgJ7N&vXMIfh zg?mFhhAF>kcWL<>llZj!y$U(%!}7NY_l9^3Q~oZ@ox4J__5Q^R zSLggQm#>;K7X58NbgW*+Jm(VD8eZN)iZ|S0>^Uj$H-q}*#Uo7tZAs0Z4 zH@4WjVOqFvWC7o8_#MMNGWO1v%Ls4EttJI?=SzROydP%HJLg@&EiZH4WxRdwBK)qx z<$Gt0&lT?))~pG2);qMzJuznxUx1rXZh)h-TV|46xao57qg!0EQnK?Y)+5#vj+HrX zVjba+{`6CSy7lM!D=z)?n{*V$ebYbfhoD^_H{J33b$aik35_5)6w~obxM!z^yf4Zf zJ-zgZ%X^V{-%Y=}Y8cJ^qtB5{?~_iy-+em0G_vB?&5c8Q#pgXog>b*mO~qx{PF%!- z8IP2ggN`}-?1txHd(fw;VFJNSQ~QRo>XKI&*!YSJ1;LZ^*-E_|$QoUE&iqhsfSO=2 zq(039z-ku-K6m6EoqzK+_MRNQe@EXMzuxDC8*yN1#2k$_K~4WT*a{OrnwJuUBCI^Z z9H%)(PcPc4!+l&WU$}uq7J$GCki!e%rj2Kc5g6Jbf!Sp-0Yh-FFA?}3^PhXp7@{(U zzMu4S#-r3Iw7czQfHVED>g}taUFU=iwQ)n!{r730$l8JNE=i!;m4r1Sm_dWpBJnR- zn{Y4#( zBJ4RFx6g^)y=J2+j1)TKPoId&Qwie7B{(4__8&lkM#L>$7;ajCu^kwqs1|ukidfxo zuhVghy_d_O*x^#0qsPYCaF}nm#W-Zls>MvDnTS}(d78!^VI`90bn33XK#d;-!P zixi?4l4a={^GPU0gc`&tEhdF6aYTz>EYCv41b}|h1g7yf_U6J6MPHHSUOcsG72%7`LN&R z8^$o`kjFh7z5OxEr4EQ;WY0nf65#E?j1%6+?S56jL**Ypo@${D>1{l2F$tHXs&$l6b}{q;?C=48+2PrQMq4j*{!`YPs;q$nfE( z2QXyg>1WP6x=V-AZvQt{E(gEP%HcilvhVMCA}$xUZ=w!iEkL)Ik!99gCJmW(5N@sI z;x1L%K8-4Q49`ib`Fs)zRJ2eTN`xgbsdvGG20#}#3tDuAk7-7n`+i(X_PhCKtQ_7W z_ou$|12+9S5B$Neio1I#NPpXqdsjbr*usDEn0-I_9{c_&@lWDM`OXsWSV_%|Ln1OL zabyaKM6M(;xRc{)SRfCF`mP`^Q8XNl+F>_pY3>rQ1Hg@}W*iQy&N=uT_02f@k{x14 z!}>7hOEgE`ZxY}??YpZ9hoTum_Q4WjDbvoh+{L>P;Ho5EwP{AymW4NAJ=U-gCOGl& z)>wZnV?FW{xJ|py056f=q%&GyifBBCwauzoY32Rl9jv-8nAKK7bl}f*Uar2Du@1{O z{n}U!^}VOPBSYerfXiE{dCN9$Ag^Gpt_~M(m!_NRqlB?{agV~;De!m7@Y@8}$m#C`ZO&c}PAlJyN<8ukL$D<&tq^$My?!5E|~ z7pT9DM!FcJN0Sv5@hSlW0eJy(zUt|L4^x~(+W0vtt3>qe#(sZpPVZy7LdN##C`%WZGTjPo9r z11$wuy5Xvixl?AB0??2DB<~I69`mk&RQX7i-cY|6jzd06xV4>k6qxmctflo2c6mLD zvNP(_FTYEsq;h|&Zu0p-merp8kGD;j&zmvGLVzm<`=vDt|GU+rWV_E^V9C_T{xvMl z9&ZOJHEOsjw1PY3`|+378ou?~(*ky7b%(EKS)-q5`ufHD=VYtL@eNzcXfKhw*bAnY z=$~K4oWoPJ_P>PXd($`C-gebr;8o)2l+AiRauRpRqw~deM=PuTEv${=2Jlt|*MVYt zrPYA#W_yJMuMW@~FC*-}X`v^!h1f@v0vF;rJt{v72ko zrEpwEisHdi3o(+*K%mc+7L=_-s}Rn=6U$d91kuzK1;m+|nj2tK@M2{p;>z zO#%}I<_$J^P=#qE9y5)(56ADvHjB&Ej*r~uy+?y4_*^oWW6Ohb#WHDEK~2FeV0SlY zmoV^>wKKCSg7^!#_>IM!^AWw5+cJiE$r6@6&0UFDZAVkY3kY@9D2dOaOmgPZpk{ZW zT503L4;vX*bz!>8Jc>t$>wicbZa86XRYvFsVI4KpePa+0><(JX<~9Ff~xBsIqee&XffJwxs89K^qijlIfW<@y@+Z zb!w8~Nw*N|p#y*Bwk}`ygzuIglv6UEq&wH{lyU8My@TEgThE!&j#fIAADg`_oGd0- z8L#2Q1eGRCX&szpN&d9Fm{t9-*Tj7;NVt~coKt_2a8|A}M>QXk;Ys)Y{T-v--@!7s z+w*(!#7c*Qz%|w{!|&kR53KP$?4vmOJ>m}=-r*Bi-4>WNguIby}V|;1)=(iuU z>B%9EgZQnB`^`$vhQz1(=->V?9}T1aa(vx}dqaG*j)wrde6$<2fHS?I3Vp7nR&ikg zoly;!-KgL;vg^(dnGzbzzjJTW|kInsMT%j|~97lp? zf~b)=?@S|;=Q52+9IDZ$(aSK~)9vL-6ZfdKTHU6T=1!9dy@;jkpsXo1y|?iXrC~3? zjmWAVVUHZRSdBXstGr)W6#+>FgH9Epp%PLaI4tX7&*1`kQDHu+>KjJHNQ(l=`u=Ww zS_I#YK+V#Eup2KKmZF7<$n_Kx3J=qM86pp#IxZjdM1tvPJd0B2iJERED&kUob)4-K z|Ndh0y7m4P2D28$#jiQddyOX?0$R8XQy2pXxDc~vyid)z#G!{>;m!QO#;^Y`q1?FS@K}TmEo}IHSmry1!CQuPa_~`Cu35mafh7R4+9aUzrZCZ!@R)N8qgYRU`l~?Iw(|I zSDRNLLtPaxMxA^CN^j7pM$QJ9#sqse<#@Ri>q|0-TiPQX1s1iz${6fe`c4m!RBye$a;H^caw!?IzCj_u#rO$H8qVg)S$SLb>f+C^n607ow))VU$Jn)o>kP#O%=beoSf}K&1vI_^ml)KX<`wBQv zV08<)UMJwbrDaBwC?3=+fL22(hAM)}r zKZSEEriCfYb(`dq^ZPOUUOO1|H*{}_8=nvK(f(#z;(HwJF7u%CucP*x?d%%dw}lhU zqb!V(UJ{P@_dt(jnUC9!6P`T1Nm!xx4v6w6o=$C%Qtj$_E$l$&F*i^*%URhd){j40x@cF+(l)UXBnb;&)5&cdBrdk{Rn1_rDb+MNJZInOA1T8B>X~m zku5O0!PD2y7p`9T6X)2p!uz8V6z@N5J5|78x|Y$9O3UYWydLkbL5$Id&lJCqp<7=! zTKl=ZNBoeHj30#y=&;WgJ4JzK8D@Q5d+%ucuMfaalkdjAjmuY)kp{mtB>c{h@ZP^0 zjvs$W_^Bb`=ZAz}8xnqFNcgQG;dh3F_x`W`^1mbJ4t`qB9b6E-axE`C?TZ9V{aLGz z{%1sQJc$=?Kb0ERrv>jXeXZ3uPvXu0IZHP$d?drY@37&2N6w8U!#5=SXbSzQb&LN! zDO@YJ8k&Cl=Iqkk*kKUOL$YxyCwO*+6kKB zfYWYd-!zT15V$~v5uPVCN{Q4*#m-vUXtrA&MS+`+&M~s_2M9N&jjEvS(d5>2AC3DG z^?54TK#Whh4JAESjxLN!Kj;+~p;i+qzgFs2+2clF7ltmN-&!hGv7WKCgzl==ruvX$ zDCt^V_w2bc;V*Vu=-}e2v??m|)_tt~{lW*cUpROAwAt|Iu%v;NIVes*dFPX|7#1F} z^ye;KeT*4GcxW2EWspr_OX=Cz1jO^+hj^F1)zAXnn*ue?Rkb^Y7 zw7!`G^T@A7W|}{D?lg(wd9v9F7HIPUucN1~@>u<89!m~2krnK^`)`! zahMbVC9HR2BGIPe(9^a&K%uSFshR?haHXs9nDIV!(hL{9;kI^#r}?E4V0jAN0uFjM znvH5>2~G3*Q+6D>^~>*A<2>n&CuLl^_Ly;RN%#SAzb@`EaUaZC_=Dm;A#PvX*TtO^ zcY?1d?|WG@DNDf1)Qff;V0Z#1CakPry~VU9PI^-zt-uoS#b&dLqWDS>m zB8Wsd%c&l94<~^o$QMloSuGqdd;kCo-KB}LQFn~|^L_nYSaOWE^7MsEXT58ereB=C zaB=$d#d9Vh>M!(6SImOTGRFxcCR|YXu7$JPNRKipLc7JN1tlrpOX$668Ah9Qq{&z{ z;r=FSFb5w-Eh#QYV-UyinU%i;KD43?x|i^$p&b*Dm9Jz5DrZ^w>cJPzdi;g6ms-$) zH3t-JYlrHM6Ux~B<@B1SGN54$&F95RP%Yat8dlr4G{HlSJve9;6IFL|l1)|(nNngy znSyspS8$z33)XgvSz_?GzUhSFh<>+LgmDVXlWus~wFEgXD@%eCKKe`_@?+P%*bvM* z)(+Ze#zpy(s}xXCG^0|CTH93Xx1`&eUX5^I5~=5{4VT+#un#lK#AqYSIYZ8HQ80%b zj-We1imVR|2pxf_B+7BSw(f>%bL%-W*2kB1m5_&SK+6UVK5C@-$p`r^jt1ASCJ z`8O%P2b!3D;`8(TND(oUNrw0px7_SV{nYu)2< z7u_?NflP@f2UZjlnG_GFE#{cHX76F2FUAsm9~^1z?as;3>g&A$_-qf^7eDfwa&Hjc z)H|BNn{7UyEbZs3fcFh_T(3#Iyn->fV5$gIj-vfl4QKnrc(zWBmfoF=_*wax{qSh~ zG&ABCX1TJb(y(`Z9e{HuBYuQwzckP83BKm@uS-6A|H0;S9&gSK`6!Nc_5ZJQZUc|- zpA$Mec=zkK2~W5?|4+Q1--7$PMB&Cu_+-ospZSmd%U2rwGT^oRX6esgNr6{h3Rk-& zo@dvROPL+SZ*6+C^06-XD%UOWp%3}K3-^Y2xN=gMUfpswhC=k?R0$zwzCRURk6hd$(E7VZu4aMPhMY45^CS)dUB z9S=>N8I9jr!6(1uXWQYI>pXYio{~6+;Ww>6xHlrd-dUSo<@fq;NAe}V^Kjo!em5jO z5kcmc%4##g?`safEDzgo_iIPeBGPnEy%A6M4BXmYXxuK1%ldx+zt5br z@+2Ny=CG-5QbK?Tq-z@v4^RsTW3**R0@EUL1G70{Ku*pTqkd zc<bA9OK|3ZAd?e;;t-Zu5DeSfFeLl;zUUf>I& zAO5V`;s1II|8og_Zmd(eYcr=#iUB0iV=~J&qKm4Sn`~6}s|L4E4e>$tnZDX@8>w1*RX9)^OxEe&37UQc_y{lLj%j&W!1^agC zV68AoDAeB-#XqV(9V!`)sb|?}8|i;qQlTB!(0Zd?H;q~HtreT^ho!atmZvS9H^II7-QrhseS z4fxa6_nTo2oa^kghJSzTf!9E(>AfFT&}rA7mTHaAime(mg#=wHEw@24q;_Si}Kw4pah4vDL<%W9WhqzBOCYW zR_a@FBZIA9S~?2`pEnT{VT)SppWdCScPk8QDb;duKW=Fll>Sq=RU$9-i}y`fyY-a( zgMS|HDS=V!y)}k5>}kuMZ4T|%rtfzt=UPs+gbuO*9xr{o0YLL}_}dhsHu6icJw552 zR3VP=?#$5uaA_A|#y$1^fn=7)xZlrTd((^Wy$^q*Kgs^aT6<@`8SXxUz4k+z zf}V1bskiLy@B8sTN?+N(ygj__fxY$poM%U*yuU{bKC7Syy!D)*xn;q*uEv$aS(J_i zwBx)rybAgSjFMPl$`0+vjaGw|6We;sJe}k}x`Slo@pR7(2hd&eK90p+)j!gcuDlqSCBD5{FY&!7 zA22%;@X)Vb)Ue1a*a_FEbV|!O3mg>9vVLgV1!2WjtGJ3Yu1CJv_uAbi&xx9I%hrSq z0PuX|h1tUS%U23>vsW&kzaWPLUH>yP=gxeD=Ne%T9EYoMFKitbRPvDXH1YfRzZCh~ zm|*v@ST67wEY3Tddt%#yC*k(=cLIV`ls-}w9olrG7drjI4I2_JQgbjZ-*JCp^z%2z zw8|#AyM(*(HH%#ydJgG9^0;L-a?43(USeBEK7zE3H{lPGF@Gq`;e?e#7x0DvucmPM zbNyYu=b?f!^ZBxa9%|r0oq)HeSacrYaCaeq6f1^31}ms@`=UPNy? zmOjUyrYOwg@)6}%9MAFZix=m|ZD%Y$iEu;1jo0vn@=(BWDc4`iNz&gfCk*4`;<@Fd zUpP#~w0P_si|l&bJBAPQ2d4Q7D{q2+7>-J8<7Mw!SnSqq>CrYa;;Ub{n{OsOvMk_S zqUO*K!&!A4|3xg5Yqjk0mulwvqXm~Mk3uY!pW(v^SL|4LXbhofH1HfEp_~kzB%C4U z@GaG`hMf%<)P#+>TNnUHa}np)RO>jLau%b<{t{h`9Yaw1F}`hl)?4UT%F_Ep?pSZ3 z_{LmFr3PaS#hTTQu<@c8u^KAVVSM7ROoskuRA#{oH=iLZmX7rYxcJ8X+ua42O|d~1 z-qZ}oDxbnfHI=maIPiwc!lE^t>i9ePqT?Mbv&8sw{V~q$6cc=EIELAM@#xObhzWlT?F#QY6m3oll%O|Tv8^!L~Fy6~}KfW+^H z$A&k3!ehgnKH;_lmf?E~8~VdyrzL2^%`j9&U;=vlt=J?U z!pHi3xLvs#dW}Vx+;r&JJ$Q@O>)%7aAba+PjKHIOV901}AHfKUskSk>#KL1OyXC_P z4`7l4cAf;imp-g72|4GOn{=%&WSbn4SJ=K4) zZh{0B>-G)BnjlzWmOW(Hp^b)tW<7}_( zLO=-yYe6#+F73Pn6E8nXrxz|(p4|4|=^r97NxU&QBSVo7?)nt7xbb4A@-^XZfwXw6 zZ@ee;hQJk@l>+47wsRGV(83jO7HIGrX^Xj$2J1#!2NUpS-6Wp=$Ul}|fw$`@CZ{Z& zX#Q@;7`W%~(%iX=g^L$XUznS_bb0Q=`EUK;vqZ4|p=aUwE0?d$UR8dW4w~F@-F?#q zI9SuExNsKFFxBTDVmvxMd*SGdR!aUe6UU#K$oskD`C}*YA3FZbxcAsp?io zW9f6$_y{RnPDg^-!0SkN=SJXSv(|zb}D(2|Uaa@WC8^R)4YVTlV?afjN7g>mpxgN z(dmabclq+gtNzt%v$L14%mMPPettPkKRoyL$Bg_bthX77*?(#9GrPc}OY^o6t`GZ& z*Bp3;>yu{h!2gq@^|$!}_^IhS&|ePj{`9ojU9MpVx`e&yMeL(euZucpfzkA$P8PN2 zsP{$9zJ5;@dL>W}Kr5VVID{?+Fceev55sz+EGM^Mz?E0sTa&KsNc;{~^r`l%i$-m_ zRpA}WtC+gn463jK00V`f=6dRYs8WZ;Fx&vdO%x2}ux3KNXV{NT@u%I0I_&UT45~}V zJglyFET;76&1?X16_Jc#_$IvSMH{{sTaB9VJ;>fMxK$HrMtJQTm1d#cSnL3wi*2`x ztvcRmil3?73P+bUcphwGd$n}#vVTMe#Xwb49@VRSEz&Z$1IjF=N1G;Rf$v%uZr zp7(sUu>f=CRwS@}ff*{>Nj8r%Bcx>sq#8z`F*{k+y9{Y>6S<2^*l4I-_@dEBS^C1g zjgE1R!`%s=7{a#?Q?CBuu8U@O0m~Eqd22CUL19`HuvoDLPd4$m6BDM3VNjzD%h>e} zA`)B}?S^S?n%TBd`?vt3TAFF(yki_t}qE$qkF0 zJKP<2?0U4Z^4Es_)3S*N`K)|uRvLz2Bi@=1x0X^NGkS~IOSm9QDuygNXc0wu<7Qvr z<#I68TCOZELQ(%oTn=RxyOA$gJp}3JEd0}_a`}~G`Fwud)CXaNiv8k^JI1N(-&nMY z|1iW*tCXI@Y8DpKj2-KzPZ`VA0%WOUR1y6n+p|7h-49m(OGV>*r-t&0WWaDg!RfXsm z>3M;R!yO)r!0qwcPO1EnuNL0a92%MvWJ2vk{k~LGw|#43*sh`2UWRy~Tl6ie;v zn|_S&du>k-nU2VC^3nXqCL9w<_P$N4$q?}cyYAtVT^KpykKJdF%(I=&xR(Zhp$?6o zHo9IkrE*^=S~*=khu!bAc#1t)#$7(#%26IzGGOO8XVH0?ueFfHtTO4CoV^A4eXX(_KJEj7r5aquzO<3#QhGGi}14KK}q7kVgPXx(!LBAH}Lqi={Q>a-wj z(9r7PwgTj=?Tt&T%C0HqAz1(PASaHQHlOLX<wk|tnJ~!%%Q{DNaX)hy zEmeL}*w}ic%Q8KFO$G5oX)~F&F*q6# zw7eUb|F+SW_+NN9DgL~16-P~k6IEqJo~XMdF#lYj3?>Z)b##; z*w>RDcK*0d!}X)}N23J|u1~!Rw=d%UGQ88)a~^ptf+HU16+|NrpY1e2Mw-eY_!NB=vl9FzIGDgK(jcb)uYx!QqSR2stk zr97H~H&kK(9NLeSIncB4qdf2Jk)Ec{TkoH~b@A8qz4t4k^2_u+0rx!Kd`RmAek{MU zf``wc9xXS@*S!Q^q)+*6?~%UZzrII&c&SU|8P2 z_+2)?Zj1Y_xG7`Sw{3A#K7@Zw-1z*GepnXx>_~dFylpx1q|9G~`>uhE%QFW*%H!Z) z7_Gefg0J!aZX*6i-uYO}&Z*|3#(#Un_%{;qpFn*2F#S`B_%&?*y)S`%3G7Q?UjlIn zupN*4*nVRzWq&>m{`Dc@4>qn{rV{(1_$#)I2YoBY;wI*uRMKc4t*+ds0u zI*4Ch-1vN;kIoyXBtGq5=l)2%f18JUO*qjpa2CeWs!m~y*b*^xjyEKHUhKnac-FVl z(Rc1I+4z1z?6_v7lY~=x`Zo7Bw{8_`-Htgoa|k=R)MFU5C8PNI{{B>bRQS{xZf-Ge}7Hv{r=)%3-{r?eJ7&z&Q$}dJx~!&6pEn4g<~z) zX*8-u?jEy?%iC*mP*NPXdZ58gb5k{SGoB2CsCN)L2xk;J=@W-@>FRe34-LJgZ*@?p z7HX(q-!V&U1}mh&&CUo*<4e9TFMJ&PoL27)+mx+to0rH_8YW3b@eDeNFz{jS2yakb z4V#hFEpTDmm0B0and?EL+ph9lX4=y~y>VW5k>|iP(bhl#`m3tpa$V}>2c%rwJsK|` zVLFVyYhi)bIB`tA)onsap-&=m$X=YvFUN}m*^&SN`+XTJPJbv8RtJxOA-k@HsLdnt+d|l*mPU_K*$y=ja`EFbP zrp3iTq;< zp?;4Z&mTWFaV!ep8M!Oxr_Y>=(2hgAT+7GBG0r13=ta?M^F_^Y4;ZaI_V!S zkK2CqEKW!oWM~tYT9qbABG{;vA8x<%vBT1GqW#A1z(RLP4$pyi|0Syvt>CaN1&eHQ zrfvoc#s|Rnf&ba||9MIlevgQo&IkGE_-;(%YgcS+I)>X)e<@HLkCDfXJ@TjN)_DET z#m7A=4^RA69(VQ_->|adF?J{sh8-ctOdNl2B>2oqSLL|5NBpeuJSe!rbZhCNIniKa zpgdS!H~*J-zGaPH<*}6!KioeSZO$hU5vqrm|J$R*Ka~+b?L5wAM*O5)!t`gv|2-al zb)L2V)u#LP-+ad!Y26n0z2CC#JK}yr+}q;ze%ppW4})e#&k6S!+I!&6G;rR)>-B%W z)#x^DAmX_ibnMXs<2}8|)5slk{J`t$|B;|o530``*E7U*9L6b8r_d-zwJQ_lTbInS zi_424Cu~AVwt_Pvu;fqGB^)OSV)~yI^g|R!h-CP=0N=?;5Hzkf<9VUD5{GGfhs)XN zHXAaK3bot8J!tC6YasO#wCRJJaHgafl{9`DxYwH!gk$TQ&;Pl0d z1(*;#bMER@SQE6T$9;?$vc?z6z+Ym1hU5FgmFjVb0vzeTX(o*(P;`QV>4$K`j3zhN; z4n>72Gn^$#fOj6+Y)ObRW>hcWY=JGX7jRfB4#A;Sro@eqzTM)d;FpU0AFUxxeZQ^m z+Ap*JWq;(0KVgS?n#G#e#!ut93uMxJMd-bXZ?^BB5%(+NUK2NqJNx7ldyF5b57ooI zgwOh0Bt14Y5AJ_jf4uc~N9&KbGvcT9$5THrTKuy^;_G?QX`B?PHO)GMoHN8JLmoSY z0a+aH!wds03bKP!M4nePC$8ZBbo`X6cZgF(P9K|WAn6Ea_nxtT2pqeR0bM+NUtX{$ zVxDSCd}W;6a}PK=@c868G4!f8gIv?l4O!vzQ|S}#N_Yf~=M|Mnwv4q3P5@YAoq$V_ z8MHjbL>bj;&d!atd5?t%8%)E z@=exH$iJ6;mFvBq3ZGA`^HH zS;3R8al1}8eFDeTp|IQ#!NMEw(BH)i*Ek|XC@8Rhs9)6B=MKXg0tTfuSmWVU#h z8ud>Gt%mQ$ved;jYcPVwL#r^!!6U`?ypmm4H4D%6qxROZLG&}^PhY+g`4jde6h@YG z?IAz<@)5UF;*9OBUCA=m=gg9#2-fSA*`9aGrkyhTfz(gQQu+ z%Kl`hEq5C$+~o}YB;yO)k%(~EEDWsh4!=N>Biyn6GweL~K;ASbkv{|XZM^w#-A7w) zd$A z>u}$Z6fu8a!H@FD3m#W{IOolYoAO||FK&E3RF9^gY2h>bqc;6&14#MLi@$}+{$J^9 z{n-{eVf{(ScL(kjAy1xW@T2_J1&{n3p#L8E0qRf>`Ai~^ulK%ZcRiFO&c1tJ0&hhL zq}2}}{h%)icJ;K<;Ad0d)7HbbU$gDhurk$lZYLwY_7gg9WP8N+=lYM^_@BTx+xuB@ zqjq*G86Qjw`AAp~h&-Vtjn<{T*Tlw7@5TC*)-k=kZ?g#Y(3dv?jR5 zCipbCqo5~GmXA$NQgKKECnvFz<3ezPOZ#*hH#u5Zqy|x?-t2a;@Bw2Jl5pB{G6ugF zZJ_1ez}w`&j0cuV0vHUcRzY|d_yql#oCc_!!_QtH&f3rL&5># zScvV-)=%5=m&11+zdPc_=L3DTz1f!dSg%>9zHl&_CvaT+MYz|66NabN|FN%m%m_ zI+zdl;P!>M(xOx0pZ&Th-GrZl+a~~?B)s*F{xf{Tfp_ywp_1}o+s^#k{Mpg+Z!05y zGJegE{LhUR|5QeNp3ex7UMd#iC^h9djAx?L{rJ1nOz+B`>=Ng7NhJP>>Qa< z9#Cudu6Qqa7tumk3sOE@gyJ3Jv@gYdvi8Z7IXp(m$sf|p;K zQslm~&akM;Piwg_Z7N#nCBUOR8puIw^xQ^(eK3+H`_RF~z!3)%x}9?47E;1BNK1+P z2!OOY@v&Ay!YBTt;|7zYHt^ZgPeC;0^)pFQJ zfPA=q?f+6VPNBd1klzD;JU-rhg?{*bQRri+ZszZI#hrtj{mu3s`3LF)eV8xoqkN=G zpGWYKhkHsqhUt%###`l`R&JM3Zne_B%}Hz;{Q55NlU?bG>!Wjvl*<#s-`E#N<8LY>ewzH3!IuwS_+-hi zR{%du{=S+5Z#p}~X^l2g71pp2{+q!U+mD%lVdbOpni6*i_<%lid^$g5e3UcG<-bC@ z^)d4wN6v1!RTv$Y>nK6}^?eo}EYH_})yjkIC*?CMZhSt_N9EC!_%xl%-w>sf`?5{A zIk(``FP%1||Nq5eRc=LxUhZG9)sbbdQ=o;D4CkbjcCv^z3R znw+c_aRO_3(zlnq+E5&JvnRG9QF+UM)uwOmt8Mx&$T`3Fz1O<`^b_{|6<=fDH5oVm zErI{na{ldkIZvA8i$+kJSQvv=mJDKPsvFls6znYwWkI-Vry?Bka|nPDp5<9C4XAXW z{}-x&7TeVzXok9kZk$}V43+s1V8&q>9$R@tkfz?t&@>#xBADmRiq$1vSF&6aHx@TB z4GF3oXDbE5x&DO!!}KIP7F#hhGbk~5bAfjs|2D2EkO*S%?M8U7Ys}x7)X~2Li@gmn z%{iy?xzK_lD6fK$X{KE(jpm%NQjCd07we&R7ODsL)Xn*b`Sx?TTBM8nC6>bZiuhYY z*b?BtcZy3`Z0(em6^A_8%RgF5-s7>?N-|Qw@xn%97epY#+hPnCA-b1vVm%i=I18aM z*lB1q#cnsNP(zE{HV$)o!gv~^y&*Pqc^kE6!(0q*af(RCvUJ783|9@m9fH ze@u+Ek66{<3^ZrHSiHT&+^+#b)bW|WGgA~?HQR%*Ip=Gox5`3 z((}5wfO{c%Pq2{087tBOe8*eBpS~x|Elv&Z7JTeu!zZxIQ^hV1*9y31upCHYjiwSQyfq6tv`uZzF(KP&E`nArpH3~(r~ z&+SoO;n?VPn=YldvPb@v-mK88l=H}UwGxkWY{KtnC0ymTA?`hhU?>#L$4!Y(uG!Y? z?8$fKclO^}`6$0r;vOpUJpe1e^AcZ@U*+fC9{JJy^8Vf4>iZs&w2$&8O5hMKAZ`_L zAVPX3>@S!hW}$$ay9#s_7IA+Pu1K;>S$*>wS1(&NtZDL!6ZRn%9`}fIo8CdN@Vxol z-i6KeSA&D?cb+V+kcwl#0pW(Y0EO$+j;CS7ZJ|BSluDJ2}P(7Tp^YMj$>-zospQbO<{EfY}AKLz` z4ZyGQzH;16h_m*Mx@;`qpabWNZQO{{t>QdHc$g02!TAGBTpS!Ryb`nd!o`lDe8gYC zun#8-^-eWcx~ebJCw>>qYxACX+)M_Fp;8RZp+ro_z`gcFABvohQW>Vz5%V+l)67-v zCvN@G5+A3R<}!m^CoqR^mUsW&X!(`Th_CT$xjBotSsz~&eC1~&BR!yw^k{#&nK3@) zTO)k}ALM`jH%F6SGb4Ul{5u)(m2Z7(eY-aRU(3B+Cy}FI-6m#NWtj1a?3?*YSl;b~ zMw+06P;t7qmT}Lf1wrlQj-+b~lt{Tq(JlAVl?yOzRG2+?=`3bopR&$zbCxv`=6}kU z&@f{8h{s=Cgfg4Y9m}4CB@jax@we*+i`C+i*+FFs654h=lVr$h>RLNnK$u#xS3sGZIcQW#clS z=l!)ka>Ly3A`-c5E<85Xi-$gWFrQ~aF_e^>Q^oio){3k?CHLp$E;{FSymFw*B&zsA z0YU}BT`z^9x7Z2E2+2~ki`QHvT3GD_Sd$J_XhMw~y-6494pq^i9mkriS@do!7AzhP zk$9O-%8 z@UZJg?DJPQ_CGN~&HEq55SN#0FXraPaRT2s7)m{DV;>Of%Q$e4d75e5SfBUh_tusl z^mP)gv2Dew+f@Zd)0`VE$1c`e>Sf});k+K%9XJhyw`2-jIpiGI#U<^wL}T{xQh;Ld3w+k} zDE%0`G5>(=-VQWBnRIirJWSo9)9dx+F(tbgGmwv7x~(M=73FX@y)VIhanvSd7#j!B%^Hgh;Y;tYGHKBd!0N8RJtteJj6Pg0K4U zpF{lmP>0|S+%w{#I2yj&JISxMfAfDNn|zYAHSVV1E5C0z{4yWC!%@B}PLftXck+JA zWdO0gJ@`j0zvTZQeq-Xs=L3DTz4awNP47=3eflte?!vtx9`_@?2gc&*^~9}lKB%{* zcTD2b^uG7WczSbi-_P{Ekx1{oH#xoDS6X>%daH@_uIw?r)Tc5hcQK?|DI4mP^;TBw>hK@X5(u{^Qim`T@DwBmI6H$72|de`Hcd{#$|3nZs1n-97%& z-9PDFM@wB2G6F`wf+N#r2rj4Bt=xn11dpVsy>?Sw)tkObeW9^3K|M2Q{M+)EGbhK)`It01CJn`sy($Lgd^sI4ON|W3 zJ>4={N%{p8; z7PKeAXjt#vb!*@(@x%~}3>cX@VNgfyK#9olG5Hho*?kFfSGo4mF-DhR3?RN9o+b}u zAkLaCRjdI4;2=}eXqvN0wwk2N*Ay8X0FxW*(&Fun!{kiACEQ)pzsLHvUtb&cq+eS8 zr1|gHS8QPX0j#f5?LFPM-LJn`!`ts~_xszzet)|^J~pFwj+ghx$7UG5KR!+yKbZk` zr1p?Hj^1@T@g$7Dnpp&OU`hcNJqj=}fopCtb>uJ_opmyvQQlKWqSFkU?efhCk9YRL|O~y7`P!@0uPS_RG;NFwD%eGh#&SpKHNJ(S#frYh;*o{wC2#EU60#v`-14w(RjamJc^fc zcolAA=n>yZcp($M82&ooeeogR$tN`2DFM`1V}H5*fzjl*Blwz*XP=IzV+wBe&3xQ+ zSfBL&Nq)6|U;E%_{H_bW^7~4H-*vcWgsoxtr9Id#HJ<9$ZhtamD)0POJ;s-259r<=>4kM?a`*gV{v;(r z+_fXSw`WikilI?In|K=o;)@|U4&`? z?`^yvguK)GkDVzi&tcQ6ZQZ?$_-XOaPmdOVGb6s)>2(sB!#De*os9Sns`(7lFZj$) z`u*;)_&D^y@yEP*K~@}b?)pkQe{=h^m6!6rF7924-G@LOZ*53?n%;{@pFS+(*Wum} zk73eFdP+y%#NPltUp(A-o*eU5(D44O%`>Bwr>z0_X?(t(!lyEr#_#;u(fDl+z)#~h z|4e_m4&rz0+-Usz1Mt)MUD+eQTLa?L^d{e0cDFO)r?oFr=SP#*tl(2F_bjM(-zrSJg{KHW^i}d6z5OyT(eYvB3EDetU$5fnxOdF*9AT$esrt`>Tr!T3nz=uxMokoo1~wN+ zKDHn*$N0!bXd4J}fKhJT`vlX#oSD-wFmB-?6|D5&couCp7&<+|LA?DTBZlVxx-Cz? z^9?qCufE5=^}emAW zHgA+_?>j4fwKFO#7|Mgp>1NeiCRa*7D?hv^0}7cfb1MPxlkTi^7I5eamlCTvSz}*G z{%vrhb|5wLf4H4=~#-XV0NG7VJipdCi$A>>L;w&!y+w>LR+iBR069Mt3H9{ z%R=UH<2g-*ukMERV z$%^nCXlPhW;~7f+gXxFz&su(LUnBJ`)GoAkx@;J4E`a8`J?RQB8g2`q5Q&16_i@zn z7|Jg46nV}^(&M6{SwwS7u_{L~9dtct?Y6{xjn@ znyWp?kOTXBt%qvgdl{Y8`Ppl%0VX2WKP~TeI5LrB1N}uh{kFL+ER3eyfW2p`ur5=B zH4#y}Rbtbd7ENkA`+9@>dV{RjvHGyDH@I&%8rq}xLoYOKUb+0i$BbSE%O2;UX>ePK-l?S1=pXn{yq*q%&jDW7V6v=3&7{CJy4g&c@?W(4&58S-#K$)H0De0e;|J)Q z`k1{j#5gAWj~aQh{Am993!~*Zh)%>=wDPr{5uas>ca*s0rjFHTX7U1l zN&b@UNizK1V4nO>1(yFe@O=Qkytu!BZ-)Eg{ye@P!SB7|zKw6fzgyfZ;yxhm>*9Vy z+$Y5Ci5t%S=l4+p%sW1_4@rKcjfZ!ZMyp@9jQBRS(f=_01MuDZtg)#WZn2=f)7E0o1VpfT!fmZ~SL~l2a_}8sV|YXxaREuvb9jn2#{g^j0I{2cHp8O0`GtF? zc&R5W9*e0{xxr9bJe0##tCi9X+&OBmHq1E;Lz%-re{uSGUw7NVhZ7v8X9$+MNI&j1 z$EKd$8B}hKm^1CJpFDYWZhG!q;mWz`v$!?++NIMMFQ56yrE}LgB<^{-bSE`-;-k>a4@ee&e=nQysv;mSD<5))5a{Zp_*R!Gc)LD7il z`wJp2z+vQn@}!e^Wh;EQJC$mc_o9c58*X2-x2Bl}L)#H1fJou3%v3IME=o3PB(a*O zY9`$#E@ulZxyvol#CET5_R`bGn1?N2#2Nn-o3{SaggtpX`;ID_tw~|Ux;Fd#6|_HE z?Rv;0OSBUs8<4#xC(N1n2kNn@ZHy;lL2_Dk-oM^J_h%X(X0T*~v2ule5jlwqu-zD= z_7WmWG(}c6Xp9_$T5$5@lTeXx`wv#Us9$q{@!7#FXThwWeZuQvRjiBEGrJzv@@`>O ztOlQsSv!m4<^P5qzkFWw*EBS3{p;?@=)A4YDT$K?KX$7Bde&jDigml_DPo1M;vEH_ zZT!DwxIb#v{;XA*RY6|DIpXPRwFg4O_06@-m$8P)70Wtu;$`H? zEv#9#yc_sFj$hmST1ex|-c9`H8fXLD)4v2b;?SR~om>m;AV-@H4%bWD2D<|(QEElV zhO47RZxJrLPFe&W*Hzn)ND25&WpL2@KfPm(W5x}6r-3)SwmhxfwC}sOWPH40-&68l zkvD1b;oJRPHFrxCp`HlsQx5uWO78ei8ThA+N@_SvGdA29FqZ&*iX)hXfMZp3El?+g zMitD*iR>f)MGJPJa={0u)}VY3YI~uy6U%~9aKiI+G@a%e$ij- z*6sa))McIUeRE+ac?Jbs7>{uc{Y--&nEf|bjJA2>J;wLu64B0TGccq6 zQqg2U5rYm01X5!#1aV=}c8^ps<}kt}rC2@UuQs}Bt*=JyV3q<=#R4{6CQ6fNx{6K8 z9(A~b`wJ=^;(+{lsLll|#ago(O#1#~?|ihhTx|IzAA_h0n&SM*`Sau7{Mb9sEEnsr zzQ1JYHp&5bQNC6oSktLvkOaY%*q;afCy;NI&MH$vr_DW5Xu!~5jYSHxh;pz;d1?mO zdSC~F@gg73r)iGmiq)u^P@G2Kssq|F=uU}`5s1Yj)ee?MNp!bpJ-tWsN*DQ73;Z+7 z0fv<4TCGMaM*j+0E4F(TwD5uFduPx_1!#e8;QJg}EAHR4&?fN|q3>M-Y!Urg)xfeP z^wE~FeQUz6h`zx0u6T2x;(IRurfpg|wyj}9paM*JTYip$Mn8_uBaRAU>Y8@c_rB3P zjQD-@I6l7IeoM)#9L`s&LA|KAC3~TKUTBoN)xf``L6FzcxkzB=p+XP!NdFrTm;Iyu z;V1^1FJU`}$p=brS1vErHc^?t`|!l^>ptGveg;Kc^J*GV%}pswR3>e)g<4p(4dAa6@o^1hd>cQM|2#99s=Dj5akzH}2s zEG#||@EsXv{%1K4aY4>ED8xb$ePydvTtx}L1>>;wrOvX3@x6fhSjT9mi1wrGeb9Rr z-!FJqp}Th0JB{(+vt}%K*1L#5J};texPo@%5^DGbK%Ntrvppk01K#s*(0G*Nt0mJS zaqoFG9F=lpnm#JUy-wi%CjN7@+izTYC0M$Qd(}S@te#tW30%SbE#08C`eG4d4eMoi zh5s33cK|@+zUp1}&LgcaVfXwBQu{K#8W>?xQjF&kTxIY~sd4y$t`rgmBDB>F)l_ni$HU#F6Y-P@^@#=!tU5`X%#HA=CV4 z&z`27AeIWJ!yiz1_x-2Q|I$s}3Ci+~?|IaF<2xRum#1Ox!1ufHoBiHLz1eje#&9zj zH6Qo)-`&6cm>Ea+P~Cc`@gI|SPxzPh`0RV^OJH9D`x4lfz*|uQo3=kV@O}1u`whGP zl3RMa(|@QY+FMZo!==gX9;Y^;bN6lN%lRAdaBMK+z|G@(-Ek{AX=)rib4&jz{7gJ_ zB%V#c^PwJr3%}KewgHrXjN|VA&)%1S$5CAC*2u<|gqM+QgB%QujRCQbWn+tkWiXo( zwu!(*VTneYErBdeMiPhw!h}_<8H5973xh+5!j{1p5c>d55QM-$fQU5{wkWIE5Fmh4ON-o4**f8WtxRsGkgQ>T`$uI_0WoFOAj6wyoJ4bdw$6F+b}_HKKM zR2gKvV*&^fgV%$wmv2m6R0jE!4%0p=*X=Ov>p|Wj@cKQtW`3)T^Yc2(5Uw)f!X=$j zhG`F#<94LMr^DH9-7dP#{8UE5;3SPO@kB3$O*_2wip|7VYhlv`MJnUaX`OTYp>`_+ zSB?69`|d#D_@L%4JBw1f1!9i|;A*b#-kkVB_iKJrVC!Am0Sw1x7KPAS8*hstp~giGfV>(IuR zpkI1$&HQ9f8Mtx;5q7hObV?bfJyed{AzV6FgS-jwA|6~bKaH$N;6)MM5c{YMH~Xw|MY(~deQD&)pq!*pu0b6UB>RR8 zo*#P@8)6@o;btF|>vov-g{l9eoRGO6;+6?%R7Q`%OCs#n-$69`| ze^EJZhj8g!;7;`aUhqi9Imn(WgBL;A)gI`SGE94{a$L#~E}e=XZyI_NMma87tR!l` zL6dhG+Rvj;nL4NpxAvoQ-44^fB=Sa3P7pHQLHW83UXKx`yrfGG(@&(|?J(`gBJU7* z{T^I1zg5>&d7WhlJ8dC8>69`|d#D_@L%4J@(X|J&KE9~5imGTTf_$9QyHW$Xz)dkpvljIp8z*924C`#U-}GQ z8eyj`#3h|lhFMQ4$L$a%QaKN)bZMqJWGKgCUZ zy!3})j~v1$dwN`S3y+X|nKU>lBTVPr^oEe`rC0b)zj*PxVK@1N4%6-s+HkCcu0J&f zCysCm;vc8Y-O39=msffH+|Fc+*hl=VQ8t}(9#p4cgX3je)}`G-$Qv4`9@K6%2FF=% zH@m-yJYIIY*+G0SJIrxQd5Je**n_Mi4wFx|ksZSu$fq`=_9?&GyHA+<1NRiG9&l^M z;EQq8PDz8;kFeYInRH4SWq8${U69?~ggnD$UPZijH`ToQRJuPsuAkg*QRL%-b% z8@xEevTv3$(!KOhSu}UhVcJRMyB&kz(-DH0{HzC`xO9-M!v?4DXCw8CUgBC8@vR3C9T6(S&979h+hN-0zrR@Jz{59r-KZnd$iBks z^SmI!8)6@o;btF|>vov-RUvNz<=`8zZqyNJWM7}b%TRcY`<}O%k;-thkIHpB65xxC zW32I!JL1SY1Rma%bEA$(BR{Dd^1O0{ zH)NlS%5d`&mAmQq-{*nqqYgakxt{U|&pu@JkrxK%-EhM@v$mo3J>cud)4$+29={am ztgqj#q2>6M9Va)`F4$}L1&eDNy1N%Fs$IOOc4cF2V^e+CisQjqP@%)SM;q(4ANB!| z&@orTroWp!ut6Spe$L46mT!vx&-TX#HR>kPCJ$`#zy^7sY|+T^zA64c+aDX$sGCTe zJmBF0-);qJcJ-{NZ<0?Sxw#PJt?_*evQ3eN^9ye0^sJ zQBL*r=#4K>oX+_cXNT28@KU4XFTu)O^#?W&< zer`_p@unTBHE%}>|Mky#K4_eO1D_+M^J0goCt~nkC>hDK?PY(M`4UFH>>gac;fJ(w z{uunY$ts`PqXO+A{^fR>y#VRee#?J3BVWr-qxA+>=Ij02Qa-;u+OK*uD}UHHAK8t| zmG%{X3(hJ~lPU_LK0)G@ZWf(=_(WCao25#k{>bOM;QL%{lhsvdb*e*n>ST4vVjce5 zdAchU^>8`<3Ch28N_6uu#@KcRYWmvEbsP6`TN6@Q)hLr}D5X)FKdd|zoX6}RVlU~e z#(D9dqraxx%U&r|p($}l?CE+_dNKbN_ z4zK6_aMxqH?z0EpDR5Rw9CEenLpL3ni?qHK7h6$v`Xw8yKW}@uE3ZGr z`XfKl`aT5GvE`*>=qDeRvQG)AK90E{$z^=O{m*X8J!pQMVa<C7me0mcy_-0t{vG< z?(dczy?S1)GI-yRbq4dS`4{a$KFu6>=C@elv1HE)s?@iIwXP=r`p?zAApgeMzrR?h{gTOQ%eMKmj_(Yl z<1CI-c$D9%+)^HoG>@M|A0j0E59sGD zA@d~aP{r{d%+UE?{*zAl7-;F^dd>ino^NrSShJ1mH`yxoNIh)Cm?*2BY1hwg7xBBG z%;j?3rZqIy;&z{qbh%fMJ|pRZ>{oLP`ES>4H2Vfat98-&it*`BKOHW7VD7+_V*XeY76My4J!NFi@bTRup;Phlvl! zM|3|&eZLgzZ7-zz#Tf1vWsG>C@MNrbA>~IOwbuybN9w~)+*ls3%dPv{3fL=sgX&55 ze;haOPf~`zcbKc&!?|vry;#@ZnkNnZ_I%y3`BIMbML{Xo(Bog1Z|C{ja6d5aA4A6Z zEAhn$8c$M=X@Atjo#5|uf&fhbO?UV6r_$_c(zP?Muzb?G6 z!8^W;^~p6s`X$wq#w*v?o||^2_2%4>YaiiwA=@BhvV?5zon_7YfnQ9v`b!q`?dLp) zeEUP1ag;jrW46the|;lu>YJ(ehGegZ%EKIGajH=V8S9dVbi`Okg=0>t z;JJzBsO=}JX;>3Zj!uf$x=06f&JL3wy2uY7Bgor|S3?)Aaf6fW!|1>G(R3Y+D8-+s*-!9fLr6=jtU+ z{|k_KWCtDHK(h4`j{8a{s?v&S8S>M=D?HoKN%I`}-fkbW&1ekw(w+3b@37~6?gw_c za-9>T^VGKl*%oUIU|r*z%lmPH)Mu-WJ}YZfx*nsyQ9Xx^ar+vtV`QGSud`CV6U7dh z=V_c%UuT{*uVO!x%Bj3twq{L_7EI(mTTKCkE%g%FnQN&`w5?C>5oDfZTg^E)vXuzAW}#1zu4>6FGXJk^gDcDmoN57ewpi? zb35lZeK&M(zW>dYbpYurMSmqet>>h^6sK`0dYvddr#)hq*iYxHq2GzTj!fi#ljnn{KG)ankAM{NeWBx1 zfL`T{ny1KjsDp0@)csi=6BL)W-6_9MKxJF$y{P*hnoo7lJ(#b10`{IiPxqfY-_iE$ z`>wY=W}8($r1c!^y#7ePrv6LgRQj`|i>-vEuwD9z^mpOWxTgEAoeEUxM_kuxqpp;n z;*!tQgMMM}u}FEslem;aDDyxCUmq*D9Jlf$zo6Xza@^cg4?djtwVJKN|Hb!b*MD30 zvrmAY?aw|mb!H8nVf3@R`F`fO0^;~?{a?q^*ediOKCKU4inM?NdcuDF&cxnB^feA{vOAv&d&L@g!Yuz>NR%8&>ey9dR|ZNzf$Y%2a;YlJ!93Y>@jWgxqO`~ zG4@eYe2(;HxLv04^YLqs)a8CHJVv?VBcmRXfxM4`sK*t&kNC_ zzGgk^B!BxK&+E$aHO|j=h8UZ-((}i@-_ZJh1*8%G7RTr9t;<^oq$2{P(8(OXhR<6k z@yFRlHmpaA~jVszGS&MxVnj5Nd&nkLl4x#**KkaczJd7PnZ!f2l z9&>J?@hN&FUF?wYDk$Sv&Qrd%$nQMQ`ph|S_;MYA?gT&>2)+~eOb+Li; zkS_!!o$|-97NWhaYQ4AB_hi052l+Zx!`IWddv!mUb=es0E`DY->o>@{1M|3k!X_Hj%cXHj(QJ@z;Vc^`Ux@kGVc}y?39WzE;0QbXJ6VAfAboPf(lJ)6SSuG#q@YdRprs{s(gXm3mRW3Wwd)E>e$eVb4-+k5%3* zpPGd}uAgDguE4 zyP!*Z_Oo4Ai~od6Nd7Y8lp}M5@W_r*_FpOd;gxRYH|HYZ%Xx{5UT2(m zR1RS;BiS$SXpnuSnCoaf;#uMBZ_qm(yIJpaP@hRXm-l_9REO0JeVw-E4En6L5J$| zM*-i~FbObPVwME(wxQDj6OdXNav{x zIt~Dmf7Wu`O5{Hq>mTuh)z8d+?bT1EU(#H;X6*NMdf3N({63iT-HXT{^KFoV@2}_m zrXSVodSBF_>rGV~RWBO%rMM@PaYN%F#@ButCz9?lr<>P|@lEGk1-i*GKWZf zw+U)?{jBKBh<3WYo>(|R&A~f+bSa0W7J>j zC-Voli*v0KToteEnFDz5mTTrwvsv>v|?{ z;PN+6kJDetSMvS}!(W->w7%NowZG0gVdMQ}*NxWy=KCyE_iAIl6@S}(fcMlSUG~^y zEn)UY(nE2&9*EDpkjh2>HJ^zm3+XANQm@V9^(J#{ztb9`6wkMIFHl>4gfhq$JaZPL zymq-xJH&3XryAEcJliUMme&gvMqJ7io*NQh>Lq=K;-yHZ{H4aYWPQ#y4sNdNO7mTs z_aTC3==!Sw`WHz3(JtNZ$~jOI&;*J{4a%-ph2(m$}lsZvtPNtS;vLu6~)IQRGX#Jw@PV09s#8O zb}Yy5;J8(ubDu=oS9~Dzkj%-nH&(6JD1Aiwj&rP0yw{lD z<-Dnf<}J}_kph|rBpL$r+P`}5P zfwG_B^K)=&H#zM2!^;nnF6i`=qzgLFOWf-`rN_`dTl0PO|CYGSJ6x~XcrGcwi5i}x z=ZbV%k00|~`PTXz`9I)tzQ#E3e|U8M9^-t<<$S+!KI)=>$T%N-Xte&aZSwXd9vpqX z$~Yf#Ip1xZ&$^uNGtNg`>>o7FXT&~wW>>AB*%fY^w=d4;?fxxmBNOR*BkL`hYq`zr z_kATUebu?pW35&(Bfnmfc|wqMow+^&!+kp6k79b z5Z9Lqe%JrMUe|N+r*G)CFxN68#xDG0&2e-slQPK$N|*OvX86<$%nSD1TS>IhwSa^*%7x{j~q%ugv%B%XVcw#{07& zt`dCblpA73EUnF=PGlQRd?>kldV#tmG^%HWuC^rQ`zPV-M5zky~;DMb5*Qoh4@j*mi{yz^79i@XE*Dlx;AmY zu-6cxkLqgrRpx$Kn+s2nd=NvwmHsU0f~2pS<_7&7z-ljw%Q&F(6-GNsJ$Tg|@tK1F3n|DmuPvhEH3ZS=Y~y>))w`zqg;-E)BM|LcLIk2rMv0Z6(& z;5el@*S%toX=4xD_(Q%gwc2?tw`O|wbXCrJ~?_z7zu$AnY!OtIdTB_~62T1KgJUY%| zdy`|>D>jL}&be`exL1?+3d{D-Uymm6eWCfg%PQmi8RmN`6^?#gjq9=XE=8IBE=88} zQd{N%x-A29HIg6x%uw?I%sJ#^33!I^F}Myu=-Ky>?u$O7^3DfUjBm0Mtjz zm*&S)Zd)!JHqiJE1L=yCWSbIvojq}dE`Jq}&c6#JOZINo@e_cQ{~jP6j{(WPw>kaN z7M*?_Q0yk&c(#vs-fGL39(y%hFVedYzXzoKKF9WZ&Zv8e{hc-Z{w6>FkG5-juL4rN zK9B17Hs8fuzNik$mls8O&vL)-tkLED3P}CsUXE`8JEZ@N@;{YJ<(cJIAkOoV`A$%kQEmid z;3h7&q(PUv5a?Ns=~vQkmbVshnTO1D;yKnzR{O8j?=WW%&if$)KUih+*rYbSgWs#V z{CI8O9YEr|2&De`CC7<7$}f)jz_K^yu$$~N*EDorvypMVU--5B@06?Zbqezsi!(O! zymI?WU7y#1RF7efpVX%7cLtD-^Ehrr@MpX_n(t_*4LgGD(>wUO`f{z--x~35?~>i} zAV%epK;%4* zw$XPq)9TkjI$`GY-okuBBW39>D-t+hmjy_QHGp6@f# zmI?Td`b-)qzLIm)N63?Oe*Nd8*XkkUsSz^peSZJzuz>de74vj|dlpE#{>Aa1ZKu!Q z4J6$U;+jaub3lsE*wbrxKZW&pk-FRSy7*Mu%`xBGzLk4B z;mPv>%rm2&(Nq5ro;*9EHla2Ylydmq-sVd_MzjA6Ioi$HPUIW%8%gVzquVKmJ&YH3=Ccjx`1*kt!P@r(GC>Q{>2-pl*3g3jv{jki+1 zp2@vAUC+!t(F*K|$`}!paXKD4_W)#HRB*if(r!|&Gw#eUJV}?h6XpDPDEWlv6?gM% zg)zrUey2Y~pP4fAn z+`m}Z%X_K4d|jgJme?UEkxH=6q^eQevMuqC>quG=By+LqISRQ4qtzmwx5$|t|#9?bTKv>(RX z%x5vz8SjT$>sOlpB#(>*Gfwj=#bekX$M@Z4;T_y*d>`KP6Ya-!u=N6dZn6U3k)-3g zN*6!TJdgR1e%rL=vooxHwdRD@@ki+RoX6Igc;B9mtAV3@O><~3=2*6|B#5>4TK)au z_@!Fczo4sv=ZT-<`7#|#_jl1nIx0{v*d^a0Q@vWp*7GN;HN59P?EtM~e<0a&ILA-q z_$dE7bjDd{2s(ci&~5tEfm-J~K+^e7ju#)K<68jfSiy0#482djMgHFQGHr7M^uNRV zMcW;$^S=!w{eS29uMW}iejpu_zop|N^y4}{#W%M^Zp8Xx8dImy@ot=d^%3v9Or!!d3WM*{=Fl0-cx{7FVk)xt@Wyl z&^!O{9QJ*!%$bh+SkJju_%injdZg?9ul)CFGuP;Oo$4RiLC<07dAePKr)d8?52W+t z3DZYboXa!kr@*yZ=Zie&$oj+NgqcG+_TlT0c|LBOe^2IRnNv)x#+-tAbegZk`X-ib z6QvW+MBWcF_rLtfeEpN#>-xXMb8G$a+&_S0)}PAY{RnGbd!4>$t@lgPhW;q)@8tR< zkJI|^1CFV`g6rair8v*?&j`f0vEpli&i=&hX7#ITtfjopOS$ zPWt-1{}bH*-r(=QZF{Nq&$ob{{*ijiUZ2=Y>7`tMLa|3scoZl9N#8K$oA{c%Z5gy( zn)|`j6SQ6X0>`wA%9S+^)>PCNX&xJ3y>o(koC#+{fAz2_^7Fh+LH1YVd)kg$fpoma zaSFSRSJFL3z5O@j%TOpI!S#HV>pg8e{ivt?ZV8n`acL*YG1gA-+4OxJ$d zE1)%i+;{Q1Le_lxeez4J>!bBvEnIA^3!lxez1=vpcBbcjRvu~x(K{XeGCB<{pFixe zN4}u3D86w+)3-AAoa=XqOCJ~Clm1fZ7oSkyq&1t@eGR2o<2(I$@0`97L3IhkY<-Ve zZr@`DX;I6ET^F%!yt2g>FBBVj965bsuH~Zxy5A0)&j%`uF=)J7pJXnXeKzm6iyulB z(Yw@JSnpEPcnv(P*Bxn$1sd<~0-N|Za0lI{$6ODP&b8mJ*Z6_8I&O|XGpwJ{&$OS> zH**=&cATt!!1lZg9dt}vr*&-&q+?%>AI|Y}IX;&D%13k^7C?U&uSI9vrS-i4B-`KO zc>CR>ZP)9Dui4Mdsvgz)j)uMi_&)55eywlKy`%G6x0hf=K5nfc ztMz-X39d8Q87!ZyKIZ4eGvC*B*cV9WsC=^zTXAVCDOZrnujcZoT;Z_~<$l2IFKM>p zJ|5rFALv=W{05ljvv4MVeNU6Ue80W*7uw!sUut`o1L-)4~66|l3;r@m08m#vDL%#kB zHXp(3#9iOf_TC2c>`$Uw{4416yQGs16|+_&sGoc73L`a54`ujuxjE&o@7_Py}kTk3l$K1X&; zpRe21{JlcfINvS#jo&NyU)8!P|5PhF8@U!mH$4++ve){89+MD<+v5W zAG1$Un)Ig;*Pv=+-7mIFn~Lp%a$e>l8Qadd)ZK|PPU$|Ox&YrK!Cvemde1`E_|~_e z$tKo48+(d#eB0qZXl8^sG=Oy4wsu;UlKGxNu2-6)f0IM`lHho!ZEfqy*gQwteiQLq z;Tc#rPq*xdgJ=7b>J#JpGSti7znA+VvJJmuaMqXJy)mDWBkFm~UQF+g&9T)O-9(KOga~F{G zJjU@SIqs&{o^z>A)Hc%g6fZ?w=2)q-SGsdf_2RJ}=l3&G)ARoEvwu{#uE$S+bliRF z82-VtG4p?{CH;AQS-x)OI(7aty0pGup1k4u5+!+kA-2!|L!JLRARUjL=B>~6BlQ{d z4Qnire@(wu7;(3;6T|tP3e>E%CHXNX`AKgqKc+^kF?TCcVeZ@VtrhG1T7AzL*}X_D zl5hS?pO$_=V;O658Jku`jX#oZ&kd4K`h)0mJOj7JMztOrkwtmmCWCk$#p~x4|I&W_ z3D9#~Sna;8{q4YgB+K%9fu!##j(@~)Iyq`>BL3wyhPgJY+%s=? z26kHqmr3DaeqLYlnXX3_kk;AH0KMwtS-#jI{uGq8kIbFKgI`CCV~R_=HkkIH_MFILHP++TvRQ9iH;8Zil^q zp7u%k*0(}S^4|&--8rk5Z!? zxlW0_R6gc6tG&o?lrHPTYL3%dh4h$l>N{@hMb>Nf%jU1^zI3O2zgv7FbvNVW8;R4{ zGQVLY{Z(uipE|y)WZ6n}mb!9Z8TF08z4LuMwX2?&vOsw(AxM3K-n${00HO4wcPqwU zr9#$U{U^7}$ZrI0&h?#ii*BcxK+4;B>t>d&oqw&N_;=7eG4h0MdEV zPsaqm%zu@-y@Ay-K&t zmB@cK)<5D0r+?IbdH*E1UD815GlEns*-RpRC$Yz9|75UMa{A{A-oIRYy{_khK+=1| z8tsQ;Z_w$@K&t;4Kq}|VKFxa`NXHvMI!|R-aUj`J&30Rf{AXkRBYtrDCmPE8C$pO_ zSD`(`FM?F8(?6-;X#e=3!|9(Lxc|Ixm#*iVK+=0)zxKnw?$POm_v-p@3#76F4{BZq zkd7Y%$v;#E9oqrPKfPkNm8$=2tbfE0_Vvg)S9#50G6$OL40A4=vSI(TH?%b04q1#} zI3?mIzkh4(pTM)iA-@#FJlrun`zRj&Kfo%`x?-vE;RpK`n`G=?2gzQ_GaNhRI~ ze|DzjyW9!+vU4bVf4&btcd0J-dk2hBj@)ZVc{Jv+4{5bosnLg>JZe+(JiYH#jo<6Y zo-HHwYkU7#`U}k;)gzz5hHLZv_{i;fTuXlx>G7 zW7M+?xla>&$3Yppb{`RYMURw2Nb@D>qj|$iuZ$nbFG%@mo|U+iBYbHiCraDm-SjQ- zo|pVLR!a}HHLe#^eeVl^z_KF7sCf&huFCL@Af-twuRyuiL%1SGx2u zsSoKPdpDMk|BpLuDf;2`NoxAhg=+eu$qK(+h)`YUZdRZ+>%@EdmrYWeufqEid+Rkk zE{UVke3K`r$xA0h3xbprd8bTNQ$CrfHiOP2^8OOiBl$^hW095iFw$3>>2DS*N_!Hr zPST;zEsb=J$Pb>gEZ;=@KVjS;%*CIHoj8xW+yx2jldp?2&B{{`pHN=HGM_3tlWK)9 z3H~?n)TO{8pDNlABt66vEcB^D=o9_3p>N_EJy(WfJl3z$^O9>B^YGl1^3?PEDtTaA z_G->2WjSr6??0j}t4>R~4$xzjb7nqXMESsb0oNa@^BKRKq+W-_am^s4!#4?Qgo=W& zRS(Kb^;&8m{+IIp5-Q`&yg%mNK1tmG$?)~q;<7(Vs9fY1-`%nn|DzX@bK&|* zeX0~(3zsq?UPS3<=5^HFF-aZ04*hoFWDD)`s^Nd)QeLk2>}@wwQx~0&dFx#J*>pg^ zE~IATH!r1)7pl@l7vP=CbFFtS>Dje?e%ndNx1XAnofw@EDX`wb$x;WvF}JWl6)xIW zCGMJ}K7@or`7z{7^UW+&GaHN5%%h9c%tZ)ejw2mDZmZec=C5%7TK~W#^%5kG`Vm@O zvwy8YUdjWU-MYDIZG7H0!}o%3O0GCtn3X{cx`-=tv*Zz!beV@p*O{D8upV*p zAH4%1wwXxhizlk$MU$-OxK_E&_*O+&$8DjeXQyU1PZvcCBa?%Z!j_3@_MV%o`EMSj z=66=B`HkOJ^B0BHeBTjj7S2;$A_dH|`r;qZr=GI=VnjEunmxNfP4-=A@k87{xvS~S zoBnR{z$OoD^1vn!Z1TV+4{Y+lCJ+2S@xYf@&(RS-Os|i1O#h{2-fE<$UCxE?(0tjK z#1^aC72gqX9&s1mzp$U{JodwV7#)R&yW0^!y3-CaJ>{am1pC3xqjj!MX?D?1&klQ# z?pB8A$-0!Y+blVo$BXd{n2wA~IpohFmwFICux_+J?`NCoyd00QkAp{y_7l}3jP!1Z z(^rze#-*Iov47}15-z+Cc5t%j9!IC8J?zPjV^7+poSNY1aw@o=mLKU}?<%B^Rqvcj zIj6Huv$R8&=vcy~9BRKlq`UbxaOddu`{e>tvksSH50;Lci+*Z9f4JCl%u&9GOF6&8 z`%uoK9DDzC)Igu}pmMsAK30E^KQOvJ6YxA})A2vc12X@9v8-6p{dFx%Rdumie^jvw zA^kHvXKlf=k#jM3Ox_XS;XogM9q&3FiTEDqqX%MK{}|6ww*hAnxV!D7)GxNhw=t3a z8RpVc@!o0-&j^d~e4-fVrlJpj58;E+$IioC{u*>FLAV+E?gV!U=98abK3@<}>Jy~h z0v-3_nb0NRoe90y?Tq|L-v?2%1!s8;|S`Yrd4&=Y_U zjzS%Ni2Of+?1Fwh8F}|Y`tIP=V?6Bz9q*zZ{V4MvNIMDo_S{pc+kvlchj)X5N?i)w zKAfZfOLz(Rt6^sw>hn$Ly9s%Zg-z3rDOSfrZh`zAQuUo;bv5KQNM%j2YJ*$}8Gxwo z7OM(K6XYVu-H?J~i`6$FS3q8eY<*m@ItcPZ$jy-VAq$T$RwqL;kfIZc)t-=6$S)!9 zLiYJyvAP!WIwV*NU6AJ?g^^;l2y!wc1$h&)ZC$ZC0dgDUkC1Kai&Z1!NywsxVs#y4 zBKH0cf&3iu9ArsTv5G;~LMALPRtq3?kV_%`kRiyl6~!tHxfzm!?A~0g+97?AEM)7J zV$}e-3F12ub%dM&xfe41q+)d(&QCFCi{Rx69uS&+vd|A1`QR;-SIv_jB2 zssL{W;YkJFRGF*_Rgo%Io2w~ms+y*zE59mHrD}%4e^6Chs4Z2QnytR3wo-G{)@mEI ztqQ2QYM$Co%~#v29n_AhT*^b7 zZ?%uwSM8@lYJas<9iR?W2dRVAA^4{3H`TY)q3SSoxH>`|iEk`?TUD#0)X{30I!1j5 z-v<7!I#wO0j#nqB@2SrE`UMrWjZO7kD|T;eZB_f#)wjnwYwO#)+8Uc0Yro&s)YVkG zvc0XPvpv=kiY*8=Hg(puw1$u`6#DM|HQ|HyKc=C*t)VLxYijEhW@tgh!cgc~XlwX> zS4*tPCFB0cNZMQ4R@BB$sa@IC+0-3sTo6J=I@Qt89!2@B?F}b|7VU||+9Sr-7wcTP zk&0?#O?8b;F%SDl-$vV3drGXO^WUYi6%MHDbnzejUIRao`#VRYiJ&=u=WV*P@Q!hC)L)Wlh)PN z*L5_pk0@UQay8Y~wYIEiLyv$+MyYwfwA!KE@))VsitT+(sBWqB;0UfbDB zdaI*1|xyacb#b(b`^L z*Gj(VjM>A}(r)Fl8ocAwwgwmTsI}3@YrnI=;`1~sO*m>k23_}bw>Gjm7;EV0>WDVA zHDbO%bB!(+dX|l#SU#|(b<#3smnV;U%}G=pJ%+xbiZSM;yygv=nP`Oa)MRAznw;$M z+}MOp+wRh*$8PYkvsrVLTgy?~H@2VBwjzeB)#%pJGn{$Jbh&bk&{5mj?s@4Ot) zO&XiptR=!|n=C(2g?LbV%pPlj@EXULJq;S;Jh^J^%J$r z@)lf3XsXncS4&%COGA^}tPa0b=_$<0($=-Iu6As;R^NsTrt5Vqf3@wvb+xgp6?1ko zeAOz~TwU6VCYL(dj;r;^OJ$FoO>65KI$KU|sy(@`wacZ2YY(lWJS|wjws=^aFVyIN zc~5RwRarB@umMUW?#M+ydeC-XjT^;UPXpTn7UMo>5X6c9} z_823)rK7!-#=t2poy{$6dZ3K9+;ZZ`@uFR*Tbq_NE9*`|Z=_|IRW}zM-&sVw#`cw1 zL)12*EAse3AH`K;LB)3#VbZm37@9iTyJ8JZ)KlrUAxO6kRy$KUxI>|ZYHhSLR$JHT z;^vyM3mUr^Yj9fik#b?^a|3tRBhOOhII|F_-*Zt-b;C!HrwlQ(|NL+dG-n>e{EuNOm1AP zJ&I^u4F15aS+phEM0b2HuCaBwP4l&f()5hWF{+OnL9F;>>4Cw~L^rRa*8SG>Yc2rC zn9w&|iE}@N8fW8jJ9baB0*qvj8sx7I_IfCPAx6LK@*4SGH!D;iO&oNq)4m*+i)fcu z*|aNAtDVlxX=RSqo#Hm-N&}9+!u$7VImA-aRlo8pt~Y5eu$DR2y__CMe52>mCgH`_ z4j;{}-X69#U(i)owzPTQ0$VNXy1v4_efufcz-VZS(xt=2itjA4&(^jy8dv+y7$z*Q z9Y(#D@)!ryeCBX5wiq`+sdHSoYNG}-bd7Vn<#NNeL0ZRJ;_ZLT#%S$|e$~56)x@Yuj}a4+n7!xmmRbeJH|^7r0bkTU)j>pLF;KZPmdzw zW&`V7dOR?O-lLZ;t&L%$tYsxF&M_-2ufsC^WL(EFwv~BS((nLf)3M0|n>?_|1Dias z$pf1_@PF9@p|=a-^iEHhG0GTcOfse!P4lexf?k{tY7CvG(O*|+m6yD}*uvlwI$aPiXjn%% z^ZJ;Vep2%Ug%@I8ih2I0xg16n*ZLyN>t|kq&kG8#hk0qymtlR3Qf?pf21MVpnwMY{ z-T?D5%u7GV^DRP``O6)%IdYByO}5LD=7LZnHOiC zw7(#|H%Lbn^Lm&k{X)Cb$dja7dU66KH-%yuUqta@Pf>X zi@tN+^;I#iNA!8{nwghip8q^|eLc)u&Ahk=ua9|2=BXZceFMzvV_wvQH^jUY^Ku@% z!sXh}{i5%D_j1dbmlk~0RgU0)6JGR(_(@S@BcWM1e(cYO)wWto@q;Po+Y zhG?0=!3!|2ka_A7cYPt|`I#5@;MFj% zjColPUX*zO=7oOZUT%VU<;+WY@KVeRGB1#D*Oy^lCG!#nm*5ey(C( z#KFUh!q!pFyfE{!9=s~%)rh{{k>!fMX68jip9e3_yk_P_FLT$ont4&?p{ zRqpyS%!@Nm{nVY8V_pyQ5+1y=7VYPR*yq8kWZr7#g)et6H^RIm^HLtX9_IBi&wqux zzCPxqm>2iprJ2{yyquHA3yuu)(xPv*qg>_gTZyV9MfPSpO(FfZ!CD`Vau^MY5o z>kBe3%e=G)uZnp?%nM!Zt}nv8oapo5bu(|6dEsl^^(C06uG90M|5|rmAM*;Cm+|1G zndfI-;%DyqGR!Mup1RJRH^jUE^Wq-7!jp8nm5aWlyS@PPf}+oZ7h+ze=)2xsUzm9z z<|RFN&CIJ}USy5CzBu#3%=7=;owu5KHOxzU@cNk-VP5zKcYPV=HA}g_7|D|_rVTMK z%DjXJPqpgy=oWo9y6Y=rUYvPx4_-O*dYG4U@bEJy>j*I~A^LtfvRvWSFmJWk*XPcQ zGB3%zln1Yed40?a|H@roAM;YoOMCDJnAgv|;7#uOhM1RTUebeCxKg*r0Q3AekJQK8 z7AnBJ4D%8myh`Q`ioRRi^@W+26@4DO2=j)Rm-FC7nU`Z;DULo@`9=r_m{LBme+M!RbN6af@Udn@~+H|`Gm={Pn^s#+q%qwSJ z!h=`Nydd+`?GAn15kkzXWM0&RSHrvz^RgbiDD$eA7g_6GZV&Uq%*%N2lFX}NUic1o zef`Xfh&~VAAoH5VzI7w@@$yvVm=_g&9=yVKp8uE^y3<`>8S~=IOMCFjnb#xw?sC^x z$-D&fQXaf8^Hwu2c(=R0X67YDp9e3_ygudy`bX-+4+5W^WbHf zmu6n%9`|xpl;=O@Wt==7mjUKwm>0g+p^teX<_$70<-x0AURLzo=dLfxydmc0Ja|3K z%P}u=zq`I9^M=K~2i$r6%u}-O{h&Km7%X#pIndfI-<{@`|W#8BBQO3MT+MQR) zya4mm!|uE)=9M!q?ZIniUQo(?#9d#4d6mq|dGPv~7h+!gQFnb==2bB-xZa&N%)Bu3 z{J(MMmBqCGYM7Vs;02i%VP4?3?)pN^YZiSD-WS;TrU6{TyeRW>4jz7hYaPwZ>lS^F zIm%^TH}m4m3l2DVajh@GydLJIJa~P~ONhS5-SwrJx0-oLCy&RuE`9vJvakRC%{RX{ z^@Ep!vj6|_2Mc9sHNKJ!U}WQZow5PcF)8wVL7pGT^8wlam;HU&znA@a*?*V)b=g0c z{c+j`%!4gX}M)dH+E62ju==?(gmU zcfRko@4xxJIWG6leBUegzjA*o_pfq)D)*m$zHgNKNB@nw{p9{n?(gD!|K{iWF1i1b z`zyJBlKUgM|B?F}xqtEV{X~LM?l0v2LGBM^{V(f#S-;ErT-M+A`kL3fvObpeudHun z{VMCz46i>Uyl%`e%KA{&e<5DC$@)#!XDMEP$@)sxPqIFe^^dG?Wc?!RlQ6F$_aP?dvmNSLON|;p?JYALaTd*EhL-$@NLDKMB5W$n_(^*9V#ZWquFx zye#vxyr&@VA;|j%@*aUdqx+G(2O!V;?_|1DibX|Az-s zuk!nzs4m^`H9xP{6LCiILhdb{Uih}g@Siox`G_Ol?T9BF@jgd9?T8OL;yFjW@b!`9 z&ve8Cj(E@!4>{stM?B()cRS(U1ygd+#6~#>-?Y`gs$Lu%X`sJkc+x2DqNdfy8 z-HsoxJAX{*U6MZIQrw7I@w~$+Yv;FbwR7Z=Ctbu!e!@uJwaLaG~+T8s!4W6!a(_?)yqMkm7gdfS; zAA1JO#4lZSKPoF@22w}&>^?{U2Pq8%kfDim#>F8v%9vpj8FShmY%)Z-9P)y7Y&WI z%e&g}1+DhB&@p?r)~&2>tgBtVsG)Js<;xo@DiFBI$ILZ33=LV~>9pt9Z{+b4T6u_PgHcYphzLj;clSY4Z#WumS zUTy6tGkPk0a>KKSxs``}b@;NtincLwy5+-{&1xH3TWDs8tsJvgxc6%#Z%AtARt|rA z06jo|HrFc6&LKH9y5%2x{8TPqzPM?@qD70B1Q#|fZd$%z5k9gUtZ%}AYS?Sx@`Yc0 z{ES_GLvTsso{JYQsc2jrY;34o5?r)=$zF^1T2RqYw^u{s!bSh-@)zy3V9D~uiyAAN z76unq)-7DVd{Jd(!-75Q7S`{%*PeAtzIyo>Z!7e-(z)&(tX%iF!Pxy~2X zC!z3_X!J?Dm$>LZhWf9;9cx;0EhidlIhnq-x1lVF~IE;pXFo{PomPOWWgI;B>xPMqr)TfwHlCJ%fC4+LuQFCieG++C!iz@I^G zxxYxIfUiNGLpTTAGNRNg2nT?NK#qQ^NQHr?K-ME12i^fW?4Ly{4g45#8p5hhsfCc^ z5e@-6Agu_;fmc9wEi6_^;N6h<2&aL6flNU-2c-Y=8%8()TmqSca0s{pG85q_@Ct~3 zCiDRR04YN_eXde(LHgIhe&E69DV0Vz4BV|psR4vTz?&db?nHfo{_~*|;Q%lWnS*cw z_%38V!s-J2pAg8d2!}3I>TpQ*9_R#aeG&fe5`}?xK~B53NM(S7m*D@K^cShfPf+Hi z@B=u}UZk(W|B^;}{HIElUanLS{1EW_kagh1fwx0$K{yRuc?JA-SCL8rKZX>7?_Z7c zkk8O|SzzgvN|hr$0NfAK2!0qCgB(pdfomY2pxhMjb%+8#2Q0q||62si?l$~#~u z@F$QUe3b+~0LiRHy@0!3t<;;OAGjKFKlG=7!;lpG0#i-kPyO2;LDKpurmkT<$CBsdI;D9Sp|ENz+Kkh|I@&pFt7$P6X6K37t&37 zfX_mz5Y7TWf~>wBb@@5|cMGH(ehUN7fdmmw0$+jz$j`w2Z-9ScX9W0b$YHQ21Kjo( zs4vP20TYm6@+WY{jff*X2>dam2jL{}Z;)LP_Wu$?43dC7ao{tMW`uLVANHXi!?q;w zB??nIaOYoPOd%Ws#vnbE7kDqE2H^}acN6@EwhrBl|DAISbRwJregcUi?7tOthYUi0 z@HX_lU!zTt9tKvV&<7BX0#CRdekL1$7pz6S5Y7M}yaP6aqt@Yn^g#M4FYsK*YU*#m zyCFfOr-6Tjlp~x4ehR6iymw-LfCLZ@0QZ8_ARGdI7ZOG|0z4HGK{yV)5>kb568ID( z1ADT-X?J1#BRv2-0@6o$ftNuB$bR56kV2$qfiv&M_(eDfY=ES}i2|>OD5R%=Z$efh ztoqS6AW76K3_J(YM|A|=N@4gs1ss4R$v?oqK(YwufW`Nq??I0rSPtn!dJuRBWC-Cf z@I*))oG7pdl0td{_)AC*;S}%*NDnv};2?#Oo&~-SNg$j9Zg(%n5yo8*`2KwuzX(U} zM;NjW{nq~g+7uF@`2lzfB#CeuIQ2o;fN%hKJY;}4zy~0G2xovZ9zwrDI0)>3DEJ`( z`~##0;T*6kjeY_xbbJ$_qRLQb|4pJ_C6Uwq=1Ok6^AtH~{<{vN!Ax zJ&OPJ1bGtaDd5-FV~kV1fX_i*L0J7pscJ|TZ4m|j7?Q%6O#okltU}%#(Dz%^8DT## z3<sNh zH6#FgqQLthZ^E}3;G}2KPhd{~_yb6eYy%$r2iOYzao}B$VWek(lb(ZK@&hmgNkL~A zcn+kS+8sCyDZ`pXJ&*sV1qmP=1zz$3e1dQic+DR%haj8+77wD&AnXUuhJ+9f05649 z5g)kKi>NQcL0}7{2H`01I!FZJ6!7Vn;46f)z_OR&TZGfEz}{Ceb{>R&U>MSha0K`U zmf%YJr2AWG6&%V z@ajLq&twnqeMl$LbHL9b^AT3BV{Af>CL4eO$ni)I0_!1rBOC?Z2ssSlG;r1%2!kI4 z_CO9qI0?KJvIv|M@M*|8Di`=A!$TbM(fIIvZa{=iAo(<_CJ;2u?_k*L}QEDk<6~YnV8pxC2 zq=9cih8}?);KA>rZzCKAu7(UFoCH1vQIDeTz_Rx+CmZIC$#$APy& z<|CX2egN4OVf6v(3t2>bU>js_;sfu697ud%!G~x&g#Ex{K1RDE90A_^3HlasfRp|S z`-lT9{2XJG!ob@gw-5(-sn4g@A)EyM9dbXys=%ktpWsvL5l#UA0(lbQ9Pp)yKJ^^J zS>UqGeCid1Bfz&HZxRO>oa9sQAshmp3Hb!!1n`PNpZW~pBygJ|pDJ9Bz5!fU>{EV( z)4(~?eX5Mofg!(7_5B8Z23}a|Qz?X#z~9b*oe2B4@u><(Wfo-t>mhN3qrjUW>t2FR z;IokX5zYcXg1m>Y+SaFzgM0>l9QbEQ`Ja#;@Tqekhy5A-ANT=e;>$>%>r(g*=D4_<@t>!(S*X47>ueE9n7#0ofbj!1g|MDkKa} z0{Az`(Mb32;8R;eZh)Qua5u<$goD6+Ah#eK0(O=A)J4yOzmrcLw2M#WUVuM=H}2|F z0SW_e+|8$=k0T7cad#gE1j4`@_du99z#D@;bq)NP1>U#-Wxa+l@Wu-GlQ_T|>E|VX zL>O4T2>k)+QQ!b%Xb|bZQ}#srLQfL-A*2%Z@-OzOTF4Zn$AO(9fyvz&6O)@N*paAmlToXMpd29qj^s=o>zD!rtgJuq_3A z0P+d?QX2SsNEz(O0QcAjWq}h0{sJ-|Hl%^G_C>oO90Z;N82~2<{5#}+s>^;p)dYEu z`X}&t$VK4ifCq)32Y!nHlaM(`PXTj~MF^|?(H9_{2#0|Q$U5ju0<(}@26}+Or5Ll& z9|Fc9XM>*rW*~E@T`28P*hBgcM_U|&@%{?zsqv||A!*pGzKeMP(g|NhfiFOwL*5+l zm}B8vgrmSmAalsiz^#u%8zDUiTnTA}{y6Xf$Q-n923Yw$*bUpVz^XbQu9?sY+`AEB zU0Z5>oyI z>;WDK8HRttz(z;_=~3X}t?(`EPXJf0^r=!t1CVQI zoC3G}G5QjXW#AQ%`0wB=;L{XF`(}Y(LV6xX-Oq&YAwBR<0$6+&{0099fj2=?uptAS za5low;|Cr9x&Jw|Bk(N9Ezck?@KFjQJp-I@4*ZF9Kd>Is`g_<3ybbau{F4TL0NER1 zbuQ*7ND^g5fLB6RJqw$Ge}&u+J?cF81d@c`g1}`EKk6O|B> zZ=o{@Ohf!+8*uvh@aF*fCvY#wst3_7z~zvO5RL+W3mHH-3!HQT@+0gAz5^+Pp1_4Z zbu8p;*cJu$K!WI33E-m;1sgKJSr;MiW3U-`G^8Ay2=D_)4eAoO*r$$!q$nMDFGS%w zk^z=n0y~i&1Re>ApbtcVDM%me&j4Sz6n&feQZMR#8Eimb3IQ*K48n#a@D0e>@Q+%B z>l&mRdcwd{APJNe2VMsWBRvJ&{HGXe&>sZ;01^Tx0sK9r8JsL|-^<}2vKjbKND7?5 z6+U$-B!K#+fJLiOM}z~wOCd?vlL9^t=||oSaOW#w55ghf^^gN$XBs#R8A4h9t56nX z3i@*x_#?z6x9e=|@|qffKGl7=1MWJOZ*F?G*uD0$GP}68Ji#2Ypgq zi#ZnZ-fQ3h`ydA*oCY5HGmJ;*i~|1)as&Eg;5whW8S*6BJp-JPM4yMvL0|`@5_L=f zS3~xuz6adwdf18n6966onF4+Un1S2?dvd@ZuYrHaZ@?kQn@_{GpZioDB!c#h1M6=< zT~Oa7@XTMpZkqRiJKPBWqrF1Ft^3eFsXqY!0l5hM!T&4x5Ymk{3Ii{P#1T#b`ypxg zKMmaLCd82*0p0|Oz@9X4`F-SxYm#jzzKKa z+JtmJ@C3-4NRI-G??T@|dJxzQxd`Dn@MB0BIR3jaz92!^83vvX=_Z?jS;%Lv!Vmoz zXOLUa$D+X7Aw6iX46x}Q^b_=%1n?h_KBW8ag&!apv}2qQax|_HIbi5M^d;~kz_pND z=-LNtx*u(Xv6BFP38_Ii@BpsSkSL8`V9|q^8>#NVV<8#%Dhj*}vNzJxz>pKaKtgnTh@!2R;Ld!{!`t|A)bc%@N@3kRJ4_3~=rvD2v8B@KQ(_ z>`4KAkD@G^D}djJWM~`#yVj#WJOmE#%-^8hF|O0VDUYGuksblwGJv@bVf8rL^9huN za1^-ecbH32uPkul?=klx90qnig*HGq4QzWF{sBJ;T$DjN=>&E{f(XZfA48G|`=7z} z9WsD$3b@^~=;sKBfDb{^2xoyu{Q-4HI12m_l0n%29Ilm+1j0$+w$Ed%AshlucmaI_ z{U-#hfpjB1LI{Z>90#t3#3?VZAJU9)8u$t%hj0!!<&T&<5DozMh72Pd2DU=72*-hc zgp{G~Ip86K@GZg-;N6e_!WrO>FT&3Vhk#c=LI|gTlV8I8hHwCQ3Zx3*1n@7AAj0Zp zpSld@Nh^1;V|$7$ZCWmz&jx|(31ha3P~b83w$5ahj0!! z_f?dIa1dArsYEymOhE?Gm(sv@Aw5V}uVGyQsYKl)z{??JNKXMjhg^fX$p0tkgoMBe z0#AJdeng*7ya|1f2<*uL=e>o#LFEGLAvIJka1ErN`Y>?P+u%b_09Z|7a3a87NHfAo z;8TzQ!dYPHU(mh?2Y}U(ZsG$khm<3n0yYof8UZ_#z+3(bA3}c)_`P@0*I{QGI1HJH zG3$R1ee8X#{g9po`aht45B?V!+HSW1n@IRImTMxZ!A z1}6!81u{e&VDV?L0qbW!@LI?Ktzm%6KF8Rnxe+-33(P4`LJ#m^$RPCrV8xfPhpu

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/9] 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 19891871..51a0b46b 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 00000000..c9305aee --- /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 d7135152..b1c9dfdf 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 fdada798..51759217 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 e2067c8b..3e9ccefd 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 4e088650..dc00bbc8 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 63eadcb5..907361c2 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 ca2fd674..c13341db 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 04fdaf3d..c5361de0 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 00000000..e72ce92e --- /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/9] 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 00000000..f4bc4252 --- /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 3e9ccefd..718d712a 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 6ee3e2a3..c2c2e6dc 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 87caeaf4..81d2a050 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 15b723af..d5545f7a 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/9] 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 8e8885ed..9eba7258 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 b956b4a9..b2cd6178 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/9] 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 5e2c5e53..dfdbcfd5 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 afde1aa4..0e06370b 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/9] 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 00000000..11e0f365 --- /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 00000000..909aff48 --- /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/9] 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 c1b5e450..d0a35ba4 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 From 7790f8bd8b9d9c168bd60a76a35233e915d89389 Mon Sep 17 00:00:00 2001 From: rvidye Date: Fri, 31 Jul 2026 11:02:10 -0600 Subject: [PATCH 8/9] Fix pipeline latency modeling in RT core and DRAM controller LatencyFIFO::clock() both advances the latency counter and reopens the issue slot, so clocking twice per cycle to get two issues also halved the modeled latency. Use one pipeline per issue slot, each clocked once, matching the existing UnitSFU pattern. Throughput stays at 2 issues/cycle; latency and in-flight capacity are restored. Also widens the DRAM return path, which drained only one 32B sector per cycle and would have bottlenecked the request side. Reported by Haocong Luo (SAFARI, ETH Zurich). --- src/arches-v2/units/trax/unit-rt-core.cpp | 34 +++++++++++++++++---- src/arches-v2/units/trax/unit-rt-core.hpp | 16 +++++++--- src/arches-v2/units/unit-dram.cpp | 36 +++++++++++++++-------- src/arches-v2/units/unit-dram.hpp | 9 ++++-- 4 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/arches-v2/units/trax/unit-rt-core.cpp b/src/arches-v2/units/trax/unit-rt-core.cpp index b65c5a09..dcb3c3bc 100644 --- a/src/arches-v2/units/trax/unit-rt-core.cpp +++ b/src/arches-v2/units/trax/unit-rt-core.cpp @@ -13,7 +13,8 @@ template UnitRTCore::UnitRTCore(const Configuration& config) : _max_rays(config.max_rays), _node_base_addr(config.node_base_addr), _tri_base_addr(config.tri_base_addr), _vrt_base_addr(config.vrt_base_addr), _cache(config.cache), _request_network(config.num_clients, 1), _return_network(1, config.num_clients), - _box_pipline(12), _tri_pipline(22), _cache_port(config.cache_port), _cache_port_stride(config.cache_port_stride) + _box_piplines(NUM_ISECT_PIPLINES, LatencyFIFO(12)), _tri_piplines(NUM_ISECT_PIPLINES, LatencyFIFO(22)), + _cache_port(config.cache_port), _cache_port_stride(config.cache_port_stride) { _ray_states.resize(config.max_rays); for(uint i = 0; i < _ray_states.size(); ++i) @@ -32,11 +33,18 @@ void UnitRTCore::clock_rise() _read_returns(); //n stack ops per cycle. In reality this would need to be multi banked - for(uint i = 0; i < 2; ++i) + for(uint i = 0; i < NUM_ISECT_PIPLINES; ++i) { _schedule_ray(); - _simualte_node_pipline(); - _simualte_tri_pipline(); + _issue_node_isect(i); + _issue_tri_isect(i); + } + + //advance pipeline exactly once per cycle + for (uint i = 0; i < NUM_ISECT_PIPLINES; ++i) + { + _retire_node_isect(i); + _retire_tri_isect(i); } } @@ -342,8 +350,9 @@ void UnitRTCore::_schedule_ray() } template -void UnitRTCore::_simualte_node_pipline() +void UnitRTCore::_issue_node_isect(uint pipline_index) { + LatencyFIFO& _box_pipline = _box_piplines[pipline_index]; if(!_node_isect_queue.empty() && _box_pipline.is_write_valid()) { _stall_cycles = 0; @@ -426,6 +435,12 @@ void UnitRTCore::_simualte_node_pipline() _box_pipline.write(~0); } } +} + +template +void UnitRTCore::_retire_node_isect(uint pipline_index) +{ + LatencyFIFO& _box_pipline = _box_piplines[pipline_index]; _box_pipline.clock(); @@ -442,8 +457,9 @@ void UnitRTCore::_simualte_node_pipline() } template -void UnitRTCore::_simualte_tri_pipline() +void UnitRTCore::_issue_tri_isect(uint pipline_index) { + LatencyFIFO& _tri_pipline = _tri_piplines[pipline_index]; if(!_tri_isect_queue.empty() && _tri_pipline.is_write_valid()) { _stall_cycles = 0; @@ -476,6 +492,12 @@ void UnitRTCore::_simualte_tri_pipline() _tri_pipline.write(~0u); } } +} + +template +void UnitRTCore::_retire_tri_isect(uint pipline_index) +{ + LatencyFIFO& _tri_pipline = _tri_piplines[pipline_index]; _tri_pipline.clock(); diff --git a/src/arches-v2/units/trax/unit-rt-core.hpp b/src/arches-v2/units/trax/unit-rt-core.hpp index 7085564b..e85cb1f0 100644 --- a/src/arches-v2/units/trax/unit-rt-core.hpp +++ b/src/arches-v2/units/trax/unit-rt-core.hpp @@ -122,14 +122,18 @@ class UnitRTCore : public UnitMemoryBase std::set _free_ray_ids; std::vector _ray_states; + // number of parallel intersection pipelines. + // each pipeline is a separate unit that is clocked once per cycle, so windening this changes throughput without changing the modeled latency. + const static uint NUM_ISECT_PIPLINES = 2; + //node pipline std::queue _node_isect_queue; - LatencyFIFO _box_pipline; + std::vector> _box_piplines; uint _box_issue_count{0}; //tri pipline std::queue _tri_isect_queue; - LatencyFIFO _tri_pipline; + std::vector> _tri_piplines; uint _tri_issue_count{0}; //meta data @@ -191,8 +195,12 @@ class UnitRTCore : public UnitMemoryBase void _read_requests(); void _read_returns(); void _schedule_ray(); - void _simualte_node_pipline(); - void _simualte_tri_pipline(); + + // issue half runs once per issue slot + void _issue_node_isect(uint pipline_index); + void _issue_tri_isect(uint pipline_index); + void _retire_node_isect(uint pipline_index); + void _retire_tri_isect(uint pipline_index); void _issue_requests(); void _issue_returns(); diff --git a/src/arches-v2/units/unit-dram.cpp b/src/arches-v2/units/unit-dram.cpp index 6ef607f9..c4a97aaa 100644 --- a/src/arches-v2/units/unit-dram.cpp +++ b/src/arches-v2/units/unit-dram.cpp @@ -5,7 +5,9 @@ namespace Arches { namespace Units { #define ENABLE_DRAM_DEBUG_PRINTS 0 UnitDRAMRamulator::UnitDRAMRamulator(Configuration config) : UnitMainMemoryBase(config.size), - _request_network(config.num_ports, config.num_controllers * 2, 1 << 13), _return_network(config.num_controllers * 2, config.num_ports), _partition_mask(config.partition_stride) + _request_network(config.num_ports, config.num_controllers * MemoryController::NUM_REQ_PIPLINES, 1 << 13), + _return_network(config.num_controllers * MemoryController::NUM_REQ_PIPLINES, config.num_ports), + _partition_mask(config.partition_stride) { YAML::Node yaml = Ramulator::Config::parse_config_file(config.config_path, {}); @@ -139,27 +141,30 @@ void UnitDRAMRamulator::clock_rise() bool busy = _pending_requests > 0; for(uint i = 0; i < _request_network.num_sinks(); ++i) { - uint controller_index = i / 2; - if(_request_network.is_read_valid(i) && _controllers[controller_index].req_pipline.is_write_valid()) - _controllers[controller_index].req_pipline.write(_request_network.read(i)); - _controllers[controller_index].req_pipline.clock(); + uint controller_index = i / MemoryController::NUM_REQ_PIPLINES; + uint pipline_index = i % MemoryController::NUM_REQ_PIPLINES; + LatencyFIFO& req_pipline = _controllers[controller_index].req_piplines[pipline_index]; - if(!_controllers[controller_index].req_pipline.empty()) busy = true; - if(!_controllers[controller_index].req_pipline.is_read_valid()) continue; - const MemoryRequest& request = _controllers[controller_index].req_pipline.peek(); + if(_request_network.is_read_valid(i) && req_pipline.is_write_valid()) + req_pipline.write(_request_network.read(i)); + req_pipline.clock(); + + if(!req_pipline.empty()) busy = true; + if(!req_pipline.is_read_valid()) continue; + const MemoryRequest& request = req_pipline.peek(); if (request.type == MemoryRequest::Type::STORE) { if (_store(request, controller_index)) { - _controllers[controller_index].req_pipline.read(); + req_pipline.read(); } } else if (request.type == MemoryRequest::Type::LOAD) { if (_load(request, controller_index)) { - _controllers[controller_index].req_pipline.read(); + req_pipline.read(); _pending_requests++; } } @@ -199,18 +204,23 @@ void UnitDRAMRamulator::clock_fall() for(uint controller_index = 0; controller_index < _controllers.size(); ++controller_index) { MemoryController& controller = _controllers[controller_index]; - if(!controller.return_queue.empty()) + + //drain up to NUM_REQ_PIPLINES returns per cycle so return path is as wide as the request path. + for (uint k = 0; k < MemoryController::NUM_REQ_PIPLINES; ++k) { + if (controller.return_queue.empty()) break; + + uint source_index = controller_index * MemoryController::NUM_REQ_PIPLINES + k; const RamulatorReturn& ramulator_return = controller.return_queue.top(); const MemoryReturn& ret = _returns[ramulator_return.return_id]; - if(_return_network.is_write_valid(controller_index)) + if(_return_network.is_write_valid(source_index)) { #if ENABLE_DRAM_DEBUG_PRINTS printf("Load Return(%d): 0x%llx\n", ret.port, ret.paddr); #endif _assert(_current_cycle >= ramulator_return.return_cycle); log.bytes_read += ret.size; - _return_network.write(ret, controller_index); + _return_network.write(ret, source_index); _free_return_ids.push(ramulator_return.return_id); controller.return_queue.pop(); _pending_requests--; diff --git a/src/arches-v2/units/unit-dram.hpp b/src/arches-v2/units/unit-dram.hpp index c13341db..a3742b5d 100644 --- a/src/arches-v2/units/unit-dram.hpp +++ b/src/arches-v2/units/unit-dram.hpp @@ -46,12 +46,17 @@ class UnitDRAMRamulator : public UnitMainMemoryBase struct MemoryController { - LatencyFIFO req_pipline; + //controller interface is NUM_REQ_PIPLINES wide so it can keep up with the DRAM bus + //each pipline is a seperate path that is clocked once per cycle, so the width sets throughput without scaling the modeled L2 to controller latency. + + const static uint NUM_REQ_PIPLINES = 2; + + std::vector> req_piplines; Ramulator::IFrontEnd* ramulator2_frontend; Ramulator::IMemorySystem* ramulator2_memorysystem; std::priority_queue return_queue; - MemoryController(uint latency) : req_pipline(latency) {} + MemoryController(uint latency) : req_piplines(NUM_REQ_PIPLINES, LatencyFIFO(latency)) {} }; uint _pending_requests = 0; From 7c36c91fc1296d9e6d498b291d69a7b07d142a51 Mon Sep 17 00:00:00 2001 From: rvidye Date: Fri, 7 Aug 2026 11:02:57 -0600 Subject: [PATCH 9/9] Make RT core and DRAM pipeline counts and latencies configurable --- src/arches-v2/units/trax/unit-rt-core.cpp | 21 +++++++++++++-------- src/arches-v2/units/trax/unit-rt-core.hpp | 14 +++++++++++--- src/arches-v2/units/unit-dram.cpp | 19 +++++++++++-------- src/arches-v2/units/unit-dram.hpp | 9 +++------ 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/arches-v2/units/trax/unit-rt-core.cpp b/src/arches-v2/units/trax/unit-rt-core.cpp index dcb3c3bc..f2daad9f 100644 --- a/src/arches-v2/units/trax/unit-rt-core.cpp +++ b/src/arches-v2/units/trax/unit-rt-core.cpp @@ -13,7 +13,9 @@ template UnitRTCore::UnitRTCore(const Configuration& config) : _max_rays(config.max_rays), _node_base_addr(config.node_base_addr), _tri_base_addr(config.tri_base_addr), _vrt_base_addr(config.vrt_base_addr), _cache(config.cache), _request_network(config.num_clients, 1), _return_network(1, config.num_clients), - _box_piplines(NUM_ISECT_PIPLINES, LatencyFIFO(12)), _tri_piplines(NUM_ISECT_PIPLINES, LatencyFIFO(22)), + _num_isect_piplines(config.num_isect_piplines), + _box_piplines(config.num_node_piplines, LatencyFIFO(config.node_pipline_latency)), + _tri_piplines(config.num_tri_piplines, LatencyFIFO(config.tri_pipline_latency)), _cache_port(config.cache_port), _cache_port_stride(config.cache_port_stride) { _ray_states.resize(config.max_rays); @@ -33,18 +35,21 @@ void UnitRTCore::clock_rise() _read_returns(); //n stack ops per cycle. In reality this would need to be multi banked - for(uint i = 0; i < NUM_ISECT_PIPLINES; ++i) + // Splot i feeds pipeline i, so the pipelines are fed in lockstep and stay balanced + uint issue_slots = std::max({_num_isect_piplines, (uint)_box_piplines.size(), (uint)_tri_piplines.size()}); + for(uint i = 0; i < issue_slots; ++i) { - _schedule_ray(); - _issue_node_isect(i); - _issue_tri_isect(i); + if(i < _num_isect_piplines) _schedule_ray(); + if(i < _box_piplines.size()) _issue_node_isect(i); + if(i < _tri_piplines.size()) _issue_tri_isect(i); } //advance pipeline exactly once per cycle - for (uint i = 0; i < NUM_ISECT_PIPLINES; ++i) + uint retire_slots = std::max((uint)_box_piplines.size(), (uint)_tri_piplines.size()); + for(uint i = 0; i < retire_slots; ++i) { - _retire_node_isect(i); - _retire_tri_isect(i); + if(i < _box_piplines.size()) _retire_node_isect(i); + if(i < _tri_piplines.size()) _retire_tri_isect(i); } } diff --git a/src/arches-v2/units/trax/unit-rt-core.hpp b/src/arches-v2/units/trax/unit-rt-core.hpp index e85cb1f0..58bddbdb 100644 --- a/src/arches-v2/units/trax/unit-rt-core.hpp +++ b/src/arches-v2/units/trax/unit-rt-core.hpp @@ -19,6 +19,14 @@ class UnitRTCore : public UnitMemoryBase paddr_t tri_base_addr{0x0ull}; paddr_t vrt_base_addr{0x0ull}; + //Each pipline is clocked once per cycle, so the counts below set throughput and the + //latencies set depth. The two are independent. + uint num_isect_piplines{2}; //scheduler/stack ops issued per cycle + uint num_node_piplines{2}; + uint num_tri_piplines{2}; + uint node_pipline_latency{12}; + uint tri_pipline_latency{22}; + UnitMemoryBase* cache{nullptr}; uint cache_port{0}; uint num_cache_ports{1}; @@ -122,9 +130,9 @@ class UnitRTCore : public UnitMemoryBase std::set _free_ray_ids; std::vector _ray_states; - // number of parallel intersection pipelines. - // each pipeline is a separate unit that is clocked once per cycle, so windening this changes throughput without changing the modeled latency. - const static uint NUM_ISECT_PIPLINES = 2; + //Each pipline is a separate unit clocked once per cycle, so widening these changes throughput + //without changing the modeled latency. + uint _num_isect_piplines{2}; //node pipline std::queue _node_isect_queue; diff --git a/src/arches-v2/units/unit-dram.cpp b/src/arches-v2/units/unit-dram.cpp index c4a97aaa..416b8b56 100644 --- a/src/arches-v2/units/unit-dram.cpp +++ b/src/arches-v2/units/unit-dram.cpp @@ -5,15 +5,18 @@ namespace Arches { namespace Units { #define ENABLE_DRAM_DEBUG_PRINTS 0 UnitDRAMRamulator::UnitDRAMRamulator(Configuration config) : UnitMainMemoryBase(config.size), - _request_network(config.num_ports, config.num_controllers * MemoryController::NUM_REQ_PIPLINES, 1 << 13), - _return_network(config.num_controllers * MemoryController::NUM_REQ_PIPLINES, config.num_ports), + _request_network(config.num_ports, config.num_controllers * config.num_req_piplines, 1 << 13), + _return_network(config.num_controllers * config.num_req_piplines, config.num_ports), _partition_mask(config.partition_stride) { YAML::Node yaml = Ramulator::Config::parse_config_file(config.config_path, {}); - _controllers.resize(config.num_controllers, config.latency); + _num_req_piplines = config.num_req_piplines; + + _controllers.reserve(config.num_controllers); for(uint i = 0; i < config.num_controllers; ++i) { + _controllers.emplace_back(config.latency, config.num_req_piplines); _controllers[i].ramulator2_frontend = Ramulator::Factory::create_frontend(yaml); _controllers[i].ramulator2_memorysystem = Ramulator::Factory::create_memory_system(yaml); @@ -141,8 +144,8 @@ void UnitDRAMRamulator::clock_rise() bool busy = _pending_requests > 0; for(uint i = 0; i < _request_network.num_sinks(); ++i) { - uint controller_index = i / MemoryController::NUM_REQ_PIPLINES; - uint pipline_index = i % MemoryController::NUM_REQ_PIPLINES; + uint controller_index = i / _num_req_piplines; + uint pipline_index = i % _num_req_piplines; LatencyFIFO& req_pipline = _controllers[controller_index].req_piplines[pipline_index]; if(_request_network.is_read_valid(i) && req_pipline.is_write_valid()) @@ -205,12 +208,12 @@ void UnitDRAMRamulator::clock_fall() { MemoryController& controller = _controllers[controller_index]; - //drain up to NUM_REQ_PIPLINES returns per cycle so return path is as wide as the request path. - for (uint k = 0; k < MemoryController::NUM_REQ_PIPLINES; ++k) + //drain up to num_req_piplines returns per cycle so return path is as wide as the request path. + for (uint k = 0; k < _num_req_piplines; ++k) { if (controller.return_queue.empty()) break; - uint source_index = controller_index * MemoryController::NUM_REQ_PIPLINES + k; + uint source_index = controller_index * _num_req_piplines + k; const RamulatorReturn& ramulator_return = controller.return_queue.top(); const MemoryReturn& ret = _returns[ramulator_return.return_id]; if(_return_network.is_write_valid(source_index)) diff --git a/src/arches-v2/units/unit-dram.hpp b/src/arches-v2/units/unit-dram.hpp index a3742b5d..68414019 100644 --- a/src/arches-v2/units/unit-dram.hpp +++ b/src/arches-v2/units/unit-dram.hpp @@ -28,6 +28,7 @@ class UnitDRAMRamulator : public UnitMainMemoryBase uint latency{1}; uint num_ports{1}; uint num_controllers{1}; + uint num_req_piplines{2}; uint64_t partition_stride{0x0ull}; double clock_ratio{4.0f}; }; @@ -46,19 +47,15 @@ class UnitDRAMRamulator : public UnitMainMemoryBase struct MemoryController { - //controller interface is NUM_REQ_PIPLINES wide so it can keep up with the DRAM bus - //each pipline is a seperate path that is clocked once per cycle, so the width sets throughput without scaling the modeled L2 to controller latency. - - const static uint NUM_REQ_PIPLINES = 2; - std::vector> req_piplines; Ramulator::IFrontEnd* ramulator2_frontend; Ramulator::IMemorySystem* ramulator2_memorysystem; std::priority_queue return_queue; - MemoryController(uint latency) : req_piplines(NUM_REQ_PIPLINES, LatencyFIFO(latency)) {} + MemoryController(uint latency, uint num_piplines) : req_piplines(num_piplines, LatencyFIFO(latency)) {} }; + uint _num_req_piplines{2}; uint _pending_requests = 0; bool _busy{false};