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