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