1*51c0b2f7Stbbdev /* 2*51c0b2f7Stbbdev Copyright (c) 2005-2020 Intel Corporation 3*51c0b2f7Stbbdev 4*51c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License"); 5*51c0b2f7Stbbdev you may not use this file except in compliance with the License. 6*51c0b2f7Stbbdev You may obtain a copy of the License at 7*51c0b2f7Stbbdev 8*51c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0 9*51c0b2f7Stbbdev 10*51c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software 11*51c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS, 12*51c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*51c0b2f7Stbbdev See the License for the specific language governing permissions and 14*51c0b2f7Stbbdev limitations under the License. 15*51c0b2f7Stbbdev */ 16*51c0b2f7Stbbdev 17*51c0b2f7Stbbdev #include <common/concurrent_priority_queue_common.h> 18*51c0b2f7Stbbdev #include <common/containers_common.h> 19*51c0b2f7Stbbdev 20*51c0b2f7Stbbdev //! \file test_concurrent_priority_queue.cpp 21*51c0b2f7Stbbdev //! \brief Test for [containers.concurrent_priority_queue] specification 22*51c0b2f7Stbbdev 23*51c0b2f7Stbbdev void test_cpq_with_smart_pointers() { 24*51c0b2f7Stbbdev const int NUMBER = 10; 25*51c0b2f7Stbbdev 26*51c0b2f7Stbbdev utils::FastRandom<> rnd(1234); 27*51c0b2f7Stbbdev 28*51c0b2f7Stbbdev std::vector<std::shared_ptr<int>> shared_pointers; 29*51c0b2f7Stbbdev for (int i = 0; i < NUMBER; ++i ) { 30*51c0b2f7Stbbdev const int rnd_get = rnd.get(); 31*51c0b2f7Stbbdev shared_pointers.emplace_back(std::make_shared<int>(rnd_get)); 32*51c0b2f7Stbbdev } 33*51c0b2f7Stbbdev std::vector<std::weak_ptr<int>> weak_pointers; 34*51c0b2f7Stbbdev std::copy(shared_pointers.begin(), shared_pointers.end(), std::back_inserter(weak_pointers)); 35*51c0b2f7Stbbdev 36*51c0b2f7Stbbdev type_tester(shared_pointers, LessForSmartPointers{}); 37*51c0b2f7Stbbdev type_tester(weak_pointers, LessForSmartPointers{}); 38*51c0b2f7Stbbdev 39*51c0b2f7Stbbdev std::vector<int> arrInt; 40*51c0b2f7Stbbdev for (int i = 0; i < NUMBER; ++i) 41*51c0b2f7Stbbdev arrInt.emplace_back(rnd.get()); 42*51c0b2f7Stbbdev 43*51c0b2f7Stbbdev type_tester_unique_ptr(arrInt); // Test std::unique_ptr 44*51c0b2f7Stbbdev } 45*51c0b2f7Stbbdev 46*51c0b2f7Stbbdev struct MyDataType { 47*51c0b2f7Stbbdev std::size_t priority; 48*51c0b2f7Stbbdev char padding[tbb::detail::max_nfs_size - sizeof(int) % tbb::detail::max_nfs_size]; 49*51c0b2f7Stbbdev 50*51c0b2f7Stbbdev MyDataType() = default; 51*51c0b2f7Stbbdev MyDataType( int val ) : priority(std::size_t(val)) {} 52*51c0b2f7Stbbdev 53*51c0b2f7Stbbdev const MyDataType operator+( const MyDataType& other ) const { 54*51c0b2f7Stbbdev return MyDataType(int(priority + other.priority)); 55*51c0b2f7Stbbdev } 56*51c0b2f7Stbbdev 57*51c0b2f7Stbbdev bool operator==(const MyDataType& other) const { 58*51c0b2f7Stbbdev return this->priority == other.priority; 59*51c0b2f7Stbbdev } 60*51c0b2f7Stbbdev }; // struct MyDataType 61*51c0b2f7Stbbdev 62*51c0b2f7Stbbdev const MyDataType DATA_MIN(INT_MIN); 63*51c0b2f7Stbbdev const MyDataType DATA_MAX(INT_MAX); 64*51c0b2f7Stbbdev 65*51c0b2f7Stbbdev struct MyLess { 66*51c0b2f7Stbbdev bool operator()( const MyDataType d1, const MyDataType d2 ) const { 67*51c0b2f7Stbbdev return d1.priority < d2.priority; 68*51c0b2f7Stbbdev } 69*51c0b2f7Stbbdev }; // struct MyLess 70*51c0b2f7Stbbdev 71*51c0b2f7Stbbdev void test_concurrent( std::size_t n ) { 72*51c0b2f7Stbbdev test_parallel_push_pop<MyLess>(n, DATA_MAX, DATA_MIN); 73*51c0b2f7Stbbdev test_flogger<MyLess, MyDataType>(n); 74*51c0b2f7Stbbdev } 75*51c0b2f7Stbbdev 76*51c0b2f7Stbbdev void test_multithreading() { 77*51c0b2f7Stbbdev for (std::size_t n = utils::MinThread; n != utils::MaxThread; ++n) { 78*51c0b2f7Stbbdev test_concurrent(n); 79*51c0b2f7Stbbdev } 80*51c0b2f7Stbbdev } 81*51c0b2f7Stbbdev 82*51c0b2f7Stbbdev struct MyThrowingType : public MyDataType { 83*51c0b2f7Stbbdev static int throw_flag; 84*51c0b2f7Stbbdev MyThrowingType() = default; 85*51c0b2f7Stbbdev MyThrowingType( const MyThrowingType& src ) : MyDataType(src) { 86*51c0b2f7Stbbdev if (throw_flag) { 87*51c0b2f7Stbbdev TBB_TEST_THROW(42); 88*51c0b2f7Stbbdev } 89*51c0b2f7Stbbdev } 90*51c0b2f7Stbbdev 91*51c0b2f7Stbbdev MyThrowingType& operator=( const MyThrowingType& other ) { 92*51c0b2f7Stbbdev priority = other.priority; 93*51c0b2f7Stbbdev return *this; 94*51c0b2f7Stbbdev } 95*51c0b2f7Stbbdev }; 96*51c0b2f7Stbbdev 97*51c0b2f7Stbbdev int MyThrowingType::throw_flag = 0; 98*51c0b2f7Stbbdev 99*51c0b2f7Stbbdev using CPQExTestType = tbb::concurrent_priority_queue<MyThrowingType, MyLess>; 100*51c0b2f7Stbbdev 101*51c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 102*51c0b2f7Stbbdev void test_exceptions() { 103*51c0b2f7Stbbdev // TODO: TBB_USE_EXCEPTIONS? 104*51c0b2f7Stbbdev const std::size_t TOO_LARGE_SZ = std::vector<MyThrowingType, typename CPQExTestType::allocator_type>{}.max_size() + 1; 105*51c0b2f7Stbbdev 106*51c0b2f7Stbbdev REQUIRE(TOO_LARGE_SZ < std::numeric_limits<std::size_t>::max()); 107*51c0b2f7Stbbdev MyThrowingType elem; 108*51c0b2f7Stbbdev 109*51c0b2f7Stbbdev // Allocation of empty queue should not throw 110*51c0b2f7Stbbdev REQUIRE_NOTHROW([]{ 111*51c0b2f7Stbbdev MyThrowingType::throw_flag = 1; 112*51c0b2f7Stbbdev CPQExTestType q; 113*51c0b2f7Stbbdev }()); 114*51c0b2f7Stbbdev 115*51c0b2f7Stbbdev // Allocation of small queue should not throw for reasonably sized type 116*51c0b2f7Stbbdev REQUIRE_NOTHROW([]{ 117*51c0b2f7Stbbdev MyThrowingType::throw_flag = 1; 118*51c0b2f7Stbbdev CPQExTestType(42); 119*51c0b2f7Stbbdev }()); 120*51c0b2f7Stbbdev 121*51c0b2f7Stbbdev // Allocate a queue with too large initial size 122*51c0b2f7Stbbdev REQUIRE_THROWS([&]{ 123*51c0b2f7Stbbdev MyThrowingType::throw_flag = 0; 124*51c0b2f7Stbbdev CPQExTestType q(TOO_LARGE_SZ); 125*51c0b2f7Stbbdev }()); 126*51c0b2f7Stbbdev 127*51c0b2f7Stbbdev // Test copy ctor exceptions 128*51c0b2f7Stbbdev MyThrowingType::throw_flag = 0; 129*51c0b2f7Stbbdev CPQExTestType src_q(42); 130*51c0b2f7Stbbdev elem.priority = 42; 131*51c0b2f7Stbbdev for (std::size_t i = 0; i < 42; ++i) src_q.push(elem); 132*51c0b2f7Stbbdev 133*51c0b2f7Stbbdev REQUIRE_THROWS_MESSAGE([&]{ 134*51c0b2f7Stbbdev MyThrowingType::throw_flag = 1; 135*51c0b2f7Stbbdev CPQExTestType q(src_q); 136*51c0b2f7Stbbdev }(), "Copy ctor did not throw exception"); 137*51c0b2f7Stbbdev 138*51c0b2f7Stbbdev // Test assignment 139*51c0b2f7Stbbdev MyThrowingType::throw_flag = 0; 140*51c0b2f7Stbbdev CPQExTestType assign_q(24); 141*51c0b2f7Stbbdev 142*51c0b2f7Stbbdev REQUIRE_THROWS_MESSAGE([&]{ 143*51c0b2f7Stbbdev MyThrowingType::throw_flag = 1; 144*51c0b2f7Stbbdev assign_q = src_q; 145*51c0b2f7Stbbdev }(), "Assignment did not throw exception"); 146*51c0b2f7Stbbdev REQUIRE(assign_q.empty()); 147*51c0b2f7Stbbdev 148*51c0b2f7Stbbdev for (std::size_t i = 0; i < push_selector_variants; ++i) { 149*51c0b2f7Stbbdev MyThrowingType::throw_flag = 0; 150*51c0b2f7Stbbdev CPQExTestType pq(3); 151*51c0b2f7Stbbdev REQUIRE_NOTHROW([&]{ 152*51c0b2f7Stbbdev push_selector(pq, elem, i); 153*51c0b2f7Stbbdev push_selector(pq, elem, i); 154*51c0b2f7Stbbdev push_selector(pq, elem, i); 155*51c0b2f7Stbbdev }()); 156*51c0b2f7Stbbdev 157*51c0b2f7Stbbdev try { 158*51c0b2f7Stbbdev MyThrowingType::throw_flag = 1; 159*51c0b2f7Stbbdev push_selector(pq, elem, i); 160*51c0b2f7Stbbdev } catch(...) { 161*51c0b2f7Stbbdev REQUIRE_MESSAGE(!pq.empty(), "Failed: pq should not be empty"); 162*51c0b2f7Stbbdev REQUIRE_MESSAGE(pq.size() == 3, "Failed: pq should contain only three elements"); 163*51c0b2f7Stbbdev REQUIRE_MESSAGE(pq.try_pop(elem), "Failed: pq is not functional"); 164*51c0b2f7Stbbdev } 165*51c0b2f7Stbbdev 166*51c0b2f7Stbbdev MyThrowingType::throw_flag = 0; 167*51c0b2f7Stbbdev CPQExTestType pq2(3); 168*51c0b2f7Stbbdev REQUIRE_NOTHROW([&]{ 169*51c0b2f7Stbbdev push_selector(pq2, elem, i); 170*51c0b2f7Stbbdev push_selector(pq2, elem, i); 171*51c0b2f7Stbbdev }()); 172*51c0b2f7Stbbdev 173*51c0b2f7Stbbdev try { 174*51c0b2f7Stbbdev MyThrowingType::throw_flag = 1; 175*51c0b2f7Stbbdev push_selector(pq2, elem, i); 176*51c0b2f7Stbbdev } catch(...) { 177*51c0b2f7Stbbdev REQUIRE_MESSAGE(!pq2.empty(), "Failed: pq should not be empty"); 178*51c0b2f7Stbbdev REQUIRE_MESSAGE(pq2.size() == 2, "Failed: pq should contain only two elements"); 179*51c0b2f7Stbbdev REQUIRE_MESSAGE(pq2.try_pop(elem), "Failed: pq is not functional"); 180*51c0b2f7Stbbdev } 181*51c0b2f7Stbbdev } 182*51c0b2f7Stbbdev } 183*51c0b2f7Stbbdev #endif 184*51c0b2f7Stbbdev 185*51c0b2f7Stbbdev void test_scoped_allocator() { 186*51c0b2f7Stbbdev using allocator_data_type = AllocatorAwareData<std::scoped_allocator_adaptor<std::allocator<int>>>; 187*51c0b2f7Stbbdev using basic_allocator_type = std::scoped_allocator_adaptor<std::allocator<allocator_data_type>>; 188*51c0b2f7Stbbdev using allocator_type = std::allocator_traits<basic_allocator_type>::template rebind_alloc<allocator_data_type>; 189*51c0b2f7Stbbdev using container_type = tbb::concurrent_priority_queue<allocator_data_type, std::less<allocator_data_type>, allocator_type>; 190*51c0b2f7Stbbdev 191*51c0b2f7Stbbdev allocator_type allocator; 192*51c0b2f7Stbbdev allocator_data_type data1(1, allocator); 193*51c0b2f7Stbbdev allocator_data_type data2(1, allocator); 194*51c0b2f7Stbbdev 195*51c0b2f7Stbbdev container_type c1(allocator); 196*51c0b2f7Stbbdev container_type c2(allocator); 197*51c0b2f7Stbbdev 198*51c0b2f7Stbbdev allocator_data_type::activate(); 199*51c0b2f7Stbbdev 200*51c0b2f7Stbbdev c1.push(data1); 201*51c0b2f7Stbbdev c2.push(std::move(data2)); 202*51c0b2f7Stbbdev 203*51c0b2f7Stbbdev // TODO: support uses allocator construction in this place 204*51c0b2f7Stbbdev // c1.emplace(data1); 205*51c0b2f7Stbbdev 206*51c0b2f7Stbbdev c1 = c2; 207*51c0b2f7Stbbdev c2 = std::move(c1); 208*51c0b2f7Stbbdev 209*51c0b2f7Stbbdev allocator_data_type::deactivate(); 210*51c0b2f7Stbbdev } 211*51c0b2f7Stbbdev 212*51c0b2f7Stbbdev // Testing concurrent_priority_queue with smart pointers and other special types 213*51c0b2f7Stbbdev //! \brief \ref error_guessing 214*51c0b2f7Stbbdev TEST_CASE("concurrent_priority_queue with smart_pointers") { 215*51c0b2f7Stbbdev test_cpq_with_smart_pointers(); 216*51c0b2f7Stbbdev } 217*51c0b2f7Stbbdev 218*51c0b2f7Stbbdev //! Testing push-pop operations in concurrent_priority_queue with multithreading and specific value type 219*51c0b2f7Stbbdev //! \brief \ref error_guessing 220*51c0b2f7Stbbdev TEST_CASE("multithreading support in concurrent_priority_queue with specific value type") { 221*51c0b2f7Stbbdev test_multithreading(); 222*51c0b2f7Stbbdev } 223*51c0b2f7Stbbdev 224*51c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 225*51c0b2f7Stbbdev //! Testing exceptions support in concurrent_priority_queue 226*51c0b2f7Stbbdev //! \brief \ref stress \ref error_guessing 227*51c0b2f7Stbbdev TEST_CASE("exception handling in concurrent_priority_queue") { 228*51c0b2f7Stbbdev test_exceptions(); 229*51c0b2f7Stbbdev } 230*51c0b2f7Stbbdev #endif 231*51c0b2f7Stbbdev 232*51c0b2f7Stbbdev //! \brief \ref error_guessing 233*51c0b2f7Stbbdev TEST_CASE("concurrent_priority_queue with std::scoped_allocator_adaptor") { 234*51c0b2f7Stbbdev test_scoped_allocator(); 235*51c0b2f7Stbbdev } 236