151c0b2f7Stbbdev /*
2*b15aabb3Stbbdev Copyright (c) 2005-2021 Intel Corporation
351c0b2f7Stbbdev
451c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev you may not use this file except in compliance with the License.
651c0b2f7Stbbdev You may obtain a copy of the License at
751c0b2f7Stbbdev
851c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev
1051c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev See the License for the specific language governing permissions and
1451c0b2f7Stbbdev limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev
1751c0b2f7Stbbdev #ifndef __TBB_test_common_concurrent_priority_queue_common_H
1851c0b2f7Stbbdev #define __TBB_test_common_concurrent_priority_queue_common_H
1951c0b2f7Stbbdev
2051c0b2f7Stbbdev // We need to skip allocator_traits::is_always_equal tests for C++11 and C++14
2151c0b2f7Stbbdev #define __TBB_TEST_SKIP_IS_ALWAYS_EQUAL_CHECK (__cplusplus < 201703L)
2251c0b2f7Stbbdev #include <common/test.h>
2351c0b2f7Stbbdev #include <common/utils.h>
2449e08aacStbbdev #include <oneapi/tbb/concurrent_priority_queue.h>
2549e08aacStbbdev #include <oneapi/tbb/blocked_range.h>
2651c0b2f7Stbbdev #include <vector>
2751c0b2f7Stbbdev
2851c0b2f7Stbbdev namespace equality_comparison_helpers {
2951c0b2f7Stbbdev
3051c0b2f7Stbbdev template <typename ElementType, typename Compare, typename Allocator>
toVector(const tbb::concurrent_priority_queue<ElementType,Compare,Allocator> & source)3151c0b2f7Stbbdev std::vector<ElementType> toVector( const tbb::concurrent_priority_queue<ElementType, Compare, Allocator>& source ) {
3251c0b2f7Stbbdev auto cpq = source;
3351c0b2f7Stbbdev std::vector<ElementType> v;
3451c0b2f7Stbbdev v.reserve(cpq.size());
3551c0b2f7Stbbdev
3651c0b2f7Stbbdev ElementType element;
3751c0b2f7Stbbdev while(cpq.try_pop(element)) {
3851c0b2f7Stbbdev v.emplace_back(element);
3951c0b2f7Stbbdev }
4051c0b2f7Stbbdev std::reverse(v.begin(), v.end());
4151c0b2f7Stbbdev return v;
4251c0b2f7Stbbdev }
4351c0b2f7Stbbdev
4451c0b2f7Stbbdev }; // namespace equality_comparison_helpers
4551c0b2f7Stbbdev
4651c0b2f7Stbbdev template <bool HasCopyCtor>
4751c0b2f7Stbbdev struct QueuePushHelper {
4851c0b2f7Stbbdev template <typename Q, typename T>
pushQueuePushHelper4951c0b2f7Stbbdev static void push(Q& q, T&& t) {
5051c0b2f7Stbbdev q.push(std::forward<T>(t));
5151c0b2f7Stbbdev }
5251c0b2f7Stbbdev };
5351c0b2f7Stbbdev
5451c0b2f7Stbbdev template<>
5551c0b2f7Stbbdev template <typename Q, typename T>
push(Q & q,T && t)5651c0b2f7Stbbdev void QueuePushHelper<false>::push( Q& q, T&& t ) {
5751c0b2f7Stbbdev q.push(std::move(t));
5851c0b2f7Stbbdev }
5951c0b2f7Stbbdev
6051c0b2f7Stbbdev template <bool HasCopyCtor, typename QueueType>
examine(QueueType & q1,QueueType & q2,const std::vector<typename QueueType::value_type> & vec_sorted)6151c0b2f7Stbbdev void examine( QueueType& q1, QueueType& q2, const std::vector<typename QueueType::value_type>& vec_sorted ) {
6251c0b2f7Stbbdev using value_type = typename QueueType::value_type;
6351c0b2f7Stbbdev
6451c0b2f7Stbbdev REQUIRE((!q1.empty() && q1.size() == vec_sorted.size()));
6551c0b2f7Stbbdev value_type elem;
6651c0b2f7Stbbdev
6751c0b2f7Stbbdev q2.clear();
6851c0b2f7Stbbdev REQUIRE((q2.empty() && !q2.size() && !q2.try_pop(elem)));
6951c0b2f7Stbbdev
7051c0b2f7Stbbdev typename std::vector<value_type>::const_reverse_iterator it1;
7151c0b2f7Stbbdev for (it1 = vec_sorted.rbegin(); q1.try_pop(elem); ++it1) {
7251c0b2f7Stbbdev REQUIRE(utils::IsEqual{}(elem, *it1));
7351c0b2f7Stbbdev if (std::distance(vec_sorted.rbegin(), it1) % 2) {
7451c0b2f7Stbbdev QueuePushHelper<HasCopyCtor>::push(q2, elem);
7551c0b2f7Stbbdev } else {
7651c0b2f7Stbbdev QueuePushHelper<HasCopyCtor>::push(q2, std::move(elem));
7751c0b2f7Stbbdev }
7851c0b2f7Stbbdev }
7951c0b2f7Stbbdev REQUIRE(it1 == vec_sorted.rend());
8051c0b2f7Stbbdev REQUIRE((q1.empty() && !q1.size()));
8151c0b2f7Stbbdev REQUIRE((!q2.empty() && q2.size() == vec_sorted.size()));
8251c0b2f7Stbbdev
8351c0b2f7Stbbdev q1.swap(q2);
8451c0b2f7Stbbdev REQUIRE((q2.empty() && !q2.size()));
8551c0b2f7Stbbdev REQUIRE((!q1.empty() && q1.size() == vec_sorted.size()));
8651c0b2f7Stbbdev for (it1 = vec_sorted.rbegin(); q1.try_pop(elem); ++it1)
8751c0b2f7Stbbdev REQUIRE(utils::IsEqual{}(elem, *it1));
8851c0b2f7Stbbdev REQUIRE(it1 == vec_sorted.rend());
8951c0b2f7Stbbdev };
9051c0b2f7Stbbdev
9151c0b2f7Stbbdev template <typename QueueType>
examine(const QueueType & q,const std::vector<typename QueueType::value_type> & vec_sorted)9251c0b2f7Stbbdev void examine( const QueueType& q, const std::vector<typename QueueType::value_type>& vec_sorted ) {
9351c0b2f7Stbbdev QueueType q1(q), q2(q);
9451c0b2f7Stbbdev examine</*HasCopyCtor=*/true>(q1, q2, vec_sorted);
9551c0b2f7Stbbdev }
9651c0b2f7Stbbdev
9751c0b2f7Stbbdev // TODO: consider wrapping each constructor test into separate TEST_CASE
9851c0b2f7Stbbdev template <typename ValueType, typename Compare>
type_tester(const std::vector<ValueType> & vec,Compare comp)9951c0b2f7Stbbdev void type_tester( const std::vector<ValueType>& vec, Compare comp ) {
10051c0b2f7Stbbdev using queue_type = tbb::concurrent_priority_queue<ValueType, Compare>;
10151c0b2f7Stbbdev REQUIRE_MESSAGE(vec.size() >= 5, "Array should have at least 5 elements");
10251c0b2f7Stbbdev
10351c0b2f7Stbbdev std::vector<ValueType> vec_sorted(vec);
10451c0b2f7Stbbdev std::sort(vec_sorted.begin(), vec_sorted.end(), comp);
10551c0b2f7Stbbdev
10651c0b2f7Stbbdev // Construct an empty queue
10751c0b2f7Stbbdev queue_type q1;
10851c0b2f7Stbbdev q1.assign(vec.begin(), vec.end());
10951c0b2f7Stbbdev examine(q1, vec_sorted);
11051c0b2f7Stbbdev
11151c0b2f7Stbbdev // Constructor from std::initializer_list
11251c0b2f7Stbbdev queue_type q2({vec[0], vec[1], vec[2]});
11351c0b2f7Stbbdev for (auto it = vec.begin() + 3; it != vec.end(); ++it)
11451c0b2f7Stbbdev q2.push(*it);
11551c0b2f7Stbbdev examine(q2, vec_sorted);
11651c0b2f7Stbbdev
11751c0b2f7Stbbdev // Assignment operator with std::initializer_list
11851c0b2f7Stbbdev queue_type q3;
11951c0b2f7Stbbdev q3 = {vec[0], vec[1], vec[2]};
12051c0b2f7Stbbdev for (auto it = vec.begin() + 3; it != vec.end(); ++it)
12151c0b2f7Stbbdev q3.push(*it);
12251c0b2f7Stbbdev examine(q3, vec_sorted);
12351c0b2f7Stbbdev
12451c0b2f7Stbbdev // Copy ctor
12551c0b2f7Stbbdev queue_type q4(q1);
12651c0b2f7Stbbdev examine(q4, vec_sorted);
12751c0b2f7Stbbdev
12851c0b2f7Stbbdev // Copy ctor with allocator
12951c0b2f7Stbbdev auto alloc = q1.get_allocator();
13051c0b2f7Stbbdev queue_type q4_alloc(q1, alloc);
13151c0b2f7Stbbdev examine(q4_alloc, vec_sorted);
13251c0b2f7Stbbdev
13351c0b2f7Stbbdev // Constructor from the half-open interval
13451c0b2f7Stbbdev queue_type q5(vec.begin(), vec.end());
13551c0b2f7Stbbdev examine(q5, vec_sorted);
13651c0b2f7Stbbdev
13751c0b2f7Stbbdev // Constructor from the allocator object
13851c0b2f7Stbbdev queue_type q6(alloc);
13951c0b2f7Stbbdev q6.assign(vec.begin(), vec.end());
14051c0b2f7Stbbdev examine(q6, vec_sorted);
14151c0b2f7Stbbdev
14251c0b2f7Stbbdev // Constructor from the comparator and allocator object
14351c0b2f7Stbbdev queue_type q7(comp);
14451c0b2f7Stbbdev q7.assign(vec.begin(), vec.end());
14551c0b2f7Stbbdev examine(q7, vec_sorted);
14651c0b2f7Stbbdev
14751c0b2f7Stbbdev queue_type q8(comp, alloc);
14851c0b2f7Stbbdev q8.assign(vec.begin(), vec.end());
14951c0b2f7Stbbdev examine(q8, vec_sorted);
15051c0b2f7Stbbdev
15151c0b2f7Stbbdev // Constructor from the initial capacity, comparator and allocator
15251c0b2f7Stbbdev queue_type q9(100);
15351c0b2f7Stbbdev q9.assign(vec.begin(), vec.end());
15451c0b2f7Stbbdev examine(q9, vec_sorted);
15551c0b2f7Stbbdev
15651c0b2f7Stbbdev queue_type q10(100, comp);
15751c0b2f7Stbbdev q10.assign(vec.begin(), vec.end());
15851c0b2f7Stbbdev examine(q10, vec_sorted);
15951c0b2f7Stbbdev
16051c0b2f7Stbbdev queue_type q11(100, alloc);
16151c0b2f7Stbbdev q11.assign(vec.begin(), vec.end());
16251c0b2f7Stbbdev examine(q11, vec_sorted);
16351c0b2f7Stbbdev
16451c0b2f7Stbbdev // Constructor from the half-open interval, compare and allocator object
16551c0b2f7Stbbdev queue_type q12(vec.begin(), vec.end(), comp);
16651c0b2f7Stbbdev examine(q12, vec_sorted);
16751c0b2f7Stbbdev
16851c0b2f7Stbbdev queue_type q13(vec.begin(), vec.end(), alloc);
16951c0b2f7Stbbdev examine(q13, vec_sorted);
17051c0b2f7Stbbdev
17151c0b2f7Stbbdev // Constructor from the std::initializer_list from the half-open interval, compare and allocator object
17251c0b2f7Stbbdev queue_type q14({vec[0], vec[1], vec[2]}, comp);
17351c0b2f7Stbbdev for (auto it = vec.begin() + 3; it != vec.end(); ++it)
17451c0b2f7Stbbdev q14.push(*it);
17551c0b2f7Stbbdev examine(q14, vec_sorted);
17651c0b2f7Stbbdev
17751c0b2f7Stbbdev queue_type q15({vec[0], vec[1], vec[2]}, alloc);
17851c0b2f7Stbbdev for (auto it = vec.begin() + 3; it != vec.end(); ++it)
17951c0b2f7Stbbdev q15.push(*it);
18051c0b2f7Stbbdev examine(q15, vec_sorted);
18151c0b2f7Stbbdev }
18251c0b2f7Stbbdev
18351c0b2f7Stbbdev template <typename ValueType>
type_tester(const std::vector<ValueType> & vec)18451c0b2f7Stbbdev void type_tester( const std::vector<ValueType>& vec ) {
18551c0b2f7Stbbdev type_tester(vec, std::less<ValueType>{});
18651c0b2f7Stbbdev }
18751c0b2f7Stbbdev
18851c0b2f7Stbbdev struct LessForSmartPointers {
18951c0b2f7Stbbdev template <typename T>
operatorLessForSmartPointers19051c0b2f7Stbbdev bool operator()( const T& t1, const T& t2 ) {
19151c0b2f7Stbbdev return *t1 < *t2;
19251c0b2f7Stbbdev }
19351c0b2f7Stbbdev
19451c0b2f7Stbbdev template <typename T>
operatorLessForSmartPointers19551c0b2f7Stbbdev bool operator()( const std::weak_ptr<T>& t1, const std::weak_ptr<T>& t2 ) {
19651c0b2f7Stbbdev return *t1.lock().get() < *t2.lock().get();
19751c0b2f7Stbbdev }
19851c0b2f7Stbbdev }; // struct LessForSmartPointers
19951c0b2f7Stbbdev
20051c0b2f7Stbbdev template <typename T>
type_tester_unique_ptr(const std::vector<T> & vec)20151c0b2f7Stbbdev void type_tester_unique_ptr( const std::vector<T>& vec ) {
20251c0b2f7Stbbdev REQUIRE_MESSAGE(vec.size() >= 5, "Array should have at least 5 elements");
20351c0b2f7Stbbdev
20451c0b2f7Stbbdev using value_type = std::unique_ptr<T>;
20551c0b2f7Stbbdev using queue_type = tbb::concurrent_priority_queue<value_type, LessForSmartPointers>;
20651c0b2f7Stbbdev
20751c0b2f7Stbbdev std::vector<value_type> vec_sorted;
20851c0b2f7Stbbdev for (auto& item : vec) {
20951c0b2f7Stbbdev vec_sorted.push_back(value_type(new T(item)));
21051c0b2f7Stbbdev }
21151c0b2f7Stbbdev std::sort(vec_sorted.begin(), vec_sorted.end(), LessForSmartPointers{});
21251c0b2f7Stbbdev
21351c0b2f7Stbbdev queue_type q1, q1_copy;
21451c0b2f7Stbbdev for (auto& item : vec) {
21551c0b2f7Stbbdev q1.push(value_type(new T(item)));
21651c0b2f7Stbbdev q1_copy.push(value_type(new T(item)));
21751c0b2f7Stbbdev }
21851c0b2f7Stbbdev examine</*HasCopyCtor=*/false>(q1, q1_copy, vec_sorted);
21951c0b2f7Stbbdev
22051c0b2f7Stbbdev queue_type q3_copy;
22151c0b2f7Stbbdev q1.clear();
22251c0b2f7Stbbdev
22351c0b2f7Stbbdev for (auto& item : vec) {
22451c0b2f7Stbbdev q1.emplace(new T(item));
22551c0b2f7Stbbdev }
22651c0b2f7Stbbdev
22751c0b2f7Stbbdev queue_type q3(std::move(q1));
22851c0b2f7Stbbdev examine</*HasCopyCtor=*/false>(q3, q3_copy, vec_sorted);
22951c0b2f7Stbbdev }
23051c0b2f7Stbbdev
23151c0b2f7Stbbdev const std::size_t MAX_ITER = 10000;
23251c0b2f7Stbbdev const std::size_t push_selector_variants = 3; // push, push rvalue and emplace
23351c0b2f7Stbbdev
23451c0b2f7Stbbdev template <typename Q, typename E>
push_selector(Q & q,E e,std::size_t i)23551c0b2f7Stbbdev void push_selector(Q& q, E e, std::size_t i) {
23651c0b2f7Stbbdev switch(i % push_selector_variants) {
23751c0b2f7Stbbdev case 0: q.push(e); break;
23851c0b2f7Stbbdev case 1: q.push(std::move(e)); break;
23951c0b2f7Stbbdev case 2: q.emplace(e); break;
24051c0b2f7Stbbdev }
24151c0b2f7Stbbdev }
24251c0b2f7Stbbdev
24351c0b2f7Stbbdev static std::atomic<std::size_t> counter;
24451c0b2f7Stbbdev
24551c0b2f7Stbbdev template <typename T, typename C>
24651c0b2f7Stbbdev class FillBody {
24751c0b2f7Stbbdev std::size_t n_thread;
24851c0b2f7Stbbdev T my_min, my_max;
24951c0b2f7Stbbdev tbb::concurrent_priority_queue<T, C>* q;
25051c0b2f7Stbbdev public:
25151c0b2f7Stbbdev FillBody( const FillBody& ) = delete;
25251c0b2f7Stbbdev FillBody& operator=( const FillBody& ) = delete;
25351c0b2f7Stbbdev
FillBody(std::size_t n,T max,T min,tbb::concurrent_priority_queue<T,C> * cpq)25451c0b2f7Stbbdev FillBody( std::size_t n, T max, T min, tbb::concurrent_priority_queue<T, C>* cpq )
25551c0b2f7Stbbdev : n_thread(n), my_min(min), my_max(max), q(cpq) {}
25651c0b2f7Stbbdev
operator()25751c0b2f7Stbbdev void operator()( const std::size_t thread_id ) const {
25851c0b2f7Stbbdev T elem = my_min + T(int(thread_id));
25951c0b2f7Stbbdev for (std::size_t i = 0; i < MAX_ITER; ++i) {
26051c0b2f7Stbbdev // do some pushes
26151c0b2f7Stbbdev push_selector(*q, elem, i);
26251c0b2f7Stbbdev if (elem == my_max) elem = my_min;
26351c0b2f7Stbbdev elem = elem + T(int(n_thread));
26451c0b2f7Stbbdev }
26551c0b2f7Stbbdev }
26651c0b2f7Stbbdev }; // class FillBody
26751c0b2f7Stbbdev
26851c0b2f7Stbbdev template <typename T, typename C>
26951c0b2f7Stbbdev struct EmptyBody {
27051c0b2f7Stbbdev T my_max;
27151c0b2f7Stbbdev tbb::concurrent_priority_queue<T, C>* q;
27251c0b2f7Stbbdev C less_than;
27351c0b2f7Stbbdev public:
27451c0b2f7Stbbdev EmptyBody( const EmptyBody& ) = delete;
27551c0b2f7Stbbdev EmptyBody& operator=( const EmptyBody& ) = delete;
27651c0b2f7Stbbdev
EmptyBodyEmptyBody27751c0b2f7Stbbdev EmptyBody( T max, tbb::concurrent_priority_queue<T, C>* cpq )
27851c0b2f7Stbbdev : my_max(max), q(cpq) {}
27951c0b2f7Stbbdev
operatorEmptyBody28051c0b2f7Stbbdev void operator()( const std::size_t ) const {
28151c0b2f7Stbbdev T elem(my_max), last;
28251c0b2f7Stbbdev if (q->try_pop(last)) {
28351c0b2f7Stbbdev ++counter;
28451c0b2f7Stbbdev while(q->try_pop(elem)) {
28551c0b2f7Stbbdev REQUIRE_MESSAGE(!less_than(last, elem), "Failed pop/priority test in EmptyBody");
28651c0b2f7Stbbdev last = elem;
28751c0b2f7Stbbdev elem = my_max;
28851c0b2f7Stbbdev ++counter;
28951c0b2f7Stbbdev }
29051c0b2f7Stbbdev }
29151c0b2f7Stbbdev }
29251c0b2f7Stbbdev }; // struct EmptyBody
29351c0b2f7Stbbdev
29451c0b2f7Stbbdev template <typename T, typename C>
29551c0b2f7Stbbdev class FloggerBody {
29651c0b2f7Stbbdev tbb::concurrent_priority_queue<T, C>* q;
29751c0b2f7Stbbdev public:
29851c0b2f7Stbbdev FloggerBody( const FloggerBody& ) = delete;
29951c0b2f7Stbbdev FloggerBody& operator=( const FloggerBody& ) = delete;
30051c0b2f7Stbbdev
FloggerBody(tbb::concurrent_priority_queue<T,C> * cpq)30151c0b2f7Stbbdev FloggerBody( tbb::concurrent_priority_queue<T, C>* cpq )
30251c0b2f7Stbbdev : q(cpq) {}
30351c0b2f7Stbbdev
operator()30451c0b2f7Stbbdev void operator()( const std::size_t thread_id ) const {
30551c0b2f7Stbbdev T elem = T(int(thread_id + 1));
30651c0b2f7Stbbdev for (std::size_t i = 0; i < MAX_ITER; ++i) {
30751c0b2f7Stbbdev push_selector(*q, elem, i);
30851c0b2f7Stbbdev q->try_pop(elem);
30951c0b2f7Stbbdev }
31051c0b2f7Stbbdev }
31151c0b2f7Stbbdev }; // class FloggerBody
31251c0b2f7Stbbdev
31351c0b2f7Stbbdev template <typename C, typename T>
test_parallel_push_pop(std::size_t n,T t_max,T t_min)31451c0b2f7Stbbdev void test_parallel_push_pop( std::size_t n, T t_max, T t_min ) {
31551c0b2f7Stbbdev std::size_t qsize;
31651c0b2f7Stbbdev
31751c0b2f7Stbbdev tbb::concurrent_priority_queue<T, C> q(0);
31851c0b2f7Stbbdev FillBody<T, C> filler(n, t_max, t_min, &q);
31951c0b2f7Stbbdev EmptyBody<T, C> emptier(t_max, &q);
32051c0b2f7Stbbdev
32151c0b2f7Stbbdev counter = 0;
32251c0b2f7Stbbdev utils::NativeParallelFor(n, filler);
32351c0b2f7Stbbdev
32451c0b2f7Stbbdev qsize = q.size();
32551c0b2f7Stbbdev REQUIRE_MESSAGE(q.size() == n * MAX_ITER, "Failed concurrent push size test");
32651c0b2f7Stbbdev REQUIRE_MESSAGE(!q.empty(), "Failed concurrent push empty test");
32751c0b2f7Stbbdev
32851c0b2f7Stbbdev utils::NativeParallelFor(n, emptier);
32951c0b2f7Stbbdev REQUIRE_MESSAGE(counter == qsize, "Failed pop size test");
33051c0b2f7Stbbdev REQUIRE_MESSAGE(q.size() == 0, "Failed pop empty test");
33151c0b2f7Stbbdev }
33251c0b2f7Stbbdev
33351c0b2f7Stbbdev template <typename C, typename T>
test_flogger(std::size_t n)33451c0b2f7Stbbdev void test_flogger( std::size_t n ) {
33551c0b2f7Stbbdev tbb::concurrent_priority_queue<T, C> q(0);
33651c0b2f7Stbbdev utils::NativeParallelFor(n, FloggerBody<T, C>{&q});
33751c0b2f7Stbbdev REQUIRE_MESSAGE(q.empty(), "Failed flogger empty test");
33851c0b2f7Stbbdev REQUIRE_MESSAGE(!q.size(), "Failed flogger size test");
33951c0b2f7Stbbdev }
34051c0b2f7Stbbdev
34151c0b2f7Stbbdev #endif // __TBB_test_common_concurrent_priority_queue_common_H
342