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