2 Copyright (c) 2005-2017 Intel Corporation
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "harness_defs.h"
22 #include "tbb/concurrent_priority_queue.h"
23 #include "tbb/atomic.h"
24 #include "tbb/blocked_range.h"
28 #include "harness_allocator.h"
30 #include "test_container_move_support.h"
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 )
38 #if _MSC_VER==1500 && !__INTEL_COMPILER
39 #pragma warning( pop )
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
53 const size_t MAX_ITER = 10000;
55 tbb::atomic<unsigned int> counter;
60 char padding[tbb::internal::NFS_MaxLineSize - sizeof(int) % tbb::internal::NFS_MaxLineSize];
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);
66 bool operator==(const my_data_type& other) const {
67 return this->priority == other.priority;
71 const my_data_type DATA_MIN(INT_MIN);
72 const my_data_type DATA_MAX(INT_MAX);
76 bool operator()(const my_data_type d1, const my_data_type d2) const {
77 return d1.priority<d2.priority;
81 #if TBB_USE_EXCEPTIONS
82 class my_throwing_type : public my_data_type {
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;
91 int my_throwing_type::throw_flag = 0;
93 typedef concurrent_priority_queue<my_throwing_type, my_less > cpq_ex_test_type;
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;
101 const size_t push_selector_variants = 1;
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;
117 template<typename T, typename C>
118 class FillBody : NoAssign {
121 concurrent_priority_queue<T, C> *q;
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) {
129 push_selector(q, elem, i);
130 if (elem == my_max) elem = my_min;
131 elem = elem + T(nThread);
136 template<typename T, typename C>
137 struct EmptyBody : NoAssign {
140 concurrent_priority_queue<T, C> *q;
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)) {
148 while(q->try_pop(elem)) {
149 ASSERT(!less_than(last, elem), "FAILED pop/priority test in EmptyBody.");
158 template <typename T, typename C>
159 class FloggerBody : NoAssign {
161 concurrent_priority_queue<T, C> *q;
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);
174 namespace equality_comparison_helpers {
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());
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);
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());
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?");
216 void TestConstructorsDestructorsAccessors() {
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;
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");
230 REMARK("Destruction complete.\n");
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");
239 REMARK("Destruction complete.\n");
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");
248 REMARK("Destruction complete.\n");
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");
257 REMARK("Destruction complete.\n");
259 REMARK("Testing iterator filler constructor.\n");
260 for (int i=0; i<42; ++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.");
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.");
275 REMARK("Testing destructor.\n");
278 REMARK("Destruction complete.\n");
281 void TestAssignmentClearSwap() {
282 typedef concurrent_priority_queue<int, std::less<int> > cpq_type;
287 for (int i=0; i<42; ++i)
289 q = new cpq_type(v.begin(), v.end());
292 REMARK("Testing assignment (1).\n");
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");
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");
307 REMARK("Testing clear.\n");
309 REMARK("Clear complete.\n");
310 ASSERT(q->size()==0, "FAILED clear/size test.");
311 ASSERT(q->empty(), "FAILED clear/empty test.");
313 for (size_t i=0; i<5; ++i)
314 (void) qo->try_pop(e);
316 REMARK("Testing assignment (3).\n");
318 REMARK("Assignment complete.\n");
319 ASSERT(q->size()==37, "FAILED assignment/size test.");
320 ASSERT(!q->empty(), "FAILED assignment/empty test.");
322 for (size_t i=0; i<5; ++i)
323 (void) qo->try_pop(e);
325 REMARK("Testing swap.\n");
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.");
336 void TestSerialPushPop() {
337 concurrent_priority_queue<int, std::less<int> > *q;
338 int e=42, prev=INT_MAX;
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);
347 REMARK("Pushing complete.\n");
348 ASSERT(q->size()==MAX_ITER, "FAILED push/size test.");
349 ASSERT(!q->empty(), "FAILED push/empty test.");
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.");
357 ASSERT(q->size()==MAX_ITER-count, "FAILED swap/size test.");
358 ASSERT(!q->empty() || count==MAX_ITER, "FAILED swap/empty test.");
360 ASSERT(!q->try_pop(e), "FAILED: successful pop from the empty queue.");
361 REMARK("Popping complete.\n");
365 template <typename T, typename C>
366 void TestParallelPushPop(int nThreads, T t_max, T t_min, C /*compare*/) {
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);
373 REMARK("Testing parallel push.\n");
374 NativeParallelFor(nThreads, filler);
375 REMARK("Pushing complete.\n");
377 ASSERT(q->size()==nThreads*MAX_ITER, "FAILED push/size test.");
378 ASSERT(!q->empty(), "FAILED push/empty test.");
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.");
390 void TestExceptions() {
391 #if TBB_USE_EXCEPTIONS
392 const size_t TOO_LARGE_SZ = 1000000000;
393 my_throwing_type elem;
395 REMARK("Testing basic constructor exceptions.\n");
396 // Allocate empty queue should not throw no matter the type
398 my_throwing_type::throw_flag = 1;
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
406 // Allocate small queue should not throw for reasonably sized type
408 my_throwing_type::throw_flag = 1;
409 cpq_ex_test_type q(42);
411 ASSERT(false, "FAILED: allocating small queue should not throw exception.\n");
413 // Allocate a queue with too large initial size
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");
420 cpq_ex_test_type *pq;
422 my_throwing_type::throw_flag = 0;
424 pq = new cpq_ex_test_type(TOO_LARGE_SZ);
425 REMARK("FAILED: Huge queue did not throw exception.\n");
428 ASSERT(!pq, "FAILED: pq should not be touched when constructor throws.\n");
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);
435 for (size_t i=0; i<42; ++i) src_q.push(elem);
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");
442 my_throwing_type::throw_flag = 1;
444 pq = new concurrent_priority_queue<my_throwing_type, my_less >(src_q);
445 REMARK("FAILED: Copy construct did not throw exception.\n");
448 ASSERT(!pq, "FAILED: pq should not be touched when constructor throws.\n");
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);
456 my_throwing_type::throw_flag = 1;
458 REMARK("FAILED: Assign did not throw exception.\n");
460 ASSERT(assign_q.empty(), "FAILED: assign_q should be empty.\n");
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);
469 push_selector(pq, elem, i);
470 push_selector(pq, elem, i);
471 push_selector(pq, elem, i);
473 ASSERT(false, "FAILED: Push should not throw exception... yet.\n");
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");
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");
486 my_throwing_type::throw_flag = 0;
487 pq = new cpq_ex_test_type(3);
489 push_selector(pq, elem, i);
490 push_selector(pq, elem, i);
492 ASSERT(false, "FAILED: Push should not throw exception... yet.\n");
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");
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");
505 REMARK("Push exceptions testing complete.\n");
507 #endif // TBB_USE_EXCEPTIONS
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");
521 #if __TBB_INITIALIZER_LISTS_PRESENT
522 #include "test_initializer_list.h"
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> >({});
530 #endif //if __TBB_INITIALIZER_LISTS_PRESENT
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;
538 bool friend operator==(special_member_calls_t const& lhs, special_member_calls_t const& rhs){
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;
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;
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};
558 static size_t value_counter;
562 MoveOperationTracker() : value(++value_counter) {}
563 MoveOperationTracker( const size_t value_ ) : value( value_ ) {}
564 ~MoveOperationTracker() __TBB_NOEXCEPT( true ) {
567 MoveOperationTracker(const MoveOperationTracker& m) : value(m.value) {
568 ASSERT(m.value, "The object has been moved or destroyed");
569 ++copy_constructor_called_times;
571 MoveOperationTracker(MoveOperationTracker&& m) __TBB_NOEXCEPT(true) : value(m.value) {
572 ASSERT(m.value, "The object has been moved or destroyed");
574 ++move_constructor_called_times;
576 MoveOperationTracker& operator=(MoveOperationTracker const& m) {
577 ASSERT(m.value, "The object has been moved or destroyed");
579 ++copy_assignment_called_times;
582 MoveOperationTracker& operator=(MoveOperationTracker&& m) __TBB_NOEXCEPT(true) {
583 ASSERT(m.value, "The object has been moved or destroyed");
586 ++move_assignment_called_times;
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;
596 friend bool operator==(MoveOperationTracker const &lhs, MoveOperationTracker const &rhs){
597 return !(lhs < rhs) && !(rhs <lhs);
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;
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;
612 const size_t container_size;
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;
621 for (size_t i=1; i <= container_size; ++i){
622 cpq_src.push(MoveOperationTracker(i));
624 ASSERT(cpq_src.size() == container_size, "error in test setup ?" );
627 cpq_src_fixture(size_t size = default_container_size) : container_size(size){
631 cpq_src_fixture(typename cpq_t::allocator_type const& a, size_t size = default_container_size) : cpq_src(a), container_size(size){
638 void TestStealingMoveConstructor(){
639 typedef cpq_src_fixture<> fixture_t;
641 fixture_t::cpq_t src_copy(fixture.cpq_src);
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");
647 ASSERT(dst == src_copy, "cpq content changed during stealing move ?");
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;
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);
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");
662 ASSERT(dst == src_copy, "cpq content changed during stealing move ?");
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;
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);
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;
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 ?");
681 void TestgMoveConstructor(){
682 TestStealingMoveConstructor();
683 TestStealingMoveConstructorOtherAllocatorInstance();
684 TestPerElementMoveConstructorOtherAllocatorInstance();
687 void TestStealingMoveAssignOperator(){
688 typedef cpq_src_fixture<> fixture_t;
690 fixture_t::cpq_t src_copy(fixture.cpq_src);
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");
697 ASSERT(dst == src_copy, "cpq content changed during stealing move ?");
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;
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);
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");
714 ASSERT(dst == src_copy, "cpq content changed during stealing move ?");
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;
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);
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");
732 ASSERT(dst == src_copy, "cpq content changed during per element move ?");
735 void TestgMoveAssignOperator(){
736 TestStealingMoveAssignOperator();
737 #if __TBB_ALLOCATOR_TRAITS_PRESENT
738 TestStealingMoveAssignOperatorWithStatefulAllocator();
739 #endif //__TBB_ALLOCATOR_TRAITS_PRESENT
740 TestPerElementMoveAssignOperator();
743 struct ForwardInEmplaceTester {
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;
751 bool operator<( ForwardInEmplaceTester const& ) const { return true; }
753 bool ForwardInEmplaceTester::moveCtorCalled = false;
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;
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;
771 concurrent_priority_queue<MoveOperationTracker> q1;
773 ASSERT(mcct == 0, "Value must be zero-initialized");
774 ASSERT(ccct == 0, "Value must be zero-initialized");
776 q1.push(MoveOperationTracker());
777 ASSERT(mcct > 0, "Not working push(T&&)?");
778 ASSERT(ccct == 0, "Copying of arg occurred during push(T&&)");
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&&)");
786 ASSERT(cact == 0, "Copy assignment called during push(T&&)");
787 const size_t prev_mact = mact;
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&)");
794 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
795 REMARK("Testing Emplace...");
797 concurrent_priority_queue<NoDefaultCtorType> q2;
802 NoDefaultCtorType o(0, 0);
804 ASSERT(o.value1 == 2 && o.value2 == 35, "Unexpected data popped; possible emplace() failure.");
806 ASSERT(o.value1 == 15 && o.value2 == 3, "Unexpected data popped; possible emplace() failure.");
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.");
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 );
818 ASSERT( obj.a == 5, "Not used std::forward in emplace()?" );
819 ASSERT(!q3.try_pop( obj ), "The queue should be empty.");
822 #endif /* __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT */
824 #endif /* __TBB_CPP11_RVALUE_REF_PRESENT */
826 void TestCpqOnNThreads( int nThreads ) {
827 std::less<int> int_compare;
828 my_less data_compare;
830 TestConstructorsDestructorsAccessors();
831 TestAssignmentClearSwap();
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 );
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&)?" );
847 #if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN
850 REPORT( "Known issue: exception handling tests are skipped.\n" );
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) {
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();
862 template <typename Type> bool operator() (const std::unique_ptr<Type> &t1, const std::unique_ptr<Type> &t2) {
866 #endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */
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) );
879 template <typename Q, typename T>
880 void QueuePushHelper<false>::push( Q &q, T &&t ) {
881 q.push( std::move(t) );
884 template <bool hasCopyCtor>
885 struct QueuePushHelper {
886 template <typename Q, typename T>
887 static void push( Q &q, const T &t ) {
891 #endif /* __TBB_CPP11_RVALUE_REF_PRESENT */
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;
897 ASSERT(!q1.empty() && q1.size() == vecSorted.size(), NULL);
902 ASSERT(q2.empty() && !q2.size() && !q2.try_pop(elem), NULL);
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);
910 QueuePushHelper<hasCopyCtor>::push(q2,tbb::internal::move(elem));
912 ASSERT(it1 == vecSorted.rend(), NULL);
913 ASSERT(q1.empty() && !q1.size(), NULL);
914 ASSERT(!q2.empty() && q2.size() == vecSorted.size(), NULL);
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);
922 typename Queue::allocator_type a = q1.get_allocator();
923 ValueType *ptr = a.allocate(1);
925 a.deallocate(ptr, 1);
928 template <typename Queue>
929 void Examine(const Queue &q, const std::vector<typename Queue::value_type> &vecSorted) {
931 Examine</*hasCopyCtor=*/true>( q1, q2, vecSorted );
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");
940 std::vector<ValueType> vecSorted(vec);
941 std::sort( vecSorted.begin(), vecSorted.end(), comp );
943 // Construct an empty queue.
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);
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);
957 // Copying constructor.
959 Examine(q4, vecSorted);
960 // Construct with non-default allocator.
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);
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");
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;
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)) );
989 std::sort( vecSorted.begin(), vecSorted.end(), SmartPointersCompare() );
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)) );
999 Examine</*isCopyCtor=*/false>(q1, q1Copy, vecSorted);
1000 Examine</*isCopyCtor=*/false>(q2, q2Copy, vecSorted);
1002 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1004 QueueDebugAlloc q4Copy;
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) );
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
1021 #endif /* __TBB_CPP11_SMART_POINTERS_PRESENT && __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_IS_COPY_CONSTRUCTIBLE_PRESENT */
1023 template <typename ValueType>
1024 void TypeTester(const std::vector<ValueType> &vec) { TypeTester(vec, std::less<ValueType>()); }
1027 const int NUMBER = 10;
1029 Harness::FastRandom rnd(1234);
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) {
1037 arrTbb.push_back(a);
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));
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());
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" );
1058 TypeTesterUniquePtr(arrInt);
1059 #endif /* __TBB_IS_COPY_CONSTRUCTIBLE_BROKEN */
1060 #endif /* __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_IS_COPY_CONSTRUCTIBLE_PRESENT */
1062 REPORT( "Known issue: C++11 smart pointer tests are skipped.\n" );
1063 #endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */
1071 #if __TBB_INITIALIZER_LISTS_PRESENT
1074 REPORT("Known issue: initializer list tests are skipped.\n");
1079 #if __TBB_CPP11_RVALUE_REF_PRESENT
1080 TestgMoveConstructor();
1081 TestgMoveAssignOperator();
1082 TestMoveSupportInPushPop();
1084 REPORT("Known issue: move support tests are skipped.\n");
1087 for (int p = MinThread; p <= MaxThread; ++p) {
1088 REMARK("Testing on %d threads.\n", p);
1089 TestCpqOnNThreads(p);
1091 return Harness::Done;