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 
17*b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
18*b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19*b15aabb3Stbbdev #endif
20*b15aabb3Stbbdev 
2151c0b2f7Stbbdev #include <common/concurrent_priority_queue_common.h>
2251c0b2f7Stbbdev #include <common/containers_common.h>
2351c0b2f7Stbbdev 
2451c0b2f7Stbbdev //! \file test_concurrent_priority_queue.cpp
2551c0b2f7Stbbdev //! \brief Test for [containers.concurrent_priority_queue] specification
2651c0b2f7Stbbdev 
test_cpq_with_smart_pointers()2751c0b2f7Stbbdev void test_cpq_with_smart_pointers() {
2851c0b2f7Stbbdev     const int NUMBER = 10;
2951c0b2f7Stbbdev 
3051c0b2f7Stbbdev     utils::FastRandom<> rnd(1234);
3151c0b2f7Stbbdev 
3251c0b2f7Stbbdev     std::vector<std::shared_ptr<int>> shared_pointers;
3351c0b2f7Stbbdev     for (int i = 0; i < NUMBER; ++i ) {
3451c0b2f7Stbbdev         const int rnd_get = rnd.get();
3551c0b2f7Stbbdev         shared_pointers.emplace_back(std::make_shared<int>(rnd_get));
3651c0b2f7Stbbdev     }
3751c0b2f7Stbbdev     std::vector<std::weak_ptr<int>> weak_pointers;
3851c0b2f7Stbbdev     std::copy(shared_pointers.begin(), shared_pointers.end(), std::back_inserter(weak_pointers));
3951c0b2f7Stbbdev 
4051c0b2f7Stbbdev     type_tester(shared_pointers, LessForSmartPointers{});
4151c0b2f7Stbbdev     type_tester(weak_pointers, LessForSmartPointers{});
4251c0b2f7Stbbdev 
4351c0b2f7Stbbdev     std::vector<int> arrInt;
4451c0b2f7Stbbdev     for (int i = 0; i < NUMBER; ++i)
4551c0b2f7Stbbdev         arrInt.emplace_back(rnd.get());
4651c0b2f7Stbbdev 
4751c0b2f7Stbbdev     type_tester_unique_ptr(arrInt); // Test std::unique_ptr
4851c0b2f7Stbbdev }
4951c0b2f7Stbbdev 
5051c0b2f7Stbbdev struct MyDataType {
5151c0b2f7Stbbdev     std::size_t priority;
5251c0b2f7Stbbdev     char padding[tbb::detail::max_nfs_size - sizeof(int) % tbb::detail::max_nfs_size];
5351c0b2f7Stbbdev 
5451c0b2f7Stbbdev     MyDataType() = default;
MyDataTypeMyDataType5551c0b2f7Stbbdev     MyDataType( int val ) : priority(std::size_t(val)) {}
5651c0b2f7Stbbdev 
operator +MyDataType5751c0b2f7Stbbdev     const MyDataType operator+( const MyDataType& other ) const {
5851c0b2f7Stbbdev         return MyDataType(int(priority + other.priority));
5951c0b2f7Stbbdev     }
6051c0b2f7Stbbdev 
operator ==MyDataType6151c0b2f7Stbbdev     bool operator==(const MyDataType& other) const {
6251c0b2f7Stbbdev         return this->priority == other.priority;
6351c0b2f7Stbbdev     }
6451c0b2f7Stbbdev }; // struct MyDataType
6551c0b2f7Stbbdev 
6651c0b2f7Stbbdev const MyDataType DATA_MIN(INT_MIN);
6751c0b2f7Stbbdev const MyDataType DATA_MAX(INT_MAX);
6851c0b2f7Stbbdev 
6951c0b2f7Stbbdev struct MyLess {
operator ()MyLess7051c0b2f7Stbbdev     bool operator()( const MyDataType d1, const MyDataType d2 ) const {
7151c0b2f7Stbbdev         return d1.priority < d2.priority;
7251c0b2f7Stbbdev     }
7351c0b2f7Stbbdev }; // struct MyLess
7451c0b2f7Stbbdev 
test_concurrent(std::size_t n)7551c0b2f7Stbbdev void test_concurrent( std::size_t n ) {
7651c0b2f7Stbbdev     test_parallel_push_pop<MyLess>(n, DATA_MAX, DATA_MIN);
7751c0b2f7Stbbdev     test_flogger<MyLess, MyDataType>(n);
7851c0b2f7Stbbdev }
7951c0b2f7Stbbdev 
test_multithreading()8051c0b2f7Stbbdev void test_multithreading() {
8151c0b2f7Stbbdev     for (std::size_t n = utils::MinThread; n != utils::MaxThread; ++n) {
8251c0b2f7Stbbdev         test_concurrent(n);
8351c0b2f7Stbbdev     }
8451c0b2f7Stbbdev }
8551c0b2f7Stbbdev 
8651c0b2f7Stbbdev struct MyThrowingType : public MyDataType {
8751c0b2f7Stbbdev     static int throw_flag;
8851c0b2f7Stbbdev     MyThrowingType() = default;
MyThrowingTypeMyThrowingType8951c0b2f7Stbbdev     MyThrowingType( const MyThrowingType& src ) : MyDataType(src) {
9051c0b2f7Stbbdev         if (throw_flag) {
9151c0b2f7Stbbdev             TBB_TEST_THROW(42);
9251c0b2f7Stbbdev         }
9351c0b2f7Stbbdev     }
9451c0b2f7Stbbdev 
operator =MyThrowingType9551c0b2f7Stbbdev     MyThrowingType& operator=( const MyThrowingType& other ) {
9651c0b2f7Stbbdev         priority = other.priority;
9751c0b2f7Stbbdev         return *this;
9851c0b2f7Stbbdev     }
9951c0b2f7Stbbdev };
10051c0b2f7Stbbdev 
10151c0b2f7Stbbdev int MyThrowingType::throw_flag = 0;
10251c0b2f7Stbbdev 
10351c0b2f7Stbbdev using CPQExTestType = tbb::concurrent_priority_queue<MyThrowingType, MyLess>;
10451c0b2f7Stbbdev 
10551c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
test_exceptions()10651c0b2f7Stbbdev void test_exceptions() {
10751c0b2f7Stbbdev     // TODO: TBB_USE_EXCEPTIONS?
10851c0b2f7Stbbdev     const std::size_t TOO_LARGE_SZ = std::vector<MyThrowingType, typename CPQExTestType::allocator_type>{}.max_size() + 1;
10951c0b2f7Stbbdev 
11051c0b2f7Stbbdev     REQUIRE(TOO_LARGE_SZ < std::numeric_limits<std::size_t>::max());
11151c0b2f7Stbbdev     MyThrowingType elem;
11251c0b2f7Stbbdev 
11351c0b2f7Stbbdev     // Allocation of empty queue should not throw
11451c0b2f7Stbbdev     REQUIRE_NOTHROW([]{
11551c0b2f7Stbbdev         MyThrowingType::throw_flag = 1;
11651c0b2f7Stbbdev         CPQExTestType q;
11751c0b2f7Stbbdev     }());
11851c0b2f7Stbbdev 
11951c0b2f7Stbbdev     // Allocation of small queue should not throw for reasonably sized type
12051c0b2f7Stbbdev     REQUIRE_NOTHROW([]{
12151c0b2f7Stbbdev         MyThrowingType::throw_flag = 1;
12251c0b2f7Stbbdev         CPQExTestType(42);
12351c0b2f7Stbbdev     }());
12451c0b2f7Stbbdev 
12551c0b2f7Stbbdev     // Allocate a queue with too large initial size
12651c0b2f7Stbbdev     REQUIRE_THROWS([&]{
12751c0b2f7Stbbdev         MyThrowingType::throw_flag = 0;
12851c0b2f7Stbbdev         CPQExTestType q(TOO_LARGE_SZ);
12951c0b2f7Stbbdev     }());
13051c0b2f7Stbbdev 
13151c0b2f7Stbbdev     // Test copy ctor exceptions
13251c0b2f7Stbbdev     MyThrowingType::throw_flag = 0;
13351c0b2f7Stbbdev     CPQExTestType src_q(42);
13451c0b2f7Stbbdev     elem.priority = 42;
13551c0b2f7Stbbdev     for (std::size_t i = 0; i < 42; ++i) src_q.push(elem);
13651c0b2f7Stbbdev 
13751c0b2f7Stbbdev     REQUIRE_THROWS_MESSAGE([&]{
13851c0b2f7Stbbdev         MyThrowingType::throw_flag = 1;
13951c0b2f7Stbbdev         CPQExTestType q(src_q);
14051c0b2f7Stbbdev     }(), "Copy ctor did not throw exception");
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev     // Test assignment
14351c0b2f7Stbbdev     MyThrowingType::throw_flag = 0;
14451c0b2f7Stbbdev     CPQExTestType assign_q(24);
14551c0b2f7Stbbdev 
14651c0b2f7Stbbdev     REQUIRE_THROWS_MESSAGE([&]{
14751c0b2f7Stbbdev         MyThrowingType::throw_flag = 1;
14851c0b2f7Stbbdev         assign_q = src_q;
14951c0b2f7Stbbdev     }(), "Assignment did not throw exception");
15051c0b2f7Stbbdev     REQUIRE(assign_q.empty());
15151c0b2f7Stbbdev 
15251c0b2f7Stbbdev     for (std::size_t i = 0; i < push_selector_variants; ++i) {
15351c0b2f7Stbbdev         MyThrowingType::throw_flag = 0;
15451c0b2f7Stbbdev         CPQExTestType pq(3);
15551c0b2f7Stbbdev         REQUIRE_NOTHROW([&]{
15651c0b2f7Stbbdev             push_selector(pq, elem, i);
15751c0b2f7Stbbdev             push_selector(pq, elem, i);
15851c0b2f7Stbbdev             push_selector(pq, elem, i);
15951c0b2f7Stbbdev         }());
16051c0b2f7Stbbdev 
16151c0b2f7Stbbdev         try {
16251c0b2f7Stbbdev             MyThrowingType::throw_flag = 1;
16351c0b2f7Stbbdev             push_selector(pq, elem, i);
16451c0b2f7Stbbdev         } catch(...) {
16551c0b2f7Stbbdev             REQUIRE_MESSAGE(!pq.empty(), "Failed: pq should not be empty");
16651c0b2f7Stbbdev             REQUIRE_MESSAGE(pq.size() == 3, "Failed: pq should contain only three elements");
16751c0b2f7Stbbdev             REQUIRE_MESSAGE(pq.try_pop(elem), "Failed: pq is not functional");
16851c0b2f7Stbbdev         }
16951c0b2f7Stbbdev 
17051c0b2f7Stbbdev         MyThrowingType::throw_flag = 0;
17151c0b2f7Stbbdev         CPQExTestType pq2(3);
17251c0b2f7Stbbdev         REQUIRE_NOTHROW([&]{
17351c0b2f7Stbbdev             push_selector(pq2, elem, i);
17451c0b2f7Stbbdev             push_selector(pq2, elem, i);
17551c0b2f7Stbbdev         }());
17651c0b2f7Stbbdev 
17751c0b2f7Stbbdev         try {
17851c0b2f7Stbbdev             MyThrowingType::throw_flag = 1;
17951c0b2f7Stbbdev             push_selector(pq2, elem, i);
18051c0b2f7Stbbdev         } catch(...) {
18151c0b2f7Stbbdev             REQUIRE_MESSAGE(!pq2.empty(), "Failed: pq should not be empty");
18251c0b2f7Stbbdev             REQUIRE_MESSAGE(pq2.size() == 2, "Failed: pq should contain only two elements");
18351c0b2f7Stbbdev             REQUIRE_MESSAGE(pq2.try_pop(elem), "Failed: pq is not functional");
18451c0b2f7Stbbdev         }
18551c0b2f7Stbbdev     }
18651c0b2f7Stbbdev }
18751c0b2f7Stbbdev #endif
18851c0b2f7Stbbdev 
test_scoped_allocator()18951c0b2f7Stbbdev void test_scoped_allocator() {
19051c0b2f7Stbbdev     using allocator_data_type = AllocatorAwareData<std::scoped_allocator_adaptor<std::allocator<int>>>;
19151c0b2f7Stbbdev     using basic_allocator_type = std::scoped_allocator_adaptor<std::allocator<allocator_data_type>>;
19251c0b2f7Stbbdev     using allocator_type = std::allocator_traits<basic_allocator_type>::template rebind_alloc<allocator_data_type>;
19351c0b2f7Stbbdev     using container_type = tbb::concurrent_priority_queue<allocator_data_type, std::less<allocator_data_type>, allocator_type>;
19451c0b2f7Stbbdev 
19551c0b2f7Stbbdev     allocator_type allocator;
19651c0b2f7Stbbdev     allocator_data_type data1(1, allocator);
19751c0b2f7Stbbdev     allocator_data_type data2(1, allocator);
19851c0b2f7Stbbdev 
19951c0b2f7Stbbdev     container_type c1(allocator);
20051c0b2f7Stbbdev     container_type c2(allocator);
20151c0b2f7Stbbdev 
20251c0b2f7Stbbdev     allocator_data_type::activate();
20351c0b2f7Stbbdev 
20451c0b2f7Stbbdev     c1.push(data1);
20551c0b2f7Stbbdev     c2.push(std::move(data2));
20651c0b2f7Stbbdev 
20751c0b2f7Stbbdev     // TODO: support uses allocator construction in this place
20851c0b2f7Stbbdev     // c1.emplace(data1);
20951c0b2f7Stbbdev 
21051c0b2f7Stbbdev     c1 = c2;
21151c0b2f7Stbbdev     c2 = std::move(c1);
21251c0b2f7Stbbdev 
21351c0b2f7Stbbdev     allocator_data_type::deactivate();
21451c0b2f7Stbbdev }
21551c0b2f7Stbbdev 
21651c0b2f7Stbbdev // Testing concurrent_priority_queue with smart pointers and other special types
21751c0b2f7Stbbdev //! \brief \ref error_guessing
21851c0b2f7Stbbdev TEST_CASE("concurrent_priority_queue with smart_pointers") {
21951c0b2f7Stbbdev     test_cpq_with_smart_pointers();
22051c0b2f7Stbbdev }
22151c0b2f7Stbbdev 
22251c0b2f7Stbbdev //! Testing push-pop operations in concurrent_priority_queue with multithreading and specific value type
22351c0b2f7Stbbdev //! \brief \ref error_guessing
22451c0b2f7Stbbdev TEST_CASE("multithreading support in concurrent_priority_queue with specific value type") {
22551c0b2f7Stbbdev     test_multithreading();
22651c0b2f7Stbbdev }
22751c0b2f7Stbbdev 
22851c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
22951c0b2f7Stbbdev //! Testing exceptions support in concurrent_priority_queue
23051c0b2f7Stbbdev //! \brief \ref stress \ref error_guessing
23151c0b2f7Stbbdev TEST_CASE("exception handling in concurrent_priority_queue") {
23251c0b2f7Stbbdev     test_exceptions();
23351c0b2f7Stbbdev }
23451c0b2f7Stbbdev #endif
23551c0b2f7Stbbdev 
23651c0b2f7Stbbdev //! \brief \ref error_guessing
23751c0b2f7Stbbdev TEST_CASE("concurrent_priority_queue with std::scoped_allocator_adaptor") {
23851c0b2f7Stbbdev     test_scoped_allocator();
23951c0b2f7Stbbdev }
240