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