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