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
test_cpq_with_smart_pointers()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;
MyDataTypeMyDataType55 MyDataType( int val ) : priority(std::size_t(val)) {}
56
operator +MyDataType57 const MyDataType operator+( const MyDataType& other ) const {
58 return MyDataType(int(priority + other.priority));
59 }
60
operator ==MyDataType61 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 {
operator ()MyLess70 bool operator()( const MyDataType d1, const MyDataType d2 ) const {
71 return d1.priority < d2.priority;
72 }
73 }; // struct MyLess
74
test_concurrent(std::size_t n)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
test_multithreading()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;
MyThrowingTypeMyThrowingType89 MyThrowingType( const MyThrowingType& src ) : MyDataType(src) {
90 if (throw_flag) {
91 TBB_TEST_THROW(42);
92 }
93 }
94
operator =MyThrowingType95 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
test_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
test_scoped_allocator()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