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