1/*
2 Copyright (c) 2005-2019 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 "harness_defs.h"
18#include "tbb/concurrent_priority_queue.h"
19#include "tbb/atomic.h"
20#include "tbb/blocked_range.h"
21#include "harness.h"
22#include <functional>
23#include <algorithm>
24#include "harness_allocator.h"
25#include <vector>
26#include "test_container_move_support.h"
27
28#if _MSC_VER==1500 && !__INTEL_COMPILER
29 // VS2008/VC9 seems to have an issue; limits pull in math.h
30 #pragma warning( push )
31 #pragma warning( disable: 4985 )
32#endif
33#include <climits>
34#if _MSC_VER==1500 && !__INTEL_COMPILER
35 #pragma warning( pop )
36#endif
37
38#if __INTEL_COMPILER && (_WIN32 || _WIN64) && TBB_USE_DEBUG && _CPPLIB_VER<520
39// The Intel Compiler has an issue that causes the Microsoft Iterator
40// Debugging code to crash in vector::pop_back when it is called after a
41// vector::push_back throws an exception.
42// #define _HAS_ITERATOR_DEBUGGING 0 // Setting this to 0 doesn't solve the problem
43 // and also provokes a redefinition warning
44#define __TBB_ITERATOR_DEBUGGING_EXCEPTIONS_BROKEN
45#endif
46
47using namespace tbb;
48
49const size_t MAX_ITER = 10000;
50
51tbb::atomic<unsigned int> counter;
52
53class my_data_type {
54public:
55 int priority;
56 char padding[tbb::internal::NFS_MaxLineSize - sizeof(int) % tbb::internal::NFS_MaxLineSize];
57 my_data_type() {}
58 my_data_type(int init_val) : priority(init_val) {}
59 const my_data_type operator+(const my_data_type& other) const {
60 return my_data_type(priority+other.priority);
61 }
62 bool operator==(const my_data_type& other) const {
63 return this->priority == other.priority;
64 }
65};
66
67const my_data_type DATA_MIN(INT_MIN);
68const my_data_type DATA_MAX(INT_MAX);
69
70class my_less {
71public:
72 bool operator()(const my_data_type d1, const my_data_type d2) const {
73 return d1.priority<d2.priority;
74 }
75};
76
77#if TBB_USE_EXCEPTIONS
78class my_throwing_type : public my_data_type {
79public:
80 static int throw_flag;
81 my_throwing_type() : my_data_type() {}
82 my_throwing_type(const my_throwing_type& src) : my_data_type(src) {
83 if (my_throwing_type::throw_flag) throw 42;
84 priority = src.priority;
85 }
86};
87int my_throwing_type::throw_flag = 0;
88
89typedef concurrent_priority_queue<my_throwing_type, my_less > cpq_ex_test_type;
90#endif
91
92#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_RVALUE_REF_PRESENT
93const size_t push_selector_variants = 3;
94#elif __TBB_CPP11_RVALUE_REF_PRESENT
95const size_t push_selector_variants = 2;
96#else
97const size_t push_selector_variants = 1;
98#endif
99
100template <typename Q, typename E>
101void push_selector(Q& q, E e, size_t i) {
102 switch (i%push_selector_variants) {
103 case 0: q->push(e); break;
104#if __TBB_CPP11_RVALUE_REF_PRESENT
105 case 1: q->push(tbb::internal::move(e)); break;
106#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
107 case 2: q->emplace(e); break;
108#endif
109#endif
110 }
111}
112
113template<typename T, typename C>
114class FillBody : NoAssign {
115 int nThread;
116 T my_max, my_min;
117 concurrent_priority_queue<T, C> *q;
118 C less_than;
119public:
120 FillBody(int nThread_, T max_, T min_, concurrent_priority_queue<T, C> *q_) : nThread(nThread_), my_max(max_), my_min(min_), q(q_) {}
121 void operator()(const int threadID) const {
122 T elem = my_min + T(threadID);
123 for (size_t i=0; i<MAX_ITER; ++i) {
124 // do some pushes
125 push_selector(q, elem, i);
126 if (elem == my_max) elem = my_min;
127 elem = elem + T(nThread);
128 }
129 }
130};
131
132template<typename T, typename C>
133struct EmptyBody : NoAssign {
134 int nThread;
135 T my_max;
136 concurrent_priority_queue<T, C> *q;
137 C less_than;
138public:
139 EmptyBody(int nThread_, T max_, concurrent_priority_queue<T, C> *q_) : nThread(nThread_), my_max(max_), q(q_) {}
140 void operator()(const int /*threadID*/) const {
141 T elem(my_max), last;
142 if (q->try_pop(last)) {
143 ++counter;
144 while(q->try_pop(elem)) {
145 ASSERT(!less_than(last, elem), "FAILED pop/priority test in EmptyBody.");
146 last = elem;
147 elem = my_max;
148 ++counter;
149 }
150 }
151 }
152};
153
154template <typename T, typename C>
155class FloggerBody : NoAssign {
156 int nThread;
157 concurrent_priority_queue<T, C> *q;
158public:
159 FloggerBody(int nThread_, concurrent_priority_queue<T, C> *q_) :
160 nThread(nThread_), q(q_) {}
161 void operator()(const int threadID) const {
162 T elem = T(threadID+1);
163 for (size_t i=0; i<MAX_ITER; ++i) {
164 push_selector(q, elem, i);
165 (void) q->try_pop(elem);
166 }
167 }
168};
169
170namespace equality_comparison_helpers {
171 struct to_vector{
172 template <typename element_type, typename compare_t, typename allocator_t>
173 std::vector<element_type> operator()(tbb::concurrent_priority_queue<element_type, compare_t, allocator_t> const& source) const{
174 tbb::concurrent_priority_queue<element_type, compare_t, allocator_t> cpq((source));
175 std::vector<element_type> v; v.reserve(cpq.size());
176 element_type element;
177 while (cpq.try_pop(element)){ v.push_back(element);}
178 std::reverse(v.begin(),v.end());
179 return v;
180 }
181 };
182}
183//TODO: make CPQ more testable instead of hacking ad-hoc operator ==
184template <typename element_type, typename compare_t, typename allocator_t>
185bool operator==(tbb::concurrent_priority_queue<element_type, compare_t, allocator_t> const& lhs, tbb::concurrent_priority_queue<element_type, compare_t, allocator_t> const& rhs){
186 using equality_comparison_helpers::to_vector;
187 return to_vector()(lhs) == to_vector()(rhs);
188}
189
190template <typename range, typename element_type, typename compare_t, typename allocator_t>
191bool operator==(tbb::concurrent_priority_queue<element_type, compare_t, allocator_t> const& lhs, range const & rhs ){
192 using equality_comparison_helpers::to_vector;
193 return to_vector()(lhs) == std::vector<element_type>(rhs.begin(),rhs.end());
194}
195
196void TestToVector(){
197 using equality_comparison_helpers::to_vector;
198 int array[] = {1,5,6,8,4,7};
199 tbb::blocked_range<int *> range = Harness::make_blocked_range(array);
200 std::vector<int> source(range.begin(),range.end());
201 tbb::concurrent_priority_queue<int> q(source.begin(),source.end());
202 std::vector<int> from_cpq = to_vector()(q);
203 std::sort(source.begin(),source.end());
204 ASSERT(source == from_cpq,"equality_comparison_helpers::to_vector incorrectly copied items from CPQ?");
205}
206
207void TestHelpers(){
208 TestToVector();
209}
210
211//Comparator with assert in default constructor
212template<typename T>
213class less_a : public std::less<T>
214{
215public:
216 explicit less_a(bool no_assert = false) {
217 ASSERT(no_assert,"empty constructor should not be called");
218 };
219};
220
221void TestConstructorsDestructorsAccessors() {
222 std::vector<int> v;
223 std::allocator<int> a;
224 concurrent_priority_queue<int, std::less<int> > *q, *qo;
225 concurrent_priority_queue<int, std::less<int>, std::allocator<int> > *qi;
226
227 less_a<int> l(true);
228 concurrent_priority_queue<int, less_a<int> > *ql;
229 concurrent_priority_queue<int, less_a<int>, std::allocator<int> > *qla;
230
231 // Test constructors/destructors
232 REMARK("Testing default constructor.\n");
233 q = new concurrent_priority_queue<int, std::less<int> >();
234 REMARK("Default constructor complete.\n");
235 ASSERT(q->size()==0, "FAILED size test.");
236 ASSERT(q->empty(), "FAILED empty test.");
237 REMARK("Testing destructor.\n");
238 delete q;
239 REMARK("Destruction complete.\n");
240 REMARK("Testing capacity constructor.\n");
241 q = new concurrent_priority_queue<int, std::less<int> >(42);
242 REMARK("Capacity constructor complete.\n");
243 ASSERT(q->size()==0, "FAILED size test.");
244 ASSERT(q->empty(), "FAILED empty test.");
245 REMARK("Testing destructor.\n");
246 delete q;
247 REMARK("Destruction complete.\n");
248
249 REMARK("Testing allocator constructor.\n");
250 qi = new concurrent_priority_queue<int, std::less<int>, std::allocator<int> >(a);
251 REMARK("Allocator constructor complete.\n");
252 ASSERT(qi->size()==0, "FAILED size test.");
253 ASSERT(qi->empty(), "FAILED empty test.");
254 REMARK("Testing destructor.\n");
255 delete qi;
256 REMARK("Destruction complete.\n");
257
258 REMARK("Testing compare constructor.\n");
259 ql = new concurrent_priority_queue<int, less_a<int> >(l);
260 REMARK("Compare constructor complete.\n");
261 ASSERT(ql->size()==0, "FAILED size test.");
262 ASSERT(ql->empty(), "FAILED empty test.");
263 REMARK("Testing destructor.\n");
264 delete ql;
265 REMARK("Destruction complete.\n");
266
267 REMARK("Testing compare+allocator constructor.\n");
268 qla = new concurrent_priority_queue<int, less_a<int>, std::allocator<int> >(l, a);
269 REMARK("Compare+allocator constructor complete.\n");
270 ASSERT(qla->size()==0, "FAILED size test.");
271 ASSERT(qla->empty(), "FAILED empty test.");
272 REMARK("Testing destructor.\n");
273 delete qla;
274 REMARK("Destruction complete.\n");
275
276 REMARK("Testing capacity+allocator constructor.\n");
277 qi = new concurrent_priority_queue<int, std::less<int>, std::allocator<int> >(42, a);
278 REMARK("Capacity+allocator constructor complete.\n");
279 ASSERT(qi->size()==0, "FAILED size test.");
280 ASSERT(qi->empty(), "FAILED empty test.");
281 REMARK("Testing destructor.\n");
282 delete qi;
283 REMARK("Destruction complete.\n");
284
285 REMARK("Testing capacity+compare constructor.\n");
286 ql = new concurrent_priority_queue<int, less_a<int> >(42, l);
287 REMARK("Capacity+compare constructor complete.\n");
288 ASSERT(ql->size()==0, "FAILED size test.");
289 ASSERT(ql->empty(), "FAILED empty test.");
290 REMARK("Testing destructor.\n");
291 delete ql;
292 REMARK("Destruction complete.\n");
293
294 REMARK("Testing capacity+compare+allocator constructor.\n");
295 qla = new concurrent_priority_queue<int, less_a<int>, std::allocator<int> >(42, l, a);
296 REMARK("Capacity+compare+allocator constructor complete.\n");
297 ASSERT(qla->size()==0, "FAILED size test.");
298 ASSERT(qla->empty(), "FAILED empty test.");
299 REMARK("Testing destructor.\n");
300 delete qla;
301 REMARK("Destruction complete.\n");
302
303 REMARK("Destruction complete.\n");
304 REMARK("Testing iterator filler constructor.\n");
305 for (int i=0; i<42; ++i)
306 v.push_back(i);
307 q = new concurrent_priority_queue<int, std::less<int> >(v.begin(), v.end());
308 REMARK("Iterator filler constructor complete.\n");
309 ASSERT(q->size()==42, "FAILED vector/size test.");
310 ASSERT(!q->empty(), "FAILED vector/empty test.");
311 ASSERT(*q == v, "FAILED vector/equality test.");
312
313 REMARK("Destruction complete.\n");
314 REMARK("Testing iterator filler +compare constructor.\n");
315 ql = new concurrent_priority_queue<int, less_a<int> >(v.begin(), v.end(), l);
316 REMARK("Iterator filler +compare constructor complete.\n");
317 ASSERT(ql->size()==42, "FAILED vector/size test.");
318 ASSERT(!ql->empty(), "FAILED vector/empty test.");
319 REMARK("Testing destructor.\n");
320 delete ql;
321 REMARK("Destruction complete.\n");
322
323 REMARK("Testing copy constructor.\n");
324 qo = new concurrent_priority_queue<int, std::less<int> >(*q);
325 REMARK("Copy constructor complete.\n");
326 ASSERT(qo->size()==42, "FAILED cpq/size test.");
327 ASSERT(!qo->empty(), "FAILED cpq/empty test.");
328 ASSERT(*q == *qo, "FAILED cpq/equality test.");
329
330 REMARK("Testing destructor.\n");
331 delete q;
332 delete qo;
333 REMARK("Destruction complete.\n");
334}
335
336void TestAssignmentClearSwap() {
337 typedef concurrent_priority_queue<int, std::less<int> > cpq_type;
338 std::vector<int> v;
339 cpq_type *q, *qo;
340 int e;
341
342 for (int i=0; i<42; ++i)
343 v.push_back(i);
344 q = new cpq_type(v.begin(), v.end());
345 qo = new cpq_type();
346
347 REMARK("Testing assignment (1).\n");
348 *qo = *q;
349 REMARK("Assignment complete.\n");
350 ASSERT(qo->size()==42, "FAILED assignment/size test.");
351 ASSERT(!qo->empty(), "FAILED assignment/empty test.");
352 ASSERT(*qo == v,"FAILED assignment/equality test");
353
354 cpq_type assigned_q;
355 REMARK("Testing assign(begin,end) (2).\n");
356 assigned_q.assign(v.begin(), v.end());
357 REMARK("Assignment complete.\n");
358 ASSERT(assigned_q.size()==42, "FAILED assignment/size test.");
359 ASSERT(!assigned_q.empty(), "FAILED assignment/empty test.");
360 ASSERT(assigned_q == v,"FAILED assignment/equality test");
361
362 REMARK("Testing clear.\n");
363 q->clear();
364 REMARK("Clear complete.\n");
365 ASSERT(q->size()==0, "FAILED clear/size test.");
366 ASSERT(q->empty(), "FAILED clear/empty test.");
367
368 for (size_t i=0; i<5; ++i)
369 (void) qo->try_pop(e);
370
371 REMARK("Testing assignment (3).\n");
372 *q = *qo;
373 REMARK("Assignment complete.\n");
374 ASSERT(q->size()==37, "FAILED assignment/size test.");
375 ASSERT(!q->empty(), "FAILED assignment/empty test.");
376
377 for (size_t i=0; i<5; ++i)
378 (void) qo->try_pop(e);
379
380 REMARK("Testing swap.\n");
381 q->swap(*qo);
382 REMARK("Swap complete.\n");
383 ASSERT(q->size()==32, "FAILED swap/size test.");
384 ASSERT(!q->empty(), "FAILED swap/empty test.");
385 ASSERT(qo->size()==37, "FAILED swap_operand/size test.");
386 ASSERT(!qo->empty(), "FAILED swap_operand/empty test.");
387 delete q;
388 delete qo;
389}
390
391void TestSerialPushPop() {
392 concurrent_priority_queue<int, std::less<int> > *q;
393 int e=42, prev=INT_MAX;
394 size_t count=0;
395
396 q = new concurrent_priority_queue<int, std::less<int> >(MAX_ITER);
397 REMARK("Testing serial push.\n");
398 for (size_t i=0; i<MAX_ITER; ++i) {
399 push_selector(q, e, i);
400 e = e*-1 + int(i);
401 }
402 REMARK("Pushing complete.\n");
403 ASSERT(q->size()==MAX_ITER, "FAILED push/size test.");
404 ASSERT(!q->empty(), "FAILED push/empty test.");
405
406 REMARK("Testing serial pop.\n");
407 while (!q->empty()) {
408 ASSERT(q->try_pop(e), "FAILED pop test.");
409 ASSERT(prev>=e, "FAILED pop/priority test.");
410 prev = e;
411 ++count;
412 ASSERT(q->size()==MAX_ITER-count, "FAILED swap/size test.");
413 ASSERT(!q->empty() || count==MAX_ITER, "FAILED swap/empty test.");
414 }
415 ASSERT(!q->try_pop(e), "FAILED: successful pop from the empty queue.");
416 REMARK("Popping complete.\n");
417 delete q;
418}
419
420template <typename T, typename C>
421void TestParallelPushPop(int nThreads, T t_max, T t_min, C /*compare*/) {
422 size_t qsize;
423
424 concurrent_priority_queue<T, C> *q = new concurrent_priority_queue<T, C>(0);
425 FillBody<T, C> filler(nThreads, t_max, t_min, q);
426 EmptyBody<T, C> emptier(nThreads, t_max, q);
427 counter = 0;
428 REMARK("Testing parallel push.\n");
429 NativeParallelFor(nThreads, filler);
430 REMARK("Pushing complete.\n");
431 qsize = q->size();
432 ASSERT(q->size()==nThreads*MAX_ITER, "FAILED push/size test.");
433 ASSERT(!q->empty(), "FAILED push/empty test.");
434
435 REMARK("Testing parallel pop.\n");
436 NativeParallelFor(nThreads, emptier);
437 REMARK("Popping complete.\n");
438 ASSERT(counter==qsize, "FAILED pop/size test.");
439 ASSERT(q->size()==0, "FAILED pop/empty test.");
440
441 q->clear();
442 delete(q);
443}
444
445void TestExceptions() {
446#if TBB_USE_EXCEPTIONS
447 const size_t TOO_LARGE_SZ = 1000000000;
448 my_throwing_type elem;
449
450 REMARK("Testing basic constructor exceptions.\n");
451 // Allocate empty queue should not throw no matter the type
452 try {
453 my_throwing_type::throw_flag = 1;
454 cpq_ex_test_type q;
455 } catch(...) {
456#if !(_MSC_VER==1900)
457 ASSERT(false, "FAILED: allocating empty queue should not throw exception.\n");
458 // VS2015 warns about the code in this catch block being unreachable
459#endif
460 }
461 // Allocate small queue should not throw for reasonably sized type
462 try {
463 my_throwing_type::throw_flag = 1;
464 cpq_ex_test_type q(42);
465 } catch(...) {
466 ASSERT(false, "FAILED: allocating small queue should not throw exception.\n");
467 }
468 // Allocate a queue with too large initial size
469 try {
470 my_throwing_type::throw_flag = 0;
471 cpq_ex_test_type q(TOO_LARGE_SZ);
472 REMARK("FAILED: Huge queue did not throw exception.\n");
473 } catch(...) {}
474
475 cpq_ex_test_type *pq;
476 try {
477 my_throwing_type::throw_flag = 0;
478 pq = NULL;
479 pq = new cpq_ex_test_type(TOO_LARGE_SZ);
480 REMARK("FAILED: Huge queue did not throw exception.\n");
481 delete pq;
482 } catch(...) {
483 ASSERT(!pq, "FAILED: pq should not be touched when constructor throws.\n");
484 }
485 REMARK("Basic constructor exceptions testing complete.\n");
486 REMARK("Testing copy constructor exceptions.\n");
487 my_throwing_type::throw_flag = 0;
488 cpq_ex_test_type src_q(42);
489 elem.priority = 42;
490 for (size_t i=0; i<42; ++i) src_q.push(elem);
491 try {
492 my_throwing_type::throw_flag = 1;
493 cpq_ex_test_type q(src_q);
494 REMARK("FAILED: Copy construct did not throw exception.\n");
495 } catch(...) {}
496 try {
497 my_throwing_type::throw_flag = 1;
498 pq = NULL;
499 pq = new concurrent_priority_queue<my_throwing_type, my_less >(src_q);
500 REMARK("FAILED: Copy construct did not throw exception.\n");
501 delete pq;
502 } catch(...) {
503 ASSERT(!pq, "FAILED: pq should not be touched when constructor throws.\n");
504 }
505 REMARK("Copy constructor exceptions testing complete.\n");
506 REMARK("Testing assignment exceptions.\n");
507 // Assignment is copy-swap, so it should be exception safe
508 my_throwing_type::throw_flag = 0;
509 cpq_ex_test_type assign_q(24);
510 try {
511 my_throwing_type::throw_flag = 1;
512 assign_q = src_q;
513 REMARK("FAILED: Assign did not throw exception.\n");
514 } catch(...) {
515 ASSERT(assign_q.empty(), "FAILED: assign_q should be empty.\n");
516 }
517 REMARK("Assignment exceptions testing complete.\n");
518#ifndef __TBB_ITERATOR_DEBUGGING_EXCEPTIONS_BROKEN
519 REMARK("Testing push exceptions.\n");
520 for (size_t i=0; i<push_selector_variants; ++i) {
521 my_throwing_type::throw_flag = 0;
522 pq = new cpq_ex_test_type(3);
523 try {
524 push_selector(pq, elem, i);
525 push_selector(pq, elem, i);
526 push_selector(pq, elem, i);
527 } catch(...) {
528 ASSERT(false, "FAILED: Push should not throw exception... yet.\n");
529 }
530 try { // should crash on copy during expansion of vector
531 my_throwing_type::throw_flag = 1;
532 push_selector(pq, elem, i);
533 REMARK("FAILED: Push did not throw exception.\n");
534 } catch(...) {
535 ASSERT(!pq->empty(), "FAILED: pq should not be empty.\n");
536 ASSERT(pq->size()==3, "FAILED: pq should be only three elements.\n");
537 ASSERT(pq->try_pop(elem), "FAILED: pq is not functional.\n");
538 }
539 delete pq;
540
541 my_throwing_type::throw_flag = 0;
542 pq = new cpq_ex_test_type(3);
543 try {
544 push_selector(pq, elem, i);
545 push_selector(pq, elem, i);
546 } catch(...) {
547 ASSERT(false, "FAILED: Push should not throw exception... yet.\n");
548 }
549 try { // should crash on push copy of element
550 my_throwing_type::throw_flag = 1;
551 push_selector(pq, elem, i);
552 REMARK("FAILED: Push did not throw exception.\n");
553 } catch(...) {
554 ASSERT(!pq->empty(), "FAILED: pq should not be empty.\n");
555 ASSERT(pq->size()==2, "FAILED: pq should be only two elements.\n");
556 ASSERT(pq->try_pop(elem), "FAILED: pq is not functional.\n");
557 }
558 delete pq;
559 }
560 REMARK("Push exceptions testing complete.\n");
561#endif
562#endif // TBB_USE_EXCEPTIONS
563}
564
565template <typename T, typename C>
566void TestFlogger(int nThreads, T /*max*/, C /*compare*/) {
567 REMARK("Testing queue flogger.\n");
568 concurrent_priority_queue<T, C> *q = new concurrent_priority_queue<T, C> (0);
569 NativeParallelFor(nThreads, FloggerBody<T, C >(nThreads, q));
570 ASSERT(q->empty(), "FAILED flogger/empty test.");
571 ASSERT(!q->size(), "FAILED flogger/size test.");
572 REMARK("Flogging complete.\n");
573 delete q;
574}
575
576#if __TBB_INITIALIZER_LISTS_PRESENT
577#include "test_initializer_list.h"
578
579void TestInitList(){
580 REMARK("testing initializer_list methods \n");
581 using namespace initializer_list_support_tests;
582 TestInitListSupport<tbb::concurrent_priority_queue<char> >({1,2,3,4,5});
583 TestInitListSupport<tbb::concurrent_priority_queue<int> >({});
584}
585#endif //if __TBB_INITIALIZER_LISTS_PRESENT
586
587struct special_member_calls_t {
588 size_t copy_constructor_called_times;
589 size_t move_constructor_called_times;
590 size_t copy_assignment_called_times;
591 size_t move_assignment_called_times;
592
593 bool friend operator==(special_member_calls_t const& lhs, special_member_calls_t const& rhs){
594 return
595 lhs.copy_constructor_called_times == rhs.copy_constructor_called_times
596 && lhs.move_constructor_called_times == rhs.move_constructor_called_times
597 && lhs.copy_assignment_called_times == rhs.copy_assignment_called_times
598 && lhs.move_assignment_called_times == rhs.move_assignment_called_times;
599 }
600
601};
602#if __TBB_CPP11_RVALUE_REF_PRESENT
603struct MoveOperationTracker {
604 static size_t copy_constructor_called_times;
605 static size_t move_constructor_called_times;
606 static size_t copy_assignment_called_times;
607 static size_t move_assignment_called_times;
608
609 static special_member_calls_t special_member_calls(){
610 special_member_calls_t calls = {copy_constructor_called_times, move_constructor_called_times, copy_assignment_called_times, move_assignment_called_times};
611 return calls;
612 }
613 static size_t value_counter;
614
615 size_t value;
616
617 MoveOperationTracker() : value(++value_counter) {}
618 MoveOperationTracker( const size_t value_ ) : value( value_ ) {}
619 ~MoveOperationTracker() __TBB_NOEXCEPT( true ) {
620 value = 0;
621 }
622 MoveOperationTracker(const MoveOperationTracker& m) : value(m.value) {
623 ASSERT(m.value, "The object has been moved or destroyed");
624 ++copy_constructor_called_times;
625 }
626 MoveOperationTracker(MoveOperationTracker&& m) __TBB_NOEXCEPT(true) : value(m.value) {
627 ASSERT(m.value, "The object has been moved or destroyed");
628 m.value = 0;
629 ++move_constructor_called_times;
630 }
631 MoveOperationTracker& operator=(MoveOperationTracker const& m) {
632 ASSERT(m.value, "The object has been moved or destroyed");
633 value = m.value;
634 ++copy_assignment_called_times;
635 return *this;
636 }
637 MoveOperationTracker& operator=(MoveOperationTracker&& m) __TBB_NOEXCEPT(true) {
638 ASSERT(m.value, "The object has been moved or destroyed");
639 value = m.value;
640 m.value = 0;
641 ++move_assignment_called_times;
642 return *this;
643 }
644
645 bool operator<(MoveOperationTracker const &m) const {
646 ASSERT(value, "The object has been moved or destroyed");
647 ASSERT(m.value, "The object has been moved or destroyed");
648 return value < m.value;
649 }
650
651 friend bool operator==(MoveOperationTracker const &lhs, MoveOperationTracker const &rhs){
652 return !(lhs < rhs) && !(rhs <lhs);
653 }
654};
655size_t MoveOperationTracker::copy_constructor_called_times = 0;
656size_t MoveOperationTracker::move_constructor_called_times = 0;
657size_t MoveOperationTracker::copy_assignment_called_times = 0;
658size_t MoveOperationTracker::move_assignment_called_times = 0;
659size_t MoveOperationTracker::value_counter = 0;
660
661template<typename allocator = tbb::cache_aligned_allocator<MoveOperationTracker> >
662struct cpq_src_fixture : NoAssign {
663 enum {default_container_size = 100};
664 typedef concurrent_priority_queue<MoveOperationTracker, std::less<MoveOperationTracker>, typename allocator:: template rebind<MoveOperationTracker>::other > cpq_t;
665
666 cpq_t cpq_src;
667 const size_t container_size;
668
669 void init(){
670 size_t &mcct = MoveOperationTracker::move_constructor_called_times;
671 size_t &ccct = MoveOperationTracker::copy_constructor_called_times;
672 size_t &cact = MoveOperationTracker::copy_assignment_called_times;
673 size_t &mact = MoveOperationTracker::move_assignment_called_times;
674 mcct = ccct = cact = mact = 0;
675
676 for (size_t i=1; i <= container_size; ++i){
677 cpq_src.push(MoveOperationTracker(i));
678 }
679 ASSERT(cpq_src.size() == container_size, "error in test setup ?" );
680 }
681
682 cpq_src_fixture(size_t size = default_container_size) : container_size(size){
683 init();
684 }
685
686 cpq_src_fixture(typename cpq_t::allocator_type const& a, size_t size = default_container_size) : cpq_src(a), container_size(size){
687 init();
688 }
689
690};
691
692
693void TestStealingMoveConstructor(){
694 typedef cpq_src_fixture<> fixture_t;
695 fixture_t fixture;
696 fixture_t::cpq_t src_copy(fixture.cpq_src);
697
698 special_member_calls_t previous = MoveOperationTracker::special_member_calls();
699 fixture_t::cpq_t dst(std::move(fixture.cpq_src));
700 ASSERT(previous == MoveOperationTracker::special_member_calls(), "stealing move constructor should not create any new elements");
701
702 ASSERT(dst == src_copy, "cpq content changed during stealing move ?");
703}
704
705void TestStealingMoveConstructorOtherAllocatorInstance(){
706 typedef two_memory_arenas_fixture<MoveOperationTracker> arena_fixture_t;
707 typedef cpq_src_fixture<arena_fixture_t::allocator_t > fixture_t;
708
709 arena_fixture_t arena_fixture(8 * fixture_t::default_container_size, "TestStealingMoveConstructorOtherAllocatorInstance");
710 fixture_t fixture(arena_fixture.source_allocator);
711 fixture_t::cpq_t src_copy(fixture.cpq_src);
712
713 special_member_calls_t previous = MoveOperationTracker::special_member_calls();
714 fixture_t::cpq_t dst(std::move(fixture.cpq_src), arena_fixture.source_allocator);
715 ASSERT(previous == MoveOperationTracker::special_member_calls(), "stealing move constructor should not create any new elements");
716
717 ASSERT(dst == src_copy, "cpq content changed during stealing move ?");
718}
719
720void TestPerElementMoveConstructorOtherAllocatorInstance(){
721 typedef two_memory_arenas_fixture<MoveOperationTracker> arena_fixture_t;
722 typedef cpq_src_fixture<arena_fixture_t::allocator_t > fixture_t;
723
724 arena_fixture_t arena_fixture(8 * fixture_t::default_container_size, "TestPerElementMoveConstructorOtherAllocatorInstance");
725 fixture_t fixture(arena_fixture.source_allocator);
726 fixture_t::cpq_t src_copy(fixture.cpq_src);
727
728 special_member_calls_t move_ctor_called_cpq_size_times = MoveOperationTracker::special_member_calls();
729 move_ctor_called_cpq_size_times.move_constructor_called_times += fixture.container_size;
730
731 fixture_t::cpq_t dst(std::move(fixture.cpq_src), arena_fixture.dst_allocator);
732 ASSERT(move_ctor_called_cpq_size_times == MoveOperationTracker::special_member_calls(), "Per element move constructor should move initialize all new elements");
733 ASSERT(dst == src_copy, "cpq content changed during move ?");
734}
735
736void TestgMoveConstructor(){
737 TestStealingMoveConstructor();
738 TestStealingMoveConstructorOtherAllocatorInstance();
739 TestPerElementMoveConstructorOtherAllocatorInstance();
740}
741
742void TestStealingMoveAssignOperator(){
743 typedef cpq_src_fixture<> fixture_t;
744 fixture_t fixture;
745 fixture_t::cpq_t src_copy(fixture.cpq_src);
746
747 fixture_t::cpq_t dst;
748 special_member_calls_t previous = MoveOperationTracker::special_member_calls();
749 dst = std::move(fixture.cpq_src);
750 ASSERT(previous == MoveOperationTracker::special_member_calls(), "stealing move assign operator should not create any new elements");
751
752 ASSERT(dst == src_copy, "cpq content changed during stealing move ?");
753}
754
755void TestStealingMoveAssignOperatorWithStatefulAllocator(){
756 //Use stateful allocator which is propagated on assignment , i.e. POCMA = true
757 typedef two_memory_arenas_fixture<MoveOperationTracker, /*pocma =*/Harness::true_type> arena_fixture_t;
758 typedef cpq_src_fixture<arena_fixture_t::allocator_t > fixture_t;
759
760 arena_fixture_t arena_fixture(8 * fixture_t::default_container_size, "TestStealingMoveAssignOperatorWithStatefullAllocator");
761 fixture_t fixture(arena_fixture.source_allocator);
762 fixture_t::cpq_t src_copy(fixture.cpq_src);
763 fixture_t::cpq_t dst(arena_fixture.dst_allocator);
764
765 special_member_calls_t previous = MoveOperationTracker::special_member_calls();
766 dst = std::move(fixture.cpq_src);
767 ASSERT(previous == MoveOperationTracker::special_member_calls(), "stealing move assignment operator should not create any new elements");
768
769 ASSERT(dst == src_copy, "cpq content changed during stealing move ?");
770}
771
772void TestPerElementMoveAssignOperator(){
773 //use stateful allocator which is not propagate on assignment , i.e. POCMA = false
774 typedef two_memory_arenas_fixture<MoveOperationTracker, /*pocma =*/Harness::false_type> arena_fixture_t;
775 typedef cpq_src_fixture<arena_fixture_t::allocator_t > fixture_t;
776
777 arena_fixture_t arena_fixture(8 * fixture_t::default_container_size, "TestPerElementMoveAssignOperator");
778 fixture_t fixture(arena_fixture.source_allocator);
779 fixture_t::cpq_t src_copy(fixture.cpq_src);
780 fixture_t::cpq_t dst(arena_fixture.dst_allocator);
781
782 special_member_calls_t move_ctor_called_cpq_size_times = MoveOperationTracker::special_member_calls();
783 move_ctor_called_cpq_size_times.move_constructor_called_times += fixture.container_size;
784 dst = std::move(fixture.cpq_src);
785 ASSERT(move_ctor_called_cpq_size_times == MoveOperationTracker::special_member_calls(), "per element move assignment should move initialize new elements");
786
787 ASSERT(dst == src_copy, "cpq content changed during per element move ?");
788}
789
790void TestgMoveAssignOperator(){
791 TestStealingMoveAssignOperator();
792#if __TBB_ALLOCATOR_TRAITS_PRESENT
793 TestStealingMoveAssignOperatorWithStatefulAllocator();
794#endif //__TBB_ALLOCATOR_TRAITS_PRESENT
795 TestPerElementMoveAssignOperator();
796}
797
798struct ForwardInEmplaceTester {
799 int a;
800 static bool moveCtorCalled;
801 ForwardInEmplaceTester( int a_val ) : a( a_val ) {}
802 ForwardInEmplaceTester( ForwardInEmplaceTester&& obj, int a_val ) : a( obj.a ) {
803 moveCtorCalled = true;
804 obj.a = a_val;
805 }
806 bool operator<( ForwardInEmplaceTester const& ) const { return true; }
807};
808bool ForwardInEmplaceTester::moveCtorCalled = false;
809
810struct NoDefaultCtorType {
811 size_t value1, value2;
812 NoDefaultCtorType( size_t value1_, size_t value2_ ) : value1( value1_ ), value2( value2_ ) {}
813 bool operator<(NoDefaultCtorType const &m) const {
814 return value1+value2 < m.value1+m.value2;
815 }
816};
817
818void TestMoveSupportInPushPop() {
819 REMARK("Testing Move Support in Push/Pop...");
820 size_t &mcct = MoveOperationTracker::move_constructor_called_times;
821 size_t &ccct = MoveOperationTracker::copy_constructor_called_times;
822 size_t &cact = MoveOperationTracker::copy_assignment_called_times;
823 size_t &mact = MoveOperationTracker::move_assignment_called_times;
824 mcct = ccct = cact = mact = 0;
825
826 concurrent_priority_queue<MoveOperationTracker> q1;
827
828 ASSERT(mcct == 0, "Value must be zero-initialized");
829 ASSERT(ccct == 0, "Value must be zero-initialized");
830
831 q1.push(MoveOperationTracker());
832 ASSERT(mcct > 0, "Not working push(T&&)?");
833 ASSERT(ccct == 0, "Copying of arg occurred during push(T&&)");
834
835 MoveOperationTracker ob;
836 const size_t prev_mcct = mcct;
837 q1.push(std::move(ob));
838 ASSERT(mcct > prev_mcct, "Not working push(T&&)?");
839 ASSERT(ccct == 0, "Copying of arg occurred during push(T&&)");
840
841 ASSERT(cact == 0, "Copy assignment called during push(T&&)");
842 const size_t prev_mact = mact;
843 q1.try_pop(ob);
844 ASSERT(cact == 0, "Copy assignment called during try_pop(T&)");
845 ASSERT(mact > prev_mact, "Move assignment was not called during try_pop(T&)");
846
847 REMARK(" works.\n");
848
849#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
850 REMARK("Testing Emplace...");
851
852 concurrent_priority_queue<NoDefaultCtorType> q2;
853 q2.emplace(15, 3);
854 q2.emplace(2, 35);
855 q2.emplace(8, 8);
856
857 NoDefaultCtorType o(0, 0);
858 q2.try_pop(o);
859 ASSERT(o.value1 == 2 && o.value2 == 35, "Unexpected data popped; possible emplace() failure.");
860 q2.try_pop(o);
861 ASSERT(o.value1 == 15 && o.value2 == 3, "Unexpected data popped; possible emplace() failure.");
862 q2.try_pop(o);
863 ASSERT(o.value1 == 8 && o.value2 == 8, "Unexpected data popped; possible emplace() failure.");
864 ASSERT(!q2.try_pop(o), "The queue should be empty.");
865
866 //TODO: revise this test
867 concurrent_priority_queue<ForwardInEmplaceTester> q3;
868 ASSERT( ForwardInEmplaceTester::moveCtorCalled == false, NULL );
869 q3.emplace( ForwardInEmplaceTester(5), 2 );
870 ASSERT( ForwardInEmplaceTester::moveCtorCalled == true, "Not used std::forward in emplace()?" );
871 ForwardInEmplaceTester obj( 0 );
872 q3.try_pop( obj );
873 ASSERT( obj.a == 5, "Not used std::forward in emplace()?" );
874 ASSERT(!q3.try_pop( obj ), "The queue should be empty.");
875
876 REMARK(" works.\n");
877#endif /* __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT */
878}
879#endif /* __TBB_CPP11_RVALUE_REF_PRESENT */
880
881void TestCpqOnNThreads( int nThreads ) {
882 std::less<int> int_compare;
883 my_less data_compare;
884
885 TestConstructorsDestructorsAccessors();
886 TestAssignmentClearSwap();
887 TestSerialPushPop();
888
889 TestParallelPushPop( nThreads, INT_MAX, INT_MIN, int_compare );
890 TestParallelPushPop( nThreads, (unsigned char)CHAR_MAX, (unsigned char)CHAR_MIN, int_compare );
891 TestParallelPushPop( nThreads, DATA_MAX, DATA_MIN, data_compare );
892
893 TestFlogger( nThreads, INT_MAX, int_compare );
894 TestFlogger( nThreads, (unsigned char)CHAR_MAX, int_compare );
895 TestFlogger( nThreads, DATA_MAX, data_compare );
896#if __TBB_CPP11_RVALUE_REF_PRESENT
897 MoveOperationTracker::copy_assignment_called_times = 0;
898 TestFlogger( nThreads, MoveOperationTracker(), std::less<MoveOperationTracker>() );
899 ASSERT( MoveOperationTracker::copy_assignment_called_times == 0, "Copy assignment called during try_pop(T&)?" );
900#endif
901
902#if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
903 TestExceptions();
904#else
905 REPORT( "Known issue: exception handling tests are skipped.\n" );
906#endif
907}
908
909#if __TBB_CPP11_SMART_POINTERS_PRESENT
910struct SmartPointersCompare {
911 template <typename Type> bool operator() (const std::shared_ptr<Type> &t1, const std::shared_ptr<Type> &t2) {
912 return *t1 < *t2;
913 }
914 template <typename Type> bool operator() (const std::weak_ptr<Type> &t1, const std::weak_ptr<Type> &t2) {
915 return *t1.lock().get() < *t2.lock().get();
916 }
917 template <typename Type> bool operator() (const std::unique_ptr<Type> &t1, const std::unique_ptr<Type> &t2) {
918 return *t1 < *t2;
919 }
920};
921#endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */
922
923#if __TBB_CPP11_RVALUE_REF_PRESENT
924// The helper calls copying or moving push operator if an element has copy constructor.
925// Otherwise it calls only moving push operator.
926template <bool hasCopyCtor>
927struct QueuePushHelper {
928 template <typename Q, typename T>
929 static void push( Q &q, T &&t ) {
930 q.push( std::forward<T>(t) );
931 }
932};
933template <>
934template <typename Q, typename T>
935void QueuePushHelper<false>::push( Q &q, T &&t ) {
936 q.push( std::move(t) );
937}
938#else
939template <bool hasCopyCtor>
940struct QueuePushHelper {
941 template <typename Q, typename T>
942 static void push( Q &q, const T &t ) {
943 q.push( t );
944 }
945};
946#endif /* __TBB_CPP11_RVALUE_REF_PRESENT */
947
948template <bool hasCopyCtor, typename Queue>
949void Examine(Queue &q1, Queue &q2, const std::vector<typename Queue::value_type> &vecSorted) {
950 typedef typename Queue::value_type ValueType;
951
952 ASSERT(!q1.empty() && q1.size() == vecSorted.size(), NULL);
953
954 ValueType elem;
955
956 q2.clear();
957 ASSERT(q2.empty() && !q2.size() && !q2.try_pop(elem), NULL);
958
959 typename std::vector<ValueType>::const_reverse_iterator it1;
960 for (it1 = vecSorted.rbegin(); q1.try_pop(elem); it1++) {
961 ASSERT( Harness::IsEqual()(elem, *it1), NULL );
962 if ( std::distance(vecSorted.rbegin(), it1) % 2 )
963 QueuePushHelper<hasCopyCtor>::push(q2,elem);
964 else
965 QueuePushHelper<hasCopyCtor>::push(q2,tbb::internal::move(elem));
966 }
967 ASSERT(it1 == vecSorted.rend(), NULL);
968 ASSERT(q1.empty() && !q1.size(), NULL);
969 ASSERT(!q2.empty() && q2.size() == vecSorted.size(), NULL);
970
971 q1.swap(q2);
972 ASSERT(q2.empty() && !q2.size(), NULL);
973 ASSERT(!q1.empty() && q1.size() == vecSorted.size(), NULL);
974 for (it1 = vecSorted.rbegin(); q1.try_pop(elem); it1++) ASSERT(Harness::IsEqual()(elem, *it1), NULL);
975 ASSERT(it1 == vecSorted.rend(), NULL);
976
977 typename Queue::allocator_type a = q1.get_allocator();
978 ValueType *ptr = a.allocate(1);
979 ASSERT(ptr, NULL);
980 a.deallocate(ptr, 1);
981}
982
983template <typename Queue>
984void Examine(const Queue &q, const std::vector<typename Queue::value_type> &vecSorted) {
985 Queue q1(q), q2(q);
986 Examine</*hasCopyCtor=*/true>( q1, q2, vecSorted );
987}
988
989template <typename ValueType, typename Compare>
990void TypeTester(const std::vector<ValueType> &vec, Compare comp) {
991 typedef tbb::concurrent_priority_queue<ValueType, Compare> Queue;
992 typedef tbb::concurrent_priority_queue< ValueType, Compare, debug_allocator<ValueType> > QueueDebugAlloc;
993 __TBB_ASSERT(vec.size() >= 5, "Array should have at least 5 elements");
994
995 std::vector<ValueType> vecSorted(vec);
996 std::sort( vecSorted.begin(), vecSorted.end(), comp );
997
998 // Construct an empty queue.
999 Queue q1;
1000 q1.assign(vec.begin(), vec.end());
1001 Examine(q1, vecSorted);
1002#if __TBB_INITIALIZER_LISTS_PRESENT
1003 // Constructor from initializer_list.
1004 Queue q2({ vec[0], vec[1], vec[2] });
1005 for (typename std::vector<ValueType>::const_iterator it = vec.begin() + 3; it != vec.end(); ++it) q2.push(*it);
1006 Examine(q2, vecSorted);
1007 Queue q3;
1008 q3 = { vec[0], vec[1], vec[2] };
1009 for (typename std::vector<ValueType>::const_iterator it = vec.begin() + 3; it != vec.end(); ++it) q3.push(*it);
1010 Examine(q3, vecSorted);
1011#endif
1012 // Copying constructor.
1013 Queue q4(q1);
1014 Examine(q4, vecSorted);
1015 // Construct with non-default allocator.
1016 QueueDebugAlloc q5;
1017 q5.assign(vec.begin(), vec.end());
1018 Examine(q5, vecSorted);
1019 // Copying constructor for vector with different allocator type.
1020 QueueDebugAlloc q6(q5);
1021 Examine(q6, vecSorted);
1022 // Construction with copying iteration range and given allocator instance.
1023 Queue q7(vec.begin(), vec.end());
1024 Examine(q7, vecSorted);
1025 typename QueueDebugAlloc::allocator_type a;
1026 QueueDebugAlloc q8(a);
1027 q8.assign(vec.begin(), vec.end());
1028 Examine(q8, vecSorted);
1029}
1030
1031#if __TBB_CPP11_SMART_POINTERS_PRESENT && __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_IS_COPY_CONSTRUCTIBLE_PRESENT
1032template <typename T>
1033void TypeTesterUniquePtr(const std::vector<T> &vec) {
1034 __TBB_ASSERT(vec.size() >= 5, "Array should have at least 5 elements");
1035
1036 typedef std::unique_ptr<T> ValueType;
1037 typedef tbb::concurrent_priority_queue<ValueType, SmartPointersCompare> Queue;
1038 typedef tbb::concurrent_priority_queue< ValueType, SmartPointersCompare, debug_allocator<ValueType> > QueueDebugAlloc;
1039
1040 std::vector<ValueType> vecSorted;
1041 for ( typename std::vector<T>::const_iterator it = vec.begin(); it != vec.end(); ++it ) {
1042 vecSorted.push_back( ValueType(new T(*it)) );
1043 }
1044 std::sort( vecSorted.begin(), vecSorted.end(), SmartPointersCompare() );
1045
1046 Queue q1, q1Copy;
1047 QueueDebugAlloc q2, q2Copy;
1048 for ( typename std::vector<T>::const_iterator it = vec.begin(); it != vec.end(); ++it ) {
1049 q1.push( ValueType(new T(*it)) );
1050 q1Copy.push( ValueType(new T(*it)) );
1051 q2.push( ValueType(new T(*it)) );
1052 q2Copy.push( ValueType(new T(*it)) );
1053 }
1054 Examine</*isCopyCtor=*/false>(q1, q1Copy, vecSorted);
1055 Examine</*isCopyCtor=*/false>(q2, q2Copy, vecSorted);
1056
1057#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1058 Queue q3Copy;
1059 QueueDebugAlloc q4Copy;
1060
1061 q1.clear();
1062 q2.clear();
1063 for ( typename std::vector<T>::const_iterator it = vec.begin(); it != vec.end(); ++it ) {
1064 q1.emplace( new T(*it) );
1065 q3Copy.emplace( new T(*it) );
1066 q2.emplace( new T(*it) );
1067 q4Copy.emplace( new T(*it) );
1068 }
1069
1070 Queue q3( std::move(q1) );
1071 QueueDebugAlloc q4( std::move(q2) );
1072 Examine</*isCopyCtor=*/false>(q3, q3Copy, vecSorted);
1073 Examine</*isCopyCtor=*/false>(q4, q4Copy, vecSorted);
1074#endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1075}
1076#endif /* __TBB_CPP11_SMART_POINTERS_PRESENT && __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_IS_COPY_CONSTRUCTIBLE_PRESENT */
1077
1078template <typename ValueType>
1079void TypeTester(const std::vector<ValueType> &vec) { TypeTester(vec, std::less<ValueType>()); }
1080
1081void TestTypes() {
1082 const int NUMBER = 10;
1083
1084 Harness::FastRandom rnd(1234);
1085
1086 std::vector<int> arrInt;
1087 for (int i = 0; i<NUMBER; ++i) arrInt.push_back(rnd.get());
1088 std::vector< tbb::atomic<int> > arrTbb;
1089 for (int i = 0; i<NUMBER; ++i) {
1090 tbb::atomic<int> a;
1091 a = rnd.get();
1092 arrTbb.push_back(a);
1093 }
1094
1095 TypeTester(arrInt);
1096 TypeTester(arrTbb);
1097
1098#if __TBB_CPP11_SMART_POINTERS_PRESENT
1099 std::vector< std::shared_ptr<int> > arrShr;
1100 for (int i = 0; i<NUMBER; ++i) {
1101 const int rnd_get = rnd.get();
1102 arrShr.push_back(std::make_shared<int>(rnd_get));
1103 }
1104 std::vector< std::weak_ptr<int> > arrWk;
1105 std::copy(arrShr.begin(), arrShr.end(), std::back_inserter(arrWk));
1106 TypeTester(arrShr, SmartPointersCompare());
1107 TypeTester(arrWk, SmartPointersCompare());
1108
1109#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_IS_COPY_CONSTRUCTIBLE_PRESENT
1110#if __TBB_IS_COPY_CONSTRUCTIBLE_BROKEN
1111 REPORT( "Known issue: std::is_copy_constructible is broken for move-only types. So the std::unique_ptr test is skipped.\n" );
1112#else
1113 TypeTesterUniquePtr(arrInt);
1114#endif /* __TBB_IS_COPY_CONSTRUCTIBLE_BROKEN */
1115#endif /* __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_IS_COPY_CONSTRUCTIBLE_PRESENT */
1116#else
1117 REPORT( "Known issue: C++11 smart pointer tests are skipped.\n" );
1118#endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */
1119}
1120
1121#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1122template <template <typename...>typename TQueue>
1123void TestDeductionGuides() {
1124 using ComplexType = const std::string*;
1125 std::string s("s");
1126 std::vector<ComplexType> v;
1127 auto l = {ComplexType(&s), ComplexType(&s) };
1128
1129 // check TQueue(InputIterator, InputIterator)
1130 TQueue qv(v.begin(), v.end());
1131 static_assert(std::is_same<decltype(qv), TQueue<ComplexType> >::value);
1132
1133 // check TQueue(InputIterator, InputIterator, Allocator)
1134 TQueue qva(v.begin(), v.end(), std::allocator<ComplexType>());
1135 static_assert(std::is_same<decltype(qva), TQueue<ComplexType, std::less<ComplexType>,
1136 std::allocator<ComplexType>>>::value);
1137
1138 // check TQueue(InputIterator, InputIterator, Compare)
1139 TQueue qvc(v.begin(), v.end(), less_a<ComplexType>(true));
1140 static_assert(std::is_same<decltype(qvc), TQueue<ComplexType, less_a<ComplexType>>>::value);
1141
1142 // check TQueue(InputIterator, InputIterator, Compare, Allocator)
1143 TQueue qvca(v.begin(), v.end(), less_a<ComplexType>(true), std::allocator<ComplexType>());
1144 static_assert(std::is_same<decltype(qvca), TQueue<ComplexType, less_a<ComplexType>,
1145 std::allocator<ComplexType>>>::value);
1146
1147 // check TQueue(std::initializer_list)
1148 TQueue ql(l);
1149 static_assert(std::is_same<decltype(ql), TQueue<ComplexType>>::value);
1150
1151 // check TQueue(std::initializer_list, Allocator)
1152 TQueue qla(l, std::allocator<ComplexType>());
1153 static_assert(std::is_same<decltype(qla), TQueue<ComplexType, std::less<ComplexType>,
1154 std::allocator<ComplexType>>>::value);
1155
1156 // check TQueue(std::initializer_list, Compare)
1157 TQueue qlc(l, less_a<ComplexType>(true));
1158 static_assert(std::is_same<decltype(qlc), TQueue<ComplexType, less_a<ComplexType>>>::value);
1159
1160 // check TQueue(std::initializer_list, Compare, Allocator)
1161 TQueue qlca(l, less_a<ComplexType>(true), std::allocator<ComplexType>());
1162 static_assert(std::is_same<decltype(qlca), TQueue<ComplexType, less_a<ComplexType>,
1163 std::allocator<ComplexType>>>::value);
1164
1165 // check TQueue(TQueue &)
1166 TQueue qc(qv);
1167 static_assert(std::is_same<decltype(qv), decltype(qv)>::value);
1168
1169 // check TQueue(TQueue &, Allocator)
1170 TQueue qca(qva, std::allocator<ComplexType>());
1171 static_assert(std::is_same<decltype(qca), decltype(qva)>::value);
1172
1173 // check TQueue(TQueue &&)
1174 TQueue qm(std::move(qv));
1175 static_assert(std::is_same<decltype(qm), decltype(qv)>::value);
1176
1177 // check TQueue(TQueue &&, Allocator)
1178 TQueue qma(std::move(qva), std::allocator<ComplexType>());
1179 static_assert(std::is_same<decltype(qma), decltype(qva)>::value);
1180}
1181#endif
1182
1183int TestMain() {
1184 if (MinThread < 1)
1185 MinThread = 1;
1186
1187 TestHelpers();
1188#if __TBB_INITIALIZER_LISTS_PRESENT
1189 TestInitList();
1190#else
1191 REPORT("Known issue: initializer list tests are skipped.\n");
1192#endif
1193
1194 TestTypes();
1195
1196#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1197 TestDeductionGuides<tbb::concurrent_priority_queue>();
1198#endif
1199
1200#if __TBB_CPP11_RVALUE_REF_PRESENT
1201 TestgMoveConstructor();
1202 TestgMoveAssignOperator();
1203 TestMoveSupportInPushPop();
1204#else
1205 REPORT("Known issue: move support tests are skipped.\n");
1206#endif
1207
1208 for (int p = MinThread; p <= MaxThread; ++p) {
1209 REMARK("Testing on %d threads.\n", p);
1210 TestCpqOnNThreads(p);
1211 }
1212 return Harness::Done;
1213}
1214