1*51c0b2f7Stbbdev /* 2*51c0b2f7Stbbdev Copyright (c) 2005-2020 Intel Corporation 3*51c0b2f7Stbbdev 4*51c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License"); 5*51c0b2f7Stbbdev you may not use this file except in compliance with the License. 6*51c0b2f7Stbbdev You may obtain a copy of the License at 7*51c0b2f7Stbbdev 8*51c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0 9*51c0b2f7Stbbdev 10*51c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software 11*51c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS, 12*51c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*51c0b2f7Stbbdev See the License for the specific language governing permissions and 14*51c0b2f7Stbbdev limitations under the License. 15*51c0b2f7Stbbdev */ 16*51c0b2f7Stbbdev 17*51c0b2f7Stbbdev #include <common/test.h> 18*51c0b2f7Stbbdev #include <common/utils.h> 19*51c0b2f7Stbbdev #include <common/utils_report.h> 20*51c0b2f7Stbbdev #include <common/custom_allocators.h> 21*51c0b2f7Stbbdev #include <common/container_move_support.h> 22*51c0b2f7Stbbdev 23*51c0b2f7Stbbdev #include <tbb/concurrent_queue.h> 24*51c0b2f7Stbbdev #include <tbb/cache_aligned_allocator.h> 25*51c0b2f7Stbbdev #include <type_traits> 26*51c0b2f7Stbbdev #include <atomic> 27*51c0b2f7Stbbdev 28*51c0b2f7Stbbdev //! \file conformance_concurrent_queue.cpp 29*51c0b2f7Stbbdev //! \brief Test for [containers.concurrent_queue containers.concurrent_bounded_queue] specification 30*51c0b2f7Stbbdev 31*51c0b2f7Stbbdev template <typename T> 32*51c0b2f7Stbbdev using test_allocator = StaticSharedCountingAllocator<tbb::cache_aligned_allocator<T>>; 33*51c0b2f7Stbbdev 34*51c0b2f7Stbbdev static constexpr std::size_t MinThread = 1; 35*51c0b2f7Stbbdev static constexpr std::size_t MaxThread = 4; 36*51c0b2f7Stbbdev 37*51c0b2f7Stbbdev static constexpr std::size_t MAXTHREAD = 256; 38*51c0b2f7Stbbdev 39*51c0b2f7Stbbdev static constexpr std::size_t M = 10000; 40*51c0b2f7Stbbdev static std::atomic<long> PopKind[3]; 41*51c0b2f7Stbbdev 42*51c0b2f7Stbbdev static int Sum[MAXTHREAD]; 43*51c0b2f7Stbbdev 44*51c0b2f7Stbbdev template<typename CQ, typename ValueType, typename CounterType> 45*51c0b2f7Stbbdev void push(CQ& q, ValueType v, CounterType i) { 46*51c0b2f7Stbbdev switch (i % 3) { 47*51c0b2f7Stbbdev case 0: q.push( v); break; 48*51c0b2f7Stbbdev case 1: q.push( std::move(v)); break; 49*51c0b2f7Stbbdev case 2: q.emplace( v); break; 50*51c0b2f7Stbbdev default: CHECK(false); break; 51*51c0b2f7Stbbdev } 52*51c0b2f7Stbbdev } 53*51c0b2f7Stbbdev 54*51c0b2f7Stbbdev template<typename T> 55*51c0b2f7Stbbdev class ConcQWithCapacity : public tbb::concurrent_queue<T, test_allocator<T>> { 56*51c0b2f7Stbbdev using base_type = tbb::concurrent_queue<T, test_allocator<T>>; 57*51c0b2f7Stbbdev public: 58*51c0b2f7Stbbdev ConcQWithCapacity() : my_capacity( std::size_t(-1) / (sizeof(void*) + sizeof(T)) ) {} 59*51c0b2f7Stbbdev std::size_t size() const { 60*51c0b2f7Stbbdev return this->unsafe_size(); 61*51c0b2f7Stbbdev } 62*51c0b2f7Stbbdev 63*51c0b2f7Stbbdev std::size_t capacity() const { 64*51c0b2f7Stbbdev return my_capacity; 65*51c0b2f7Stbbdev } 66*51c0b2f7Stbbdev 67*51c0b2f7Stbbdev void set_capacity( const std::size_t n ) { 68*51c0b2f7Stbbdev my_capacity = n; 69*51c0b2f7Stbbdev } 70*51c0b2f7Stbbdev 71*51c0b2f7Stbbdev bool try_push( const T& source ) { 72*51c0b2f7Stbbdev base_type::push( source); 73*51c0b2f7Stbbdev return source.get_serial() < my_capacity; 74*51c0b2f7Stbbdev } 75*51c0b2f7Stbbdev 76*51c0b2f7Stbbdev bool try_pop( T& dest ) { 77*51c0b2f7Stbbdev base_type::try_pop( dest); 78*51c0b2f7Stbbdev return dest.get_serial() < my_capacity; 79*51c0b2f7Stbbdev } 80*51c0b2f7Stbbdev 81*51c0b2f7Stbbdev private: 82*51c0b2f7Stbbdev std::size_t my_capacity; 83*51c0b2f7Stbbdev }; 84*51c0b2f7Stbbdev 85*51c0b2f7Stbbdev template<typename CQ, typename T> 86*51c0b2f7Stbbdev void TestEmptyQueue() { 87*51c0b2f7Stbbdev const CQ queue; 88*51c0b2f7Stbbdev CHECK(queue.size() == 0); 89*51c0b2f7Stbbdev CHECK(queue.capacity()> 0); 90*51c0b2f7Stbbdev CHECK(size_t(queue.capacity())>= std::size_t(-1)/(sizeof(void*)+sizeof(T))); 91*51c0b2f7Stbbdev } 92*51c0b2f7Stbbdev 93*51c0b2f7Stbbdev void TestEmptiness() { 94*51c0b2f7Stbbdev TestEmptyQueue<ConcQWithCapacity<char>, char>(); 95*51c0b2f7Stbbdev TestEmptyQueue<ConcQWithCapacity<move_support_tests::Foo>, move_support_tests::Foo>(); 96*51c0b2f7Stbbdev TestEmptyQueue<tbb::concurrent_bounded_queue<char, test_allocator<char>>, char>(); 97*51c0b2f7Stbbdev TestEmptyQueue<tbb::concurrent_bounded_queue<move_support_tests::Foo, 98*51c0b2f7Stbbdev test_allocator<move_support_tests::Foo>>, move_support_tests::Foo>(); 99*51c0b2f7Stbbdev } 100*51c0b2f7Stbbdev 101*51c0b2f7Stbbdev template<typename CQ, typename T> 102*51c0b2f7Stbbdev void TestFullQueue() { 103*51c0b2f7Stbbdev using allocator_type = decltype(std::declval<CQ>().get_allocator()); 104*51c0b2f7Stbbdev 105*51c0b2f7Stbbdev for (std::size_t n = 0; n < 100; ++n) { 106*51c0b2f7Stbbdev allocator_type::init_counters(); 107*51c0b2f7Stbbdev { 108*51c0b2f7Stbbdev CQ queue; 109*51c0b2f7Stbbdev queue.set_capacity(n); 110*51c0b2f7Stbbdev for (std::size_t i = 0; i <= n; ++i) { 111*51c0b2f7Stbbdev T f; 112*51c0b2f7Stbbdev f.set_serial(i); 113*51c0b2f7Stbbdev bool result = queue.try_push( f); 114*51c0b2f7Stbbdev CHECK((result == (i < n))); 115*51c0b2f7Stbbdev } 116*51c0b2f7Stbbdev 117*51c0b2f7Stbbdev for (std::size_t i = 0; i <= n; ++i) { 118*51c0b2f7Stbbdev T f; 119*51c0b2f7Stbbdev bool result = queue.try_pop(f); 120*51c0b2f7Stbbdev CHECK((result == (i < n))); 121*51c0b2f7Stbbdev CHECK((result == 0 || f.get_serial() == i)); 122*51c0b2f7Stbbdev } 123*51c0b2f7Stbbdev } 124*51c0b2f7Stbbdev CHECK(allocator_type::items_allocated == allocator_type::items_freed); 125*51c0b2f7Stbbdev CHECK(allocator_type::allocations == allocator_type::frees); 126*51c0b2f7Stbbdev } 127*51c0b2f7Stbbdev } 128*51c0b2f7Stbbdev 129*51c0b2f7Stbbdev void TestFullness() { 130*51c0b2f7Stbbdev TestFullQueue<ConcQWithCapacity<move_support_tests::Foo>, move_support_tests::Foo>(); 131*51c0b2f7Stbbdev TestFullQueue<tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, move_support_tests::Foo>(); 132*51c0b2f7Stbbdev } 133*51c0b2f7Stbbdev 134*51c0b2f7Stbbdev template<typename CQ> 135*51c0b2f7Stbbdev void TestClear() { 136*51c0b2f7Stbbdev using allocator_type = decltype(std::declval<CQ>().get_allocator()); 137*51c0b2f7Stbbdev allocator_type::init_counters(); 138*51c0b2f7Stbbdev const std::size_t n = 5; 139*51c0b2f7Stbbdev 140*51c0b2f7Stbbdev CQ queue; 141*51c0b2f7Stbbdev const std::size_t q_capacity = 10; 142*51c0b2f7Stbbdev queue.set_capacity(q_capacity); 143*51c0b2f7Stbbdev 144*51c0b2f7Stbbdev for (std::size_t i = 0; i < n; ++i) { 145*51c0b2f7Stbbdev move_support_tests::Foo f; 146*51c0b2f7Stbbdev f.set_serial(i); 147*51c0b2f7Stbbdev queue.push(f); 148*51c0b2f7Stbbdev } 149*51c0b2f7Stbbdev 150*51c0b2f7Stbbdev CHECK(queue.size() == n); 151*51c0b2f7Stbbdev 152*51c0b2f7Stbbdev queue.clear(); 153*51c0b2f7Stbbdev CHECK(queue.size()==0); 154*51c0b2f7Stbbdev for (std::size_t i = 0; i < n; ++i) { 155*51c0b2f7Stbbdev move_support_tests::Foo f; 156*51c0b2f7Stbbdev f.set_serial(i); 157*51c0b2f7Stbbdev queue.push( f); 158*51c0b2f7Stbbdev } 159*51c0b2f7Stbbdev 160*51c0b2f7Stbbdev CHECK(queue.size() == n); 161*51c0b2f7Stbbdev queue.clear(); 162*51c0b2f7Stbbdev CHECK(queue.size() == 0); 163*51c0b2f7Stbbdev 164*51c0b2f7Stbbdev for (std::size_t i = 0; i < n; ++i) { 165*51c0b2f7Stbbdev move_support_tests::Foo f; 166*51c0b2f7Stbbdev f.set_serial(i); 167*51c0b2f7Stbbdev queue.push(f); 168*51c0b2f7Stbbdev } 169*51c0b2f7Stbbdev 170*51c0b2f7Stbbdev CHECK(queue.size()==n); 171*51c0b2f7Stbbdev } 172*51c0b2f7Stbbdev 173*51c0b2f7Stbbdev void TestClearWorks() { 174*51c0b2f7Stbbdev TestClear<ConcQWithCapacity<move_support_tests::Foo>>(); 175*51c0b2f7Stbbdev TestClear<tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>(); 176*51c0b2f7Stbbdev } 177*51c0b2f7Stbbdev 178*51c0b2f7Stbbdev template<typename Iterator1, typename Iterator2> 179*51c0b2f7Stbbdev void TestIteratorAux( Iterator1 i, Iterator2 j, int size ) { 180*51c0b2f7Stbbdev Iterator1 old_i; // assigned at first iteration below 181*51c0b2f7Stbbdev for (std::size_t k = 0; k < (std::size_t)size; ++k) { 182*51c0b2f7Stbbdev CHECK(i != j); 183*51c0b2f7Stbbdev CHECK(!(i == j)); 184*51c0b2f7Stbbdev // Test "->" 185*51c0b2f7Stbbdev CHECK((k+1 == i->get_serial())); 186*51c0b2f7Stbbdev if (k & 1) { 187*51c0b2f7Stbbdev // Test post-increment 188*51c0b2f7Stbbdev move_support_tests::Foo f = *old_i++; 189*51c0b2f7Stbbdev CHECK((k + 1 == f.get_serial())); 190*51c0b2f7Stbbdev // Test assignment 191*51c0b2f7Stbbdev i = old_i; 192*51c0b2f7Stbbdev } else { 193*51c0b2f7Stbbdev // Test pre-increment 194*51c0b2f7Stbbdev if (k < std::size_t(size - 1)) { 195*51c0b2f7Stbbdev move_support_tests::Foo f = *++i; 196*51c0b2f7Stbbdev CHECK((k + 2 == f.get_serial())); 197*51c0b2f7Stbbdev } else ++i; 198*51c0b2f7Stbbdev // Test assignment 199*51c0b2f7Stbbdev old_i = i; 200*51c0b2f7Stbbdev } 201*51c0b2f7Stbbdev } 202*51c0b2f7Stbbdev CHECK(!(i != j)); 203*51c0b2f7Stbbdev CHECK(i == j); 204*51c0b2f7Stbbdev } 205*51c0b2f7Stbbdev 206*51c0b2f7Stbbdev template<typename Iterator1, typename Iterator2> 207*51c0b2f7Stbbdev void TestIteratorAssignment( Iterator2 j ) { 208*51c0b2f7Stbbdev Iterator1 i(j); 209*51c0b2f7Stbbdev CHECK(i == j); 210*51c0b2f7Stbbdev CHECK(!(i != j)); 211*51c0b2f7Stbbdev 212*51c0b2f7Stbbdev Iterator1 k; 213*51c0b2f7Stbbdev k = j; 214*51c0b2f7Stbbdev CHECK(k == j); 215*51c0b2f7Stbbdev CHECK(!(k != j)); 216*51c0b2f7Stbbdev } 217*51c0b2f7Stbbdev 218*51c0b2f7Stbbdev template<typename Iterator, typename T> 219*51c0b2f7Stbbdev void TestIteratorTraits() { 220*51c0b2f7Stbbdev static_assert( std::is_same<typename Iterator::iterator_category, std::forward_iterator_tag>::value, "wrong iterator category"); 221*51c0b2f7Stbbdev 222*51c0b2f7Stbbdev T x; 223*51c0b2f7Stbbdev 224*51c0b2f7Stbbdev typename Iterator::reference xr = x; 225*51c0b2f7Stbbdev typename Iterator::pointer xp = &x; 226*51c0b2f7Stbbdev CHECK((&xr == xp)); 227*51c0b2f7Stbbdev } 228*51c0b2f7Stbbdev 229*51c0b2f7Stbbdev // Test the iterators for concurrent_queue 230*51c0b2f7Stbbdev template <typename CQ> 231*51c0b2f7Stbbdev void TestIterator() { 232*51c0b2f7Stbbdev CQ queue; 233*51c0b2f7Stbbdev const CQ& const_queue = queue; 234*51c0b2f7Stbbdev for (int j=0; j < 500; ++j) { 235*51c0b2f7Stbbdev TestIteratorAux( queue.unsafe_begin() , queue.unsafe_end() , j); 236*51c0b2f7Stbbdev TestIteratorAux( queue.unsafe_cbegin() , queue.unsafe_cend() , j); 237*51c0b2f7Stbbdev TestIteratorAux( const_queue.unsafe_begin(), const_queue.unsafe_end(), j); 238*51c0b2f7Stbbdev TestIteratorAux( const_queue.unsafe_begin(), queue.unsafe_end() , j); 239*51c0b2f7Stbbdev TestIteratorAux( queue.unsafe_begin() , const_queue.unsafe_end(), j); 240*51c0b2f7Stbbdev move_support_tests::Foo f; 241*51c0b2f7Stbbdev f.set_serial(j+1); 242*51c0b2f7Stbbdev queue.push(f); 243*51c0b2f7Stbbdev } 244*51c0b2f7Stbbdev TestIteratorAssignment<typename CQ::const_iterator>( const_queue.unsafe_begin()); 245*51c0b2f7Stbbdev TestIteratorAssignment<typename CQ::const_iterator>( queue.unsafe_begin()); 246*51c0b2f7Stbbdev TestIteratorAssignment<typename CQ::iterator>( queue.unsafe_begin()); 247*51c0b2f7Stbbdev TestIteratorTraits<typename CQ::const_iterator, const move_support_tests::Foo>(); 248*51c0b2f7Stbbdev TestIteratorTraits<typename CQ::iterator, move_support_tests::Foo>(); 249*51c0b2f7Stbbdev } 250*51c0b2f7Stbbdev 251*51c0b2f7Stbbdev void TestQueueIteratorWorks() { 252*51c0b2f7Stbbdev TestIterator<tbb::concurrent_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>(); 253*51c0b2f7Stbbdev TestIterator<tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>(); 254*51c0b2f7Stbbdev } 255*51c0b2f7Stbbdev 256*51c0b2f7Stbbdev // Define wrapper classes to test tbb::concurrent_queue<T> 257*51c0b2f7Stbbdev template<typename T, typename A = tbb::cache_aligned_allocator<T>> 258*51c0b2f7Stbbdev class ConcQWithSizeWrapper : public tbb::concurrent_queue<T, A> { 259*51c0b2f7Stbbdev public: 260*51c0b2f7Stbbdev ConcQWithSizeWrapper() {} 261*51c0b2f7Stbbdev ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q ) : tbb::concurrent_queue<T, A>(q) {} 262*51c0b2f7Stbbdev ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q, const A& a ) : tbb::concurrent_queue<T, A>(q, a) {} 263*51c0b2f7Stbbdev ConcQWithSizeWrapper( const A& a ) : tbb::concurrent_queue<T, A>( a ) {} 264*51c0b2f7Stbbdev 265*51c0b2f7Stbbdev ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q ) : tbb::concurrent_queue<T>(std::move(q)) {} 266*51c0b2f7Stbbdev ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q, const A& a ) 267*51c0b2f7Stbbdev : tbb::concurrent_queue<T, A>(std::move(q), a) { } 268*51c0b2f7Stbbdev 269*51c0b2f7Stbbdev template<typename InputIterator> 270*51c0b2f7Stbbdev ConcQWithSizeWrapper( InputIterator begin, InputIterator end, const A& a = A() ) 271*51c0b2f7Stbbdev : tbb::concurrent_queue<T, A>(begin, end, a) {} 272*51c0b2f7Stbbdev typename tbb::concurrent_queue<T, A>::size_type size() const { return this->unsafe_size(); } 273*51c0b2f7Stbbdev }; 274*51c0b2f7Stbbdev 275*51c0b2f7Stbbdev enum state_type { 276*51c0b2f7Stbbdev LIVE = 0x1234, 277*51c0b2f7Stbbdev DEAD = 0xDEAD 278*51c0b2f7Stbbdev }; 279*51c0b2f7Stbbdev 280*51c0b2f7Stbbdev class Bar { 281*51c0b2f7Stbbdev state_type state; 282*51c0b2f7Stbbdev public: 283*51c0b2f7Stbbdev static std::size_t construction_num, destruction_num; 284*51c0b2f7Stbbdev std::ptrdiff_t my_id; 285*51c0b2f7Stbbdev Bar() : state(LIVE), my_id(-1) 286*51c0b2f7Stbbdev {} 287*51c0b2f7Stbbdev 288*51c0b2f7Stbbdev Bar( std::size_t _i ) : state(LIVE), my_id(_i) { construction_num++; } 289*51c0b2f7Stbbdev 290*51c0b2f7Stbbdev Bar( const Bar& a_bar ) : state(LIVE) { 291*51c0b2f7Stbbdev CHECK(a_bar.state == LIVE); 292*51c0b2f7Stbbdev my_id = a_bar.my_id; 293*51c0b2f7Stbbdev construction_num++; 294*51c0b2f7Stbbdev } 295*51c0b2f7Stbbdev 296*51c0b2f7Stbbdev ~Bar() { 297*51c0b2f7Stbbdev CHECK(state == LIVE); 298*51c0b2f7Stbbdev state = DEAD; 299*51c0b2f7Stbbdev my_id = DEAD; 300*51c0b2f7Stbbdev destruction_num++; 301*51c0b2f7Stbbdev } 302*51c0b2f7Stbbdev 303*51c0b2f7Stbbdev void operator=( const Bar& a_bar ) { 304*51c0b2f7Stbbdev CHECK(a_bar.state == LIVE); 305*51c0b2f7Stbbdev CHECK(state == LIVE); 306*51c0b2f7Stbbdev my_id = a_bar.my_id; 307*51c0b2f7Stbbdev } 308*51c0b2f7Stbbdev friend bool operator==( const Bar& bar1, const Bar& bar2 ) ; 309*51c0b2f7Stbbdev }; 310*51c0b2f7Stbbdev 311*51c0b2f7Stbbdev std::size_t Bar::construction_num = 0; 312*51c0b2f7Stbbdev std::size_t Bar::destruction_num = 0; 313*51c0b2f7Stbbdev 314*51c0b2f7Stbbdev bool operator==( const Bar& bar1, const Bar& bar2 ) { 315*51c0b2f7Stbbdev CHECK(bar1.state == LIVE); 316*51c0b2f7Stbbdev CHECK(bar2.state == LIVE); 317*51c0b2f7Stbbdev return bar1.my_id == bar2.my_id; 318*51c0b2f7Stbbdev } 319*51c0b2f7Stbbdev 320*51c0b2f7Stbbdev class BarIterator { 321*51c0b2f7Stbbdev Bar* bar_ptr; 322*51c0b2f7Stbbdev BarIterator(Bar* bp_) : bar_ptr(bp_) {} 323*51c0b2f7Stbbdev public: 324*51c0b2f7Stbbdev Bar& operator*() const { 325*51c0b2f7Stbbdev return *bar_ptr; 326*51c0b2f7Stbbdev } 327*51c0b2f7Stbbdev BarIterator& operator++() { 328*51c0b2f7Stbbdev ++bar_ptr; 329*51c0b2f7Stbbdev return *this; 330*51c0b2f7Stbbdev } 331*51c0b2f7Stbbdev Bar* operator++(int) { 332*51c0b2f7Stbbdev Bar* result = &operator*(); 333*51c0b2f7Stbbdev operator++(); 334*51c0b2f7Stbbdev return result; 335*51c0b2f7Stbbdev } 336*51c0b2f7Stbbdev friend bool operator==(const BarIterator& bia, const BarIterator& bib) ; 337*51c0b2f7Stbbdev friend bool operator!=(const BarIterator& bia, const BarIterator& bib) ; 338*51c0b2f7Stbbdev template<typename CQ, typename T, typename TIter, typename CQ_EX, typename T_EX> 339*51c0b2f7Stbbdev friend void TestConstructors (); 340*51c0b2f7Stbbdev } ; 341*51c0b2f7Stbbdev 342*51c0b2f7Stbbdev bool operator==(const BarIterator& bia, const BarIterator& bib) { 343*51c0b2f7Stbbdev return bia.bar_ptr==bib.bar_ptr; 344*51c0b2f7Stbbdev } 345*51c0b2f7Stbbdev 346*51c0b2f7Stbbdev bool operator!=(const BarIterator& bia, const BarIterator& bib) { 347*51c0b2f7Stbbdev return bia.bar_ptr!=bib.bar_ptr; 348*51c0b2f7Stbbdev } 349*51c0b2f7Stbbdev 350*51c0b2f7Stbbdev 351*51c0b2f7Stbbdev class Bar_exception : public std::bad_alloc { 352*51c0b2f7Stbbdev public: 353*51c0b2f7Stbbdev virtual const char *what() const noexcept override { return "making the entry invalid"; } 354*51c0b2f7Stbbdev virtual ~Bar_exception() noexcept {} 355*51c0b2f7Stbbdev }; 356*51c0b2f7Stbbdev 357*51c0b2f7Stbbdev class BarEx { 358*51c0b2f7Stbbdev static int count; 359*51c0b2f7Stbbdev public: 360*51c0b2f7Stbbdev state_type state; 361*51c0b2f7Stbbdev typedef enum { 362*51c0b2f7Stbbdev PREPARATION, 363*51c0b2f7Stbbdev COPY_CONSTRUCT 364*51c0b2f7Stbbdev } mode_type; 365*51c0b2f7Stbbdev static mode_type mode; 366*51c0b2f7Stbbdev std::ptrdiff_t my_id; 367*51c0b2f7Stbbdev std::ptrdiff_t my_tilda_id; 368*51c0b2f7Stbbdev 369*51c0b2f7Stbbdev static int button; 370*51c0b2f7Stbbdev 371*51c0b2f7Stbbdev BarEx() : state(LIVE), my_id(-1), my_tilda_id(-1) 372*51c0b2f7Stbbdev {} 373*51c0b2f7Stbbdev 374*51c0b2f7Stbbdev BarEx(std::size_t _i) : state(LIVE), my_id(_i), my_tilda_id(my_id^(-1)) 375*51c0b2f7Stbbdev {} 376*51c0b2f7Stbbdev 377*51c0b2f7Stbbdev BarEx( const BarEx& a_bar ) : state(LIVE) { 378*51c0b2f7Stbbdev CHECK(a_bar.state==LIVE); 379*51c0b2f7Stbbdev my_id = a_bar.my_id; 380*51c0b2f7Stbbdev if (mode == PREPARATION) 381*51c0b2f7Stbbdev if (!(++count % 100)) { 382*51c0b2f7Stbbdev TBB_TEST_THROW(Bar_exception()); 383*51c0b2f7Stbbdev } 384*51c0b2f7Stbbdev my_tilda_id = a_bar.my_tilda_id; 385*51c0b2f7Stbbdev } 386*51c0b2f7Stbbdev 387*51c0b2f7Stbbdev ~BarEx() { 388*51c0b2f7Stbbdev CHECK(state == LIVE); 389*51c0b2f7Stbbdev state = DEAD; 390*51c0b2f7Stbbdev my_id = DEAD; 391*51c0b2f7Stbbdev } 392*51c0b2f7Stbbdev static void set_mode( mode_type m ) { mode = m; } 393*51c0b2f7Stbbdev 394*51c0b2f7Stbbdev void operator=( const BarEx& a_bar ) { 395*51c0b2f7Stbbdev CHECK(a_bar.state == LIVE); 396*51c0b2f7Stbbdev CHECK(state == LIVE); 397*51c0b2f7Stbbdev my_id = a_bar.my_id; 398*51c0b2f7Stbbdev my_tilda_id = a_bar.my_tilda_id; 399*51c0b2f7Stbbdev } 400*51c0b2f7Stbbdev 401*51c0b2f7Stbbdev friend bool operator==(const BarEx& bar1, const BarEx& bar2 ) ; 402*51c0b2f7Stbbdev }; 403*51c0b2f7Stbbdev 404*51c0b2f7Stbbdev int BarEx::count = 0; 405*51c0b2f7Stbbdev BarEx::mode_type BarEx::mode = BarEx::PREPARATION; 406*51c0b2f7Stbbdev 407*51c0b2f7Stbbdev bool operator==(const BarEx& bar1, const BarEx& bar2) { 408*51c0b2f7Stbbdev CHECK(bar1.state == LIVE); 409*51c0b2f7Stbbdev CHECK(bar2.state == LIVE); 410*51c0b2f7Stbbdev CHECK(((bar1.my_id ^ bar1.my_tilda_id) == -1)); 411*51c0b2f7Stbbdev CHECK(((bar2.my_id ^ bar2.my_tilda_id) == -1)); 412*51c0b2f7Stbbdev return bar1.my_id == bar2.my_id && bar1.my_tilda_id == bar2.my_tilda_id; 413*51c0b2f7Stbbdev } 414*51c0b2f7Stbbdev 415*51c0b2f7Stbbdev template<typename CQ, typename T, typename TIter, typename CQ_EX, typename T_EX> 416*51c0b2f7Stbbdev void TestConstructors () { 417*51c0b2f7Stbbdev CQ src_queue; 418*51c0b2f7Stbbdev typename CQ::const_iterator dqb; 419*51c0b2f7Stbbdev typename CQ::const_iterator dqe; 420*51c0b2f7Stbbdev typename CQ::const_iterator iter; 421*51c0b2f7Stbbdev 422*51c0b2f7Stbbdev for (std::size_t size = 0; size < 1001; ++size) { 423*51c0b2f7Stbbdev for (std::size_t i = 0; i < size; ++i) 424*51c0b2f7Stbbdev src_queue.push(T(i + (i ^ size))); 425*51c0b2f7Stbbdev typename CQ::const_iterator sqb( src_queue.unsafe_begin()); 426*51c0b2f7Stbbdev typename CQ::const_iterator sqe( src_queue.unsafe_end() ); 427*51c0b2f7Stbbdev 428*51c0b2f7Stbbdev CQ dst_queue(sqb, sqe); 429*51c0b2f7Stbbdev CQ copy_with_alloc(src_queue, typename CQ::allocator_type()); 430*51c0b2f7Stbbdev 431*51c0b2f7Stbbdev REQUIRE_MESSAGE(src_queue.size() == dst_queue.size(), "different size"); 432*51c0b2f7Stbbdev REQUIRE_MESSAGE(src_queue.size() == copy_with_alloc.size(), "different size"); 433*51c0b2f7Stbbdev 434*51c0b2f7Stbbdev src_queue.clear(); 435*51c0b2f7Stbbdev } 436*51c0b2f7Stbbdev 437*51c0b2f7Stbbdev T bar_array[1001]; 438*51c0b2f7Stbbdev for (std::size_t size=0; size < 1001; ++size) { 439*51c0b2f7Stbbdev for (std::size_t i=0; i < size; ++i) { 440*51c0b2f7Stbbdev bar_array[i] = T(i+(i^size)); 441*51c0b2f7Stbbdev } 442*51c0b2f7Stbbdev 443*51c0b2f7Stbbdev const TIter sab(bar_array + 0); 444*51c0b2f7Stbbdev const TIter sae(bar_array + size); 445*51c0b2f7Stbbdev 446*51c0b2f7Stbbdev CQ dst_queue2(sab, sae); 447*51c0b2f7Stbbdev 448*51c0b2f7Stbbdev CHECK(size == dst_queue2.size()); 449*51c0b2f7Stbbdev CHECK(sab == TIter(bar_array+0)); 450*51c0b2f7Stbbdev CHECK(sae == TIter(bar_array+size)); 451*51c0b2f7Stbbdev 452*51c0b2f7Stbbdev dqb = dst_queue2.unsafe_begin(); 453*51c0b2f7Stbbdev dqe = dst_queue2.unsafe_end(); 454*51c0b2f7Stbbdev TIter v_iter(sab); 455*51c0b2f7Stbbdev for (; dqb != dqe; ++dqb, ++v_iter) { 456*51c0b2f7Stbbdev REQUIRE_MESSAGE((*dqb == *v_iter), "unexpected element"); 457*51c0b2f7Stbbdev } 458*51c0b2f7Stbbdev 459*51c0b2f7Stbbdev REQUIRE_MESSAGE(v_iter==sae, "different size?"); 460*51c0b2f7Stbbdev } 461*51c0b2f7Stbbdev 462*51c0b2f7Stbbdev src_queue.clear(); 463*51c0b2f7Stbbdev 464*51c0b2f7Stbbdev CQ dst_queue3(src_queue); 465*51c0b2f7Stbbdev CHECK(src_queue.size() == dst_queue3.size()); 466*51c0b2f7Stbbdev CHECK(0 == dst_queue3.size()); 467*51c0b2f7Stbbdev 468*51c0b2f7Stbbdev int k = 0; 469*51c0b2f7Stbbdev for (std::size_t i = 0; i < 1001; ++i) { 470*51c0b2f7Stbbdev T tmp_bar; 471*51c0b2f7Stbbdev src_queue.push(T(++k)); 472*51c0b2f7Stbbdev src_queue.push(T(++k)); 473*51c0b2f7Stbbdev src_queue.try_pop(tmp_bar); 474*51c0b2f7Stbbdev 475*51c0b2f7Stbbdev CQ dst_queue4( src_queue); 476*51c0b2f7Stbbdev 477*51c0b2f7Stbbdev CHECK(src_queue.size() == dst_queue4.size()); 478*51c0b2f7Stbbdev 479*51c0b2f7Stbbdev dqb = dst_queue4.unsafe_begin(); 480*51c0b2f7Stbbdev dqe = dst_queue4.unsafe_end(); 481*51c0b2f7Stbbdev iter = src_queue.unsafe_begin(); 482*51c0b2f7Stbbdev 483*51c0b2f7Stbbdev for (; dqb != dqe; ++dqb, ++iter) { 484*51c0b2f7Stbbdev REQUIRE_MESSAGE((*dqb == *iter), "unexpected element"); 485*51c0b2f7Stbbdev } 486*51c0b2f7Stbbdev 487*51c0b2f7Stbbdev REQUIRE_MESSAGE(iter == src_queue.unsafe_end(), "different size?"); 488*51c0b2f7Stbbdev } 489*51c0b2f7Stbbdev 490*51c0b2f7Stbbdev CQ dst_queue5(src_queue); 491*51c0b2f7Stbbdev 492*51c0b2f7Stbbdev CHECK(src_queue.size() == dst_queue5.size()); 493*51c0b2f7Stbbdev dqb = dst_queue5.unsafe_begin(); 494*51c0b2f7Stbbdev dqe = dst_queue5.unsafe_end(); 495*51c0b2f7Stbbdev iter = src_queue.unsafe_begin(); 496*51c0b2f7Stbbdev for (; dqb != dqe; ++dqb, ++iter) { 497*51c0b2f7Stbbdev REQUIRE_MESSAGE(*dqb == *iter, "unexpected element"); 498*51c0b2f7Stbbdev } 499*51c0b2f7Stbbdev 500*51c0b2f7Stbbdev for (std::size_t i=0; i<100; ++i) { 501*51c0b2f7Stbbdev T tmp_bar; 502*51c0b2f7Stbbdev src_queue.push(T(i + 1000)); 503*51c0b2f7Stbbdev src_queue.push(T(i + 1000)); 504*51c0b2f7Stbbdev src_queue.try_pop(tmp_bar); 505*51c0b2f7Stbbdev 506*51c0b2f7Stbbdev dst_queue5.push(T(i + 1000)); 507*51c0b2f7Stbbdev dst_queue5.push(T(i + 1000)); 508*51c0b2f7Stbbdev dst_queue5.try_pop(tmp_bar); 509*51c0b2f7Stbbdev } 510*51c0b2f7Stbbdev 511*51c0b2f7Stbbdev CHECK(src_queue.size() == dst_queue5.size()); 512*51c0b2f7Stbbdev dqb = dst_queue5.unsafe_begin(); 513*51c0b2f7Stbbdev dqe = dst_queue5.unsafe_end(); 514*51c0b2f7Stbbdev iter = src_queue.unsafe_begin(); 515*51c0b2f7Stbbdev for (; dqb != dqe; ++dqb, ++iter) { 516*51c0b2f7Stbbdev REQUIRE_MESSAGE((*dqb == *iter), "unexpected element"); 517*51c0b2f7Stbbdev } 518*51c0b2f7Stbbdev 519*51c0b2f7Stbbdev REQUIRE_MESSAGE(iter == src_queue.unsafe_end(), "different size?"); 520*51c0b2f7Stbbdev 521*51c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 522*51c0b2f7Stbbdev k = 0; 523*51c0b2f7Stbbdev typename CQ_EX::size_type n_elements = 0; 524*51c0b2f7Stbbdev CQ_EX src_queue_ex; 525*51c0b2f7Stbbdev for (std::size_t size = 0; size < 1001; ++size) { 526*51c0b2f7Stbbdev T_EX tmp_bar_ex; 527*51c0b2f7Stbbdev typename CQ_EX::size_type n_successful_pushes = 0; 528*51c0b2f7Stbbdev T_EX::set_mode(T_EX::PREPARATION); 529*51c0b2f7Stbbdev try { 530*51c0b2f7Stbbdev src_queue_ex.push(T_EX(k + (k ^ size))); 531*51c0b2f7Stbbdev ++n_successful_pushes; 532*51c0b2f7Stbbdev } catch (...) { 533*51c0b2f7Stbbdev } 534*51c0b2f7Stbbdev ++k; 535*51c0b2f7Stbbdev try { 536*51c0b2f7Stbbdev src_queue_ex.push(T_EX(k + (k ^ size))); 537*51c0b2f7Stbbdev ++n_successful_pushes; 538*51c0b2f7Stbbdev } catch (...) { 539*51c0b2f7Stbbdev } 540*51c0b2f7Stbbdev ++k; 541*51c0b2f7Stbbdev src_queue_ex.try_pop(tmp_bar_ex); 542*51c0b2f7Stbbdev n_elements += (n_successful_pushes - 1); 543*51c0b2f7Stbbdev CHECK(src_queue_ex.size() == n_elements); 544*51c0b2f7Stbbdev 545*51c0b2f7Stbbdev T_EX::set_mode(T_EX::COPY_CONSTRUCT); 546*51c0b2f7Stbbdev CQ_EX dst_queue_ex(src_queue_ex); 547*51c0b2f7Stbbdev 548*51c0b2f7Stbbdev CHECK(src_queue_ex.size() == dst_queue_ex.size()); 549*51c0b2f7Stbbdev 550*51c0b2f7Stbbdev typename CQ_EX::const_iterator dqb_ex = dst_queue_ex.unsafe_begin(); 551*51c0b2f7Stbbdev typename CQ_EX::const_iterator dqe_ex = dst_queue_ex.unsafe_end(); 552*51c0b2f7Stbbdev typename CQ_EX::const_iterator iter_ex = src_queue_ex.unsafe_begin(); 553*51c0b2f7Stbbdev 554*51c0b2f7Stbbdev for (; dqb_ex != dqe_ex; ++dqb_ex, ++iter_ex) { 555*51c0b2f7Stbbdev REQUIRE_MESSAGE(*dqb_ex == *iter_ex, "unexpected element"); 556*51c0b2f7Stbbdev } 557*51c0b2f7Stbbdev 558*51c0b2f7Stbbdev REQUIRE_MESSAGE(iter_ex==src_queue_ex.unsafe_end(), "different size?"); 559*51c0b2f7Stbbdev } 560*51c0b2f7Stbbdev #endif 561*51c0b2f7Stbbdev src_queue.clear(); 562*51c0b2f7Stbbdev 563*51c0b2f7Stbbdev using qsize_t = typename CQ::size_type; 564*51c0b2f7Stbbdev for (qsize_t size = 0; size < 1001; ++size) { 565*51c0b2f7Stbbdev for (qsize_t i = 0; i < size; ++i) { 566*51c0b2f7Stbbdev src_queue.push(T(i + (i ^ size))); 567*51c0b2f7Stbbdev } 568*51c0b2f7Stbbdev std::vector<const T*> locations(size); 569*51c0b2f7Stbbdev typename CQ::const_iterator qit = src_queue.unsafe_begin(); 570*51c0b2f7Stbbdev for (qsize_t i = 0; i < size; ++i, ++qit) { 571*51c0b2f7Stbbdev locations[i] = &(*qit); 572*51c0b2f7Stbbdev } 573*51c0b2f7Stbbdev 574*51c0b2f7Stbbdev qsize_t size_of_queue = src_queue.size(); 575*51c0b2f7Stbbdev CQ dst_queue(std::move(src_queue)); 576*51c0b2f7Stbbdev 577*51c0b2f7Stbbdev REQUIRE_MESSAGE((src_queue.empty() && src_queue.size() == 0), "not working move constructor?"); 578*51c0b2f7Stbbdev REQUIRE_MESSAGE((size == size_of_queue && size_of_queue == qsize_t(dst_queue.size())), "not working move constructor?"); 579*51c0b2f7Stbbdev 580*51c0b2f7Stbbdev qit = dst_queue.unsafe_begin(); 581*51c0b2f7Stbbdev for (qsize_t i = 0; i < size; ++i, ++qit) { 582*51c0b2f7Stbbdev REQUIRE_MESSAGE(locations[i] == &(*qit), "there was data movement during move constructor"); 583*51c0b2f7Stbbdev } 584*51c0b2f7Stbbdev 585*51c0b2f7Stbbdev for (qsize_t i = 0; i < size; ++i) { 586*51c0b2f7Stbbdev T test(i + (i ^ size)); 587*51c0b2f7Stbbdev T popped; 588*51c0b2f7Stbbdev bool pop_result = dst_queue.try_pop( popped); 589*51c0b2f7Stbbdev 590*51c0b2f7Stbbdev CHECK(pop_result); 591*51c0b2f7Stbbdev CHECK(test == popped); 592*51c0b2f7Stbbdev } 593*51c0b2f7Stbbdev } 594*51c0b2f7Stbbdev } 595*51c0b2f7Stbbdev 596*51c0b2f7Stbbdev void TestQueueConstructors() { 597*51c0b2f7Stbbdev TestConstructors<ConcQWithSizeWrapper<Bar>, Bar, BarIterator, ConcQWithSizeWrapper<BarEx>, BarEx>(); 598*51c0b2f7Stbbdev TestConstructors<tbb::concurrent_bounded_queue<Bar>, Bar, BarIterator, tbb::concurrent_bounded_queue<BarEx>, BarEx>(); 599*51c0b2f7Stbbdev } 600*51c0b2f7Stbbdev 601*51c0b2f7Stbbdev template<typename T> 602*51c0b2f7Stbbdev struct TestNegativeQueueBody { 603*51c0b2f7Stbbdev tbb::concurrent_bounded_queue<T>& queue; 604*51c0b2f7Stbbdev const std::size_t nthread; 605*51c0b2f7Stbbdev TestNegativeQueueBody( tbb::concurrent_bounded_queue<T>& q, std::size_t n ) : queue(q), nthread(n) {} 606*51c0b2f7Stbbdev void operator()( std::size_t k ) const { 607*51c0b2f7Stbbdev if (k == 0) { 608*51c0b2f7Stbbdev int number_of_pops = int(nthread) - 1; 609*51c0b2f7Stbbdev // Wait for all pops to pend. 610*51c0b2f7Stbbdev while (int(queue.size())> -number_of_pops) { 611*51c0b2f7Stbbdev std::this_thread::yield(); 612*51c0b2f7Stbbdev } 613*51c0b2f7Stbbdev 614*51c0b2f7Stbbdev for (int i = 0; ; ++i) { 615*51c0b2f7Stbbdev CHECK(queue.size() == std::size_t(i - number_of_pops)); 616*51c0b2f7Stbbdev CHECK((queue.empty() == (queue.size() <= 0))); 617*51c0b2f7Stbbdev if (i == number_of_pops) break; 618*51c0b2f7Stbbdev // Satisfy another pop 619*51c0b2f7Stbbdev queue.push(T()); 620*51c0b2f7Stbbdev } 621*51c0b2f7Stbbdev } else { 622*51c0b2f7Stbbdev // Pop item from queue 623*51c0b2f7Stbbdev T item; 624*51c0b2f7Stbbdev queue.pop(item); 625*51c0b2f7Stbbdev } 626*51c0b2f7Stbbdev } 627*51c0b2f7Stbbdev }; 628*51c0b2f7Stbbdev 629*51c0b2f7Stbbdev //! Test a queue with a negative size. 630*51c0b2f7Stbbdev template<typename T> 631*51c0b2f7Stbbdev void TestNegativeQueue( std::size_t nthread ) { 632*51c0b2f7Stbbdev tbb::concurrent_bounded_queue<T> queue; 633*51c0b2f7Stbbdev utils::NativeParallelFor( nthread, TestNegativeQueueBody<T>(queue,nthread)); 634*51c0b2f7Stbbdev } 635*51c0b2f7Stbbdev 636*51c0b2f7Stbbdev template<typename T> 637*51c0b2f7Stbbdev class ConcQPushPopWrapper : public tbb::concurrent_queue<T, test_allocator<T>> { 638*51c0b2f7Stbbdev public: 639*51c0b2f7Stbbdev ConcQPushPopWrapper() : my_capacity(std::size_t(-1) / (sizeof(void*) + sizeof(T))) 640*51c0b2f7Stbbdev {} 641*51c0b2f7Stbbdev 642*51c0b2f7Stbbdev std::size_t size() const { return this->unsafe_size(); } 643*51c0b2f7Stbbdev void set_capacity( const ptrdiff_t n ) { my_capacity = n; } 644*51c0b2f7Stbbdev bool try_push( const T& source ) { return this->push( source); } 645*51c0b2f7Stbbdev bool try_pop( T& dest ) { return this->tbb::concurrent_queue<T, test_allocator<T>>::try_pop(dest); } 646*51c0b2f7Stbbdev std::size_t my_capacity; 647*51c0b2f7Stbbdev }; 648*51c0b2f7Stbbdev 649*51c0b2f7Stbbdev template<typename CQ, typename T> 650*51c0b2f7Stbbdev struct Body { 651*51c0b2f7Stbbdev CQ* queue; 652*51c0b2f7Stbbdev const std::size_t nthread; 653*51c0b2f7Stbbdev Body( std::size_t nthread_ ) : nthread(nthread_) {} 654*51c0b2f7Stbbdev void operator()( std::size_t thread_id ) const { 655*51c0b2f7Stbbdev long pop_kind[3] = {0, 0, 0}; 656*51c0b2f7Stbbdev std::size_t serial[MAXTHREAD + 1]; 657*51c0b2f7Stbbdev memset(serial, 0, nthread * sizeof(std::size_t)); 658*51c0b2f7Stbbdev CHECK(thread_id < nthread); 659*51c0b2f7Stbbdev 660*51c0b2f7Stbbdev long sum = 0; 661*51c0b2f7Stbbdev for (std::size_t j = 0; j < M; ++j) { 662*51c0b2f7Stbbdev T f; 663*51c0b2f7Stbbdev f.set_thread_id(move_support_tests::serial_dead_state); 664*51c0b2f7Stbbdev f.set_serial(move_support_tests::serial_dead_state); 665*51c0b2f7Stbbdev bool prepopped = false; 666*51c0b2f7Stbbdev if (j & 1) { 667*51c0b2f7Stbbdev prepopped = queue->try_pop(f); 668*51c0b2f7Stbbdev ++pop_kind[prepopped]; 669*51c0b2f7Stbbdev } 670*51c0b2f7Stbbdev T g; 671*51c0b2f7Stbbdev g.set_thread_id(thread_id); 672*51c0b2f7Stbbdev g.set_serial(j + 1); 673*51c0b2f7Stbbdev push(*queue, g, j); 674*51c0b2f7Stbbdev if (!prepopped) { 675*51c0b2f7Stbbdev while(!(queue)->try_pop(f)) std::this_thread::yield(); 676*51c0b2f7Stbbdev ++pop_kind[2]; 677*51c0b2f7Stbbdev } 678*51c0b2f7Stbbdev CHECK(f.get_thread_id() <= nthread); 679*51c0b2f7Stbbdev REQUIRE_MESSAGE((f.get_thread_id() == nthread || serial[f.get_thread_id()] < f.get_serial()), "partial order violation"); 680*51c0b2f7Stbbdev serial[f.get_thread_id()] = f.get_serial(); 681*51c0b2f7Stbbdev sum += int(f.get_serial() - 1); 682*51c0b2f7Stbbdev } 683*51c0b2f7Stbbdev Sum[thread_id] = sum; 684*51c0b2f7Stbbdev for (std::size_t k = 0; k < 3; ++k) 685*51c0b2f7Stbbdev PopKind[k] += pop_kind[k]; 686*51c0b2f7Stbbdev } 687*51c0b2f7Stbbdev }; 688*51c0b2f7Stbbdev 689*51c0b2f7Stbbdev template<typename CQ, typename T> 690*51c0b2f7Stbbdev void TestPushPop( std::size_t prefill, std::ptrdiff_t capacity, std::size_t nthread ) { 691*51c0b2f7Stbbdev using allocator_type = decltype(std::declval<CQ>().get_allocator()); 692*51c0b2f7Stbbdev CHECK(nthread> 0); 693*51c0b2f7Stbbdev std::ptrdiff_t signed_prefill = std::ptrdiff_t(prefill); 694*51c0b2f7Stbbdev 695*51c0b2f7Stbbdev if (signed_prefill + 1>= capacity) { 696*51c0b2f7Stbbdev return; 697*51c0b2f7Stbbdev } 698*51c0b2f7Stbbdev 699*51c0b2f7Stbbdev bool success = false; 700*51c0b2f7Stbbdev for (std::size_t k=0; k < 3; ++k) { 701*51c0b2f7Stbbdev PopKind[k] = 0; 702*51c0b2f7Stbbdev } 703*51c0b2f7Stbbdev 704*51c0b2f7Stbbdev for (std::size_t trial = 0; !success; ++trial) { 705*51c0b2f7Stbbdev allocator_type::init_counters(); 706*51c0b2f7Stbbdev Body<CQ,T> body(nthread); 707*51c0b2f7Stbbdev CQ queue; 708*51c0b2f7Stbbdev queue.set_capacity(capacity); 709*51c0b2f7Stbbdev body.queue = &queue; 710*51c0b2f7Stbbdev for (std::size_t i = 0; i < prefill; ++i) { 711*51c0b2f7Stbbdev T f; 712*51c0b2f7Stbbdev f.set_thread_id(nthread); 713*51c0b2f7Stbbdev f.set_serial(1 + i); 714*51c0b2f7Stbbdev push(queue, f, i); 715*51c0b2f7Stbbdev CHECK(queue.size() == i + 1); 716*51c0b2f7Stbbdev CHECK(!queue.empty()); 717*51c0b2f7Stbbdev } 718*51c0b2f7Stbbdev 719*51c0b2f7Stbbdev utils::NativeParallelFor( nthread, body); 720*51c0b2f7Stbbdev 721*51c0b2f7Stbbdev int sum = 0; 722*51c0b2f7Stbbdev for (std::size_t k = 0; k < nthread; ++k) { 723*51c0b2f7Stbbdev sum += Sum[k]; 724*51c0b2f7Stbbdev } 725*51c0b2f7Stbbdev 726*51c0b2f7Stbbdev int expected = int( nthread * ((M - 1) * M / 2) + ((prefill - 1) * prefill) / 2); 727*51c0b2f7Stbbdev for (int i = int(prefill); --i>=0;) { 728*51c0b2f7Stbbdev CHECK(!queue.empty()); 729*51c0b2f7Stbbdev T f; 730*51c0b2f7Stbbdev bool result = queue.try_pop(f); 731*51c0b2f7Stbbdev CHECK(result); 732*51c0b2f7Stbbdev CHECK(int(queue.size()) == i); 733*51c0b2f7Stbbdev sum += int(f.get_serial()) - 1; 734*51c0b2f7Stbbdev } 735*51c0b2f7Stbbdev REQUIRE_MESSAGE(queue.empty(), "The queue should be empty"); 736*51c0b2f7Stbbdev REQUIRE_MESSAGE(queue.size() == 0, "The queue should have zero size"); 737*51c0b2f7Stbbdev if (sum != expected) { 738*51c0b2f7Stbbdev REPORT("sum=%d expected=%d\n",sum,expected); 739*51c0b2f7Stbbdev } 740*51c0b2f7Stbbdev 741*51c0b2f7Stbbdev success = true; 742*51c0b2f7Stbbdev if (nthread> 1 && prefill == 0) { 743*51c0b2f7Stbbdev // Check that pop_if_present got sufficient exercise 744*51c0b2f7Stbbdev for (std::size_t k = 0; k < 2; ++k) { 745*51c0b2f7Stbbdev const int min_requirement = 100; 746*51c0b2f7Stbbdev const int max_trial = 20; 747*51c0b2f7Stbbdev 748*51c0b2f7Stbbdev if (PopKind[k] < min_requirement) { 749*51c0b2f7Stbbdev if (trial>= max_trial) { 750*51c0b2f7Stbbdev REPORT("Warning: %d threads had only %ld pop_if_present operations %s after %d trials (expected at least %d). " 751*51c0b2f7Stbbdev "This problem may merely be unlucky scheduling. " 752*51c0b2f7Stbbdev "Investigate only if it happens repeatedly.\n", 753*51c0b2f7Stbbdev nthread, long(PopKind[k]), k==0?"failed":"succeeded", max_trial, min_requirement); 754*51c0b2f7Stbbdev } else { 755*51c0b2f7Stbbdev success = false; 756*51c0b2f7Stbbdev } 757*51c0b2f7Stbbdev } 758*51c0b2f7Stbbdev } 759*51c0b2f7Stbbdev } 760*51c0b2f7Stbbdev } 761*51c0b2f7Stbbdev } 762*51c0b2f7Stbbdev 763*51c0b2f7Stbbdev void TestConcurrentPushPop() { 764*51c0b2f7Stbbdev for (std::size_t nthread = MinThread; nthread <= MaxThread; ++nthread) { 765*51c0b2f7Stbbdev INFO(" Testing with "<< nthread << " thread(s)"); 766*51c0b2f7Stbbdev TestNegativeQueue<move_support_tests::Foo>(nthread); 767*51c0b2f7Stbbdev for (std::size_t prefill=0; prefill < 64; prefill += (1 + prefill / 3)) { 768*51c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(-1), nthread); 769*51c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(1), nthread); 770*51c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(2), nthread); 771*51c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(10), nthread); 772*51c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(100), nthread); 773*51c0b2f7Stbbdev } 774*51c0b2f7Stbbdev for (std::size_t prefill = 0; prefill < 64; prefill += (1 + prefill / 3) ) { 775*51c0b2f7Stbbdev TestPushPop<tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 776*51c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(-1), nthread); 777*51c0b2f7Stbbdev TestPushPop<tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 778*51c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(1), nthread); 779*51c0b2f7Stbbdev TestPushPop<tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 780*51c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(2), nthread); 781*51c0b2f7Stbbdev TestPushPop<tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 782*51c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(10), nthread); 783*51c0b2f7Stbbdev TestPushPop<tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 784*51c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(100), nthread); 785*51c0b2f7Stbbdev } 786*51c0b2f7Stbbdev } 787*51c0b2f7Stbbdev } 788*51c0b2f7Stbbdev 789*51c0b2f7Stbbdev class Foo_exception : public std::bad_alloc { 790*51c0b2f7Stbbdev public: 791*51c0b2f7Stbbdev virtual const char *what() const throw() override { return "out of Foo limit"; } 792*51c0b2f7Stbbdev virtual ~Foo_exception() throw() {} 793*51c0b2f7Stbbdev }; 794*51c0b2f7Stbbdev 795*51c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 796*51c0b2f7Stbbdev static std::atomic<long> FooExConstructed; 797*51c0b2f7Stbbdev static std::atomic<long> FooExDestroyed; 798*51c0b2f7Stbbdev static std::atomic<long> serial_source; 799*51c0b2f7Stbbdev static long MaxFooCount = 0; 800*51c0b2f7Stbbdev static const long Threshold = 400; 801*51c0b2f7Stbbdev 802*51c0b2f7Stbbdev class FooEx { 803*51c0b2f7Stbbdev state_type state; 804*51c0b2f7Stbbdev public: 805*51c0b2f7Stbbdev int serial; 806*51c0b2f7Stbbdev FooEx() : state(LIVE) { 807*51c0b2f7Stbbdev ++FooExConstructed; 808*51c0b2f7Stbbdev serial = serial_source++; 809*51c0b2f7Stbbdev } 810*51c0b2f7Stbbdev 811*51c0b2f7Stbbdev FooEx( const FooEx& item ) : state(LIVE) { 812*51c0b2f7Stbbdev CHECK(item.state == LIVE); 813*51c0b2f7Stbbdev ++FooExConstructed; 814*51c0b2f7Stbbdev if (MaxFooCount && (FooExConstructed - FooExDestroyed) >= MaxFooCount) { // in push() 815*51c0b2f7Stbbdev throw Foo_exception(); 816*51c0b2f7Stbbdev } 817*51c0b2f7Stbbdev serial = item.serial; 818*51c0b2f7Stbbdev } 819*51c0b2f7Stbbdev 820*51c0b2f7Stbbdev ~FooEx() { 821*51c0b2f7Stbbdev CHECK(state==LIVE); 822*51c0b2f7Stbbdev ++FooExDestroyed; 823*51c0b2f7Stbbdev state=DEAD; 824*51c0b2f7Stbbdev serial=DEAD; 825*51c0b2f7Stbbdev } 826*51c0b2f7Stbbdev 827*51c0b2f7Stbbdev void operator=( FooEx& item ) { 828*51c0b2f7Stbbdev CHECK(item.state==LIVE); 829*51c0b2f7Stbbdev CHECK(state==LIVE); 830*51c0b2f7Stbbdev serial = item.serial; 831*51c0b2f7Stbbdev if( MaxFooCount==2*Threshold && (FooExConstructed-FooExDestroyed) <= MaxFooCount/4 ) // in pop() 832*51c0b2f7Stbbdev throw Foo_exception(); 833*51c0b2f7Stbbdev } 834*51c0b2f7Stbbdev 835*51c0b2f7Stbbdev void operator=( FooEx&& item ) { 836*51c0b2f7Stbbdev operator=( item ); 837*51c0b2f7Stbbdev item.serial = 0; 838*51c0b2f7Stbbdev } 839*51c0b2f7Stbbdev 840*51c0b2f7Stbbdev }; 841*51c0b2f7Stbbdev 842*51c0b2f7Stbbdev template <template <typename, typename> class CQ, typename A1, typename A2, typename T> 843*51c0b2f7Stbbdev void TestExceptionBody() { 844*51c0b2f7Stbbdev enum methods { 845*51c0b2f7Stbbdev m_push = 0, 846*51c0b2f7Stbbdev m_pop 847*51c0b2f7Stbbdev }; 848*51c0b2f7Stbbdev 849*51c0b2f7Stbbdev const int N = 1000; // # of bytes 850*51c0b2f7Stbbdev 851*51c0b2f7Stbbdev MaxFooCount = 5; 852*51c0b2f7Stbbdev 853*51c0b2f7Stbbdev try { 854*51c0b2f7Stbbdev int n_pushed=0, n_popped=0; 855*51c0b2f7Stbbdev for(int t = 0; t <= 1; t++)// exception type -- 0 : from allocator(), 1 : from Foo's constructor 856*51c0b2f7Stbbdev { 857*51c0b2f7Stbbdev CQ<T,A1> queue_test; 858*51c0b2f7Stbbdev for( int m=m_push; m<=m_pop; m++ ) { 859*51c0b2f7Stbbdev // concurrent_queue internally rebinds the allocator to the one for 'char' 860*51c0b2f7Stbbdev A2::init_counters(); 861*51c0b2f7Stbbdev 862*51c0b2f7Stbbdev if(t) MaxFooCount = MaxFooCount + 400; 863*51c0b2f7Stbbdev else A2::set_limits(N/2); 864*51c0b2f7Stbbdev 865*51c0b2f7Stbbdev try { 866*51c0b2f7Stbbdev switch(m) { 867*51c0b2f7Stbbdev case m_push: 868*51c0b2f7Stbbdev for( int k=0; k<N; k++ ) { 869*51c0b2f7Stbbdev push( queue_test, T(), k); 870*51c0b2f7Stbbdev n_pushed++; 871*51c0b2f7Stbbdev } 872*51c0b2f7Stbbdev break; 873*51c0b2f7Stbbdev case m_pop: 874*51c0b2f7Stbbdev n_popped=0; 875*51c0b2f7Stbbdev for( int k=0; k<n_pushed; k++ ) { 876*51c0b2f7Stbbdev T elt; 877*51c0b2f7Stbbdev queue_test.try_pop( elt); 878*51c0b2f7Stbbdev n_popped++; 879*51c0b2f7Stbbdev } 880*51c0b2f7Stbbdev n_pushed = 0; 881*51c0b2f7Stbbdev A2::set_limits(); 882*51c0b2f7Stbbdev break; 883*51c0b2f7Stbbdev } 884*51c0b2f7Stbbdev if( !t && m==m_push ) REQUIRE_MESSAGE(false, "should throw an exception"); 885*51c0b2f7Stbbdev } catch ( Foo_exception & ) { 886*51c0b2f7Stbbdev long tc = MaxFooCount; 887*51c0b2f7Stbbdev MaxFooCount = 0; // disable exception 888*51c0b2f7Stbbdev switch(m) { 889*51c0b2f7Stbbdev case m_push: 890*51c0b2f7Stbbdev REQUIRE_MESSAGE(ptrdiff_t(queue_test.size())==n_pushed, "incorrect queue size"); 891*51c0b2f7Stbbdev for( int k=0; k<(int)tc; k++ ) { 892*51c0b2f7Stbbdev push( queue_test, T(), k); 893*51c0b2f7Stbbdev n_pushed++; 894*51c0b2f7Stbbdev } 895*51c0b2f7Stbbdev break; 896*51c0b2f7Stbbdev case m_pop: 897*51c0b2f7Stbbdev n_pushed -= (n_popped+1); // including one that threw the exception 898*51c0b2f7Stbbdev REQUIRE_MESSAGE(n_pushed>=0, "n_pushed cannot be less than 0"); 899*51c0b2f7Stbbdev for( int k=0; k<1000; k++ ) { 900*51c0b2f7Stbbdev push( queue_test, T(), k); 901*51c0b2f7Stbbdev n_pushed++; 902*51c0b2f7Stbbdev } 903*51c0b2f7Stbbdev REQUIRE_MESSAGE(!queue_test.empty(), "queue must not be empty"); 904*51c0b2f7Stbbdev REQUIRE_MESSAGE(ptrdiff_t(queue_test.size())==n_pushed, "queue size must be equal to n pushed"); 905*51c0b2f7Stbbdev for( int k=0; k<n_pushed; k++ ) { 906*51c0b2f7Stbbdev T elt; 907*51c0b2f7Stbbdev queue_test.try_pop( elt); 908*51c0b2f7Stbbdev } 909*51c0b2f7Stbbdev REQUIRE_MESSAGE(queue_test.empty(), "queue must be empty"); 910*51c0b2f7Stbbdev REQUIRE_MESSAGE(queue_test.size()==0, "queue must be empty"); 911*51c0b2f7Stbbdev break; 912*51c0b2f7Stbbdev } 913*51c0b2f7Stbbdev MaxFooCount = tc; 914*51c0b2f7Stbbdev } catch ( std::bad_alloc & ) { 915*51c0b2f7Stbbdev A2::set_limits(); // disable exception from allocator 916*51c0b2f7Stbbdev std::size_t size = queue_test.size(); 917*51c0b2f7Stbbdev switch(m) { 918*51c0b2f7Stbbdev case m_push: 919*51c0b2f7Stbbdev REQUIRE_MESSAGE(size>0, "incorrect queue size"); 920*51c0b2f7Stbbdev break; 921*51c0b2f7Stbbdev case m_pop: 922*51c0b2f7Stbbdev if( !t ) REQUIRE_MESSAGE(false, "should not throw an exception"); 923*51c0b2f7Stbbdev break; 924*51c0b2f7Stbbdev } 925*51c0b2f7Stbbdev } 926*51c0b2f7Stbbdev INFO("for t= " << t << "and m= " << m << " exception test passed"); 927*51c0b2f7Stbbdev } 928*51c0b2f7Stbbdev } 929*51c0b2f7Stbbdev } catch(...) { 930*51c0b2f7Stbbdev REQUIRE_MESSAGE(false, "unexpected exception"); 931*51c0b2f7Stbbdev } 932*51c0b2f7Stbbdev } 933*51c0b2f7Stbbdev 934*51c0b2f7Stbbdev void TestExceptions() { 935*51c0b2f7Stbbdev using allocator_t = StaticSharedCountingAllocator<tbb::cache_aligned_allocator<std::size_t>>; 936*51c0b2f7Stbbdev using allocator_char_t = StaticSharedCountingAllocator<tbb::cache_aligned_allocator<char>>; 937*51c0b2f7Stbbdev TestExceptionBody<ConcQWithSizeWrapper, allocator_t, allocator_char_t, FooEx>(); 938*51c0b2f7Stbbdev TestExceptionBody<tbb::concurrent_bounded_queue, allocator_t, allocator_char_t, FooEx>(); 939*51c0b2f7Stbbdev 940*51c0b2f7Stbbdev } 941*51c0b2f7Stbbdev 942*51c0b2f7Stbbdev std::atomic<std::size_t> num_pushed; 943*51c0b2f7Stbbdev std::atomic<std::size_t> num_popped; 944*51c0b2f7Stbbdev std::atomic<std::size_t> failed_pushes; 945*51c0b2f7Stbbdev std::atomic<std::size_t> failed_pops; 946*51c0b2f7Stbbdev 947*51c0b2f7Stbbdev class SimplePushBody { 948*51c0b2f7Stbbdev tbb::concurrent_bounded_queue<int>* q; 949*51c0b2f7Stbbdev std::size_t max; 950*51c0b2f7Stbbdev public: 951*51c0b2f7Stbbdev SimplePushBody(tbb::concurrent_bounded_queue<int>* _q, std::size_t hi_thr) : q(_q), max(hi_thr) {} 952*51c0b2f7Stbbdev 953*51c0b2f7Stbbdev void operator()(std::size_t thread_id) const { 954*51c0b2f7Stbbdev if (thread_id == max) { 955*51c0b2f7Stbbdev while ( q->size() < std::ptrdiff_t(max) ) { 956*51c0b2f7Stbbdev std::this_thread::yield(); 957*51c0b2f7Stbbdev } 958*51c0b2f7Stbbdev q->abort(); 959*51c0b2f7Stbbdev return; 960*51c0b2f7Stbbdev } 961*51c0b2f7Stbbdev try { 962*51c0b2f7Stbbdev q->push(42); 963*51c0b2f7Stbbdev ++num_pushed; 964*51c0b2f7Stbbdev } catch (...) { 965*51c0b2f7Stbbdev ++failed_pushes; 966*51c0b2f7Stbbdev } 967*51c0b2f7Stbbdev } 968*51c0b2f7Stbbdev }; 969*51c0b2f7Stbbdev 970*51c0b2f7Stbbdev class SimplePopBody { 971*51c0b2f7Stbbdev tbb::concurrent_bounded_queue<int>* q; 972*51c0b2f7Stbbdev std::ptrdiff_t max; 973*51c0b2f7Stbbdev std::ptrdiff_t prefill; 974*51c0b2f7Stbbdev public: 975*51c0b2f7Stbbdev SimplePopBody(tbb::concurrent_bounded_queue<int>* _q, std::size_t hi_thr, std::size_t nitems) 976*51c0b2f7Stbbdev : q(_q), max(hi_thr), prefill(nitems) {} 977*51c0b2f7Stbbdev 978*51c0b2f7Stbbdev void operator()(std::size_t thread_id) const { 979*51c0b2f7Stbbdev int e; 980*51c0b2f7Stbbdev if (thread_id == std::size_t(max)) { 981*51c0b2f7Stbbdev while (q->size()> prefill - max) { 982*51c0b2f7Stbbdev std::this_thread::yield(); 983*51c0b2f7Stbbdev } 984*51c0b2f7Stbbdev 985*51c0b2f7Stbbdev q->abort(); 986*51c0b2f7Stbbdev return; 987*51c0b2f7Stbbdev } 988*51c0b2f7Stbbdev try { 989*51c0b2f7Stbbdev q->pop(e); 990*51c0b2f7Stbbdev ++num_popped; 991*51c0b2f7Stbbdev } catch ( ... ) { 992*51c0b2f7Stbbdev ++failed_pops; 993*51c0b2f7Stbbdev } 994*51c0b2f7Stbbdev } 995*51c0b2f7Stbbdev }; 996*51c0b2f7Stbbdev 997*51c0b2f7Stbbdev void TestAbort() { 998*51c0b2f7Stbbdev for (std::size_t nthreads = MinThread; nthreads <= MaxThread; ++nthreads) { 999*51c0b2f7Stbbdev tbb::concurrent_bounded_queue<int> iq1; 1000*51c0b2f7Stbbdev iq1.set_capacity(0); 1001*51c0b2f7Stbbdev for (std::size_t i = 0; i < 10; ++i) { 1002*51c0b2f7Stbbdev num_pushed.store(0, std::memory_order_relaxed); 1003*51c0b2f7Stbbdev num_popped.store(0, std::memory_order_relaxed); 1004*51c0b2f7Stbbdev failed_pushes.store(0, std::memory_order_relaxed); 1005*51c0b2f7Stbbdev failed_pops.store(0, std::memory_order_relaxed); 1006*51c0b2f7Stbbdev SimplePushBody my_push_body1(&iq1, nthreads); 1007*51c0b2f7Stbbdev utils::NativeParallelFor(nthreads + 1, my_push_body1); 1008*51c0b2f7Stbbdev REQUIRE_MESSAGE(num_pushed == 0, "no elements should have been pushed to zero-sized queue"); 1009*51c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pushes == nthreads, "All threads should have failed to push an element to zero-sized queue"); 1010*51c0b2f7Stbbdev // Do not test popping each time in order to test queue destruction with no previous pops 1011*51c0b2f7Stbbdev if (nthreads < (MaxThread + MinThread) / 2) { 1012*51c0b2f7Stbbdev int e; 1013*51c0b2f7Stbbdev bool queue_empty = !iq1.try_pop(e); 1014*51c0b2f7Stbbdev REQUIRE_MESSAGE(queue_empty, "no elements should have been popped from zero-sized queue"); 1015*51c0b2f7Stbbdev } 1016*51c0b2f7Stbbdev } 1017*51c0b2f7Stbbdev 1018*51c0b2f7Stbbdev tbb::concurrent_bounded_queue<int> iq2; 1019*51c0b2f7Stbbdev iq2.set_capacity(2); 1020*51c0b2f7Stbbdev for (std::size_t i=0; i < 10; ++i) { 1021*51c0b2f7Stbbdev num_pushed.store(0, std::memory_order_relaxed); 1022*51c0b2f7Stbbdev num_popped.store(0, std::memory_order_relaxed); 1023*51c0b2f7Stbbdev failed_pushes.store(0, std::memory_order_relaxed); 1024*51c0b2f7Stbbdev failed_pops.store(0, std::memory_order_relaxed); 1025*51c0b2f7Stbbdev SimplePushBody my_push_body2(&iq2, nthreads); 1026*51c0b2f7Stbbdev utils::NativeParallelFor(nthreads + 1, my_push_body2); 1027*51c0b2f7Stbbdev REQUIRE_MESSAGE(num_pushed <= 2, "at most 2 elements should have been pushed to queue of size 2"); 1028*51c0b2f7Stbbdev if (nthreads>= 2) 1029*51c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pushes == nthreads - 2, "nthreads-2 threads should have failed to push an element to queue of size 2"); 1030*51c0b2f7Stbbdev int e; 1031*51c0b2f7Stbbdev while (iq2.try_pop(e)) ; 1032*51c0b2f7Stbbdev } 1033*51c0b2f7Stbbdev 1034*51c0b2f7Stbbdev tbb::concurrent_bounded_queue<int> iq3; 1035*51c0b2f7Stbbdev iq3.set_capacity(2); 1036*51c0b2f7Stbbdev for (std::size_t i = 0; i < 10; ++i) { 1037*51c0b2f7Stbbdev num_pushed.store(0, std::memory_order_relaxed); 1038*51c0b2f7Stbbdev num_popped.store(0, std::memory_order_relaxed); 1039*51c0b2f7Stbbdev failed_pushes.store(0, std::memory_order_relaxed); 1040*51c0b2f7Stbbdev failed_pops.store(0, std::memory_order_relaxed); 1041*51c0b2f7Stbbdev iq3.push(42); 1042*51c0b2f7Stbbdev iq3.push(42); 1043*51c0b2f7Stbbdev SimplePopBody my_pop_body(&iq3, nthreads, 2); 1044*51c0b2f7Stbbdev utils::NativeParallelFor( nthreads+1, my_pop_body ); 1045*51c0b2f7Stbbdev REQUIRE_MESSAGE(num_popped <= 2, "at most 2 elements should have been popped from queue of size 2"); 1046*51c0b2f7Stbbdev if (nthreads>= 2) 1047*51c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pops == nthreads - 2, "nthreads-2 threads should have failed to pop an element from queue of size 2"); 1048*51c0b2f7Stbbdev else { 1049*51c0b2f7Stbbdev int e; 1050*51c0b2f7Stbbdev iq3.pop(e); 1051*51c0b2f7Stbbdev } 1052*51c0b2f7Stbbdev } 1053*51c0b2f7Stbbdev 1054*51c0b2f7Stbbdev tbb::concurrent_bounded_queue<int> iq4; 1055*51c0b2f7Stbbdev std::size_t cap = nthreads / 2; 1056*51c0b2f7Stbbdev if (!cap) cap = 1; 1057*51c0b2f7Stbbdev iq4.set_capacity(cap); 1058*51c0b2f7Stbbdev for (int i=0; i<10; ++i) { 1059*51c0b2f7Stbbdev num_pushed.store(0, std::memory_order_relaxed); 1060*51c0b2f7Stbbdev num_popped.store(0, std::memory_order_relaxed); 1061*51c0b2f7Stbbdev failed_pushes.store(0, std::memory_order_relaxed); 1062*51c0b2f7Stbbdev failed_pops.store(0, std::memory_order_relaxed); 1063*51c0b2f7Stbbdev SimplePushBody my_push_body2(&iq4, nthreads); 1064*51c0b2f7Stbbdev utils::NativeParallelFor(nthreads + 1, my_push_body2); 1065*51c0b2f7Stbbdev REQUIRE_MESSAGE(num_pushed <= cap, "at most cap elements should have been pushed to queue of size cap"); 1066*51c0b2f7Stbbdev if (nthreads>= cap) 1067*51c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pushes == nthreads-cap, "nthreads-cap threads should have failed to push an element to queue of size cap"); 1068*51c0b2f7Stbbdev SimplePopBody my_pop_body(&iq4, nthreads, num_pushed); 1069*51c0b2f7Stbbdev utils::NativeParallelFor( nthreads+1, my_pop_body ); 1070*51c0b2f7Stbbdev REQUIRE_MESSAGE((int)num_popped <= cap, "at most cap elements should have been popped from queue of size cap"); 1071*51c0b2f7Stbbdev if (nthreads>= cap) 1072*51c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pops == nthreads-cap, "nthreads-cap threads should have failed to pop an element from queue of size cap"); 1073*51c0b2f7Stbbdev else { 1074*51c0b2f7Stbbdev int e; 1075*51c0b2f7Stbbdev while (iq4.try_pop(e)) ; 1076*51c0b2f7Stbbdev } 1077*51c0b2f7Stbbdev } 1078*51c0b2f7Stbbdev } 1079*51c0b2f7Stbbdev } 1080*51c0b2f7Stbbdev #endif 1081*51c0b2f7Stbbdev 1082*51c0b2f7Stbbdev template <template <typename...> class ContainerType> 1083*51c0b2f7Stbbdev void test_member_types() { 1084*51c0b2f7Stbbdev using container_type = ContainerType<int>; 1085*51c0b2f7Stbbdev static_assert(std::is_same<typename container_type::allocator_type, tbb::cache_aligned_allocator<int>>::value, 1086*51c0b2f7Stbbdev "Incorrect default template allocator"); 1087*51c0b2f7Stbbdev 1088*51c0b2f7Stbbdev static_assert(std::is_same<typename container_type::value_type, int>::value, 1089*51c0b2f7Stbbdev "Incorrect container value_type member type"); 1090*51c0b2f7Stbbdev 1091*51c0b2f7Stbbdev static_assert(std::is_signed<typename container_type::difference_type>::value, 1092*51c0b2f7Stbbdev "Incorrect container difference_type member type"); 1093*51c0b2f7Stbbdev 1094*51c0b2f7Stbbdev using value_type = typename container_type::value_type; 1095*51c0b2f7Stbbdev static_assert(std::is_same<typename container_type::reference, value_type&>::value, 1096*51c0b2f7Stbbdev "Incorrect container reference member type"); 1097*51c0b2f7Stbbdev static_assert(std::is_same<typename container_type::const_reference, const value_type&>::value, 1098*51c0b2f7Stbbdev "Incorrect container const_reference member type"); 1099*51c0b2f7Stbbdev using allocator_type = typename container_type::allocator_type; 1100*51c0b2f7Stbbdev static_assert(std::is_same<typename container_type::pointer, typename std::allocator_traits<allocator_type>::pointer>::value, 1101*51c0b2f7Stbbdev "Incorrect container pointer member type"); 1102*51c0b2f7Stbbdev static_assert(std::is_same<typename container_type::const_pointer, typename std::allocator_traits<allocator_type>::const_pointer>::value, 1103*51c0b2f7Stbbdev "Incorrect container const_pointer member type"); 1104*51c0b2f7Stbbdev 1105*51c0b2f7Stbbdev static_assert(utils::is_forward_iterator<typename container_type::iterator>::value, 1106*51c0b2f7Stbbdev "Incorrect container iterator member type"); 1107*51c0b2f7Stbbdev static_assert(!std::is_const<typename container_type::iterator::value_type>::value, 1108*51c0b2f7Stbbdev "Incorrect container iterator member type"); 1109*51c0b2f7Stbbdev static_assert(utils::is_forward_iterator<typename container_type::const_iterator>::value, 1110*51c0b2f7Stbbdev "Incorrect container const_iterator member type"); 1111*51c0b2f7Stbbdev static_assert(std::is_const<typename container_type::const_iterator::value_type>::value, 1112*51c0b2f7Stbbdev "Incorrect container iterator member type"); 1113*51c0b2f7Stbbdev } 1114*51c0b2f7Stbbdev 1115*51c0b2f7Stbbdev enum push_t { push_op, try_push_op }; 1116*51c0b2f7Stbbdev 1117*51c0b2f7Stbbdev template<push_t push_op> 1118*51c0b2f7Stbbdev struct pusher { 1119*51c0b2f7Stbbdev template<typename CQ, typename VType> 1120*51c0b2f7Stbbdev static bool push( CQ& queue, VType&& val ) { 1121*51c0b2f7Stbbdev queue.push( std::forward<VType>( val ) ); 1122*51c0b2f7Stbbdev return true; 1123*51c0b2f7Stbbdev } 1124*51c0b2f7Stbbdev }; 1125*51c0b2f7Stbbdev 1126*51c0b2f7Stbbdev template<> 1127*51c0b2f7Stbbdev struct pusher< try_push_op> { 1128*51c0b2f7Stbbdev template<typename CQ, typename VType> 1129*51c0b2f7Stbbdev static bool push( CQ& queue, VType&& val ) { 1130*51c0b2f7Stbbdev return queue.try_push( std::forward<VType>( val ) ); 1131*51c0b2f7Stbbdev } 1132*51c0b2f7Stbbdev }; 1133*51c0b2f7Stbbdev 1134*51c0b2f7Stbbdev enum pop_t { pop_op, try_pop_op }; 1135*51c0b2f7Stbbdev 1136*51c0b2f7Stbbdev template<pop_t pop_op> 1137*51c0b2f7Stbbdev struct popper { 1138*51c0b2f7Stbbdev template<typename CQ, typename VType> 1139*51c0b2f7Stbbdev static bool pop( CQ& queue, VType&& val ) { 1140*51c0b2f7Stbbdev if( queue.empty() ) return false; 1141*51c0b2f7Stbbdev queue.pop( std::forward<VType>( val ) ); 1142*51c0b2f7Stbbdev return true; 1143*51c0b2f7Stbbdev } 1144*51c0b2f7Stbbdev }; 1145*51c0b2f7Stbbdev 1146*51c0b2f7Stbbdev template<> 1147*51c0b2f7Stbbdev struct popper<try_pop_op> { 1148*51c0b2f7Stbbdev template<typename CQ, typename VType> 1149*51c0b2f7Stbbdev static bool pop( CQ& queue, VType&& val ) { 1150*51c0b2f7Stbbdev return queue.try_pop( std::forward<VType>( val ) ); 1151*51c0b2f7Stbbdev } 1152*51c0b2f7Stbbdev }; 1153*51c0b2f7Stbbdev 1154*51c0b2f7Stbbdev struct MoveOperationTracker { 1155*51c0b2f7Stbbdev static std::size_t copy_constructor_called_times; 1156*51c0b2f7Stbbdev static std::size_t move_constructor_called_times; 1157*51c0b2f7Stbbdev static std::size_t copy_assignment_called_times; 1158*51c0b2f7Stbbdev static std::size_t move_assignment_called_times; 1159*51c0b2f7Stbbdev 1160*51c0b2f7Stbbdev MoveOperationTracker() {} 1161*51c0b2f7Stbbdev MoveOperationTracker(const MoveOperationTracker&) { 1162*51c0b2f7Stbbdev ++copy_constructor_called_times; 1163*51c0b2f7Stbbdev } 1164*51c0b2f7Stbbdev MoveOperationTracker(MoveOperationTracker&&) { 1165*51c0b2f7Stbbdev ++move_constructor_called_times; 1166*51c0b2f7Stbbdev } 1167*51c0b2f7Stbbdev MoveOperationTracker& operator=(MoveOperationTracker const&) { 1168*51c0b2f7Stbbdev ++copy_assignment_called_times; 1169*51c0b2f7Stbbdev return *this; 1170*51c0b2f7Stbbdev } 1171*51c0b2f7Stbbdev MoveOperationTracker& operator=(MoveOperationTracker&&) { 1172*51c0b2f7Stbbdev ++move_assignment_called_times; 1173*51c0b2f7Stbbdev return *this; 1174*51c0b2f7Stbbdev } 1175*51c0b2f7Stbbdev }; 1176*51c0b2f7Stbbdev 1177*51c0b2f7Stbbdev size_t MoveOperationTracker::copy_constructor_called_times = 0; 1178*51c0b2f7Stbbdev size_t MoveOperationTracker::move_constructor_called_times = 0; 1179*51c0b2f7Stbbdev size_t MoveOperationTracker::copy_assignment_called_times = 0; 1180*51c0b2f7Stbbdev size_t MoveOperationTracker::move_assignment_called_times = 0; 1181*51c0b2f7Stbbdev 1182*51c0b2f7Stbbdev template <class CQ, push_t push_op, pop_t pop_op> 1183*51c0b2f7Stbbdev void TestMoveSupport() { 1184*51c0b2f7Stbbdev std::size_t &mcct = MoveOperationTracker::move_constructor_called_times; 1185*51c0b2f7Stbbdev std::size_t &ccct = MoveOperationTracker::copy_constructor_called_times; 1186*51c0b2f7Stbbdev std::size_t &cact = MoveOperationTracker::copy_assignment_called_times; 1187*51c0b2f7Stbbdev std::size_t &mact = MoveOperationTracker::move_assignment_called_times; 1188*51c0b2f7Stbbdev mcct = ccct = cact = mact = 0; 1189*51c0b2f7Stbbdev 1190*51c0b2f7Stbbdev CQ q; 1191*51c0b2f7Stbbdev 1192*51c0b2f7Stbbdev REQUIRE_MESSAGE(mcct == 0, "Value must be zero-initialized"); 1193*51c0b2f7Stbbdev REQUIRE_MESSAGE(ccct == 0, "Value must be zero-initialized"); 1194*51c0b2f7Stbbdev CHECK(pusher<push_op>::push( q, MoveOperationTracker() )); 1195*51c0b2f7Stbbdev REQUIRE_MESSAGE(mcct == 1, "Not working push(T&&) or try_push(T&&)?"); 1196*51c0b2f7Stbbdev REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&) or try_push(T&&)"); 1197*51c0b2f7Stbbdev 1198*51c0b2f7Stbbdev MoveOperationTracker ob; 1199*51c0b2f7Stbbdev CHECK(pusher<push_op>::push( q, std::move(ob) )); 1200*51c0b2f7Stbbdev REQUIRE_MESSAGE(mcct == 2, "Not working push(T&&) or try_push(T&&)?"); 1201*51c0b2f7Stbbdev REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&) or try_push(T&&)"); 1202*51c0b2f7Stbbdev 1203*51c0b2f7Stbbdev REQUIRE_MESSAGE(cact == 0, "Copy assignment called during push(T&&) or try_push(T&&)"); 1204*51c0b2f7Stbbdev REQUIRE_MESSAGE(mact == 0, "Move assignment called during push(T&&) or try_push(T&&)"); 1205*51c0b2f7Stbbdev 1206*51c0b2f7Stbbdev bool result = popper<pop_op>::pop( q, ob ); 1207*51c0b2f7Stbbdev CHECK(result); 1208*51c0b2f7Stbbdev REQUIRE_MESSAGE(cact == 0, "Copy assignment called during try_pop(T&&)"); 1209*51c0b2f7Stbbdev REQUIRE_MESSAGE(mact == 1, "Move assignment was not called during try_pop(T&&)"); 1210*51c0b2f7Stbbdev } 1211*51c0b2f7Stbbdev 1212*51c0b2f7Stbbdev void TestMoveSupportInPushPop() { 1213*51c0b2f7Stbbdev TestMoveSupport<tbb::concurrent_queue<MoveOperationTracker>, push_op, try_pop_op>(); 1214*51c0b2f7Stbbdev TestMoveSupport<tbb::concurrent_bounded_queue<MoveOperationTracker>, push_op, pop_op>(); 1215*51c0b2f7Stbbdev TestMoveSupport<tbb::concurrent_bounded_queue<MoveOperationTracker>, try_push_op, try_pop_op>(); 1216*51c0b2f7Stbbdev } 1217*51c0b2f7Stbbdev 1218*51c0b2f7Stbbdev template<class T> 1219*51c0b2f7Stbbdev class allocator: public tbb::cache_aligned_allocator<T> { 1220*51c0b2f7Stbbdev public: 1221*51c0b2f7Stbbdev std::size_t m_unique_id; 1222*51c0b2f7Stbbdev 1223*51c0b2f7Stbbdev allocator() : m_unique_id( 0 ) {} 1224*51c0b2f7Stbbdev 1225*51c0b2f7Stbbdev allocator(size_t unique_id) { m_unique_id = unique_id; } 1226*51c0b2f7Stbbdev 1227*51c0b2f7Stbbdev template<typename U> 1228*51c0b2f7Stbbdev allocator(const allocator<U>& a) noexcept { m_unique_id = a.m_unique_id; } 1229*51c0b2f7Stbbdev 1230*51c0b2f7Stbbdev template<typename U> 1231*51c0b2f7Stbbdev struct rebind { typedef allocator<U> other; }; 1232*51c0b2f7Stbbdev 1233*51c0b2f7Stbbdev friend bool operator==(const allocator& lhs, const allocator& rhs) { 1234*51c0b2f7Stbbdev return lhs.m_unique_id == rhs.m_unique_id; 1235*51c0b2f7Stbbdev } 1236*51c0b2f7Stbbdev }; 1237*51c0b2f7Stbbdev 1238*51c0b2f7Stbbdev template <typename Queue> 1239*51c0b2f7Stbbdev void AssertEquality(Queue &q, const std::vector<typename Queue::value_type> &vec) { 1240*51c0b2f7Stbbdev CHECK(q.size() == typename Queue::size_type(vec.size())); 1241*51c0b2f7Stbbdev CHECK(std::equal(q.unsafe_begin(), q.unsafe_end(), vec.begin())); 1242*51c0b2f7Stbbdev } 1243*51c0b2f7Stbbdev 1244*51c0b2f7Stbbdev template <typename Queue> 1245*51c0b2f7Stbbdev void AssertEmptiness(Queue &q) { 1246*51c0b2f7Stbbdev CHECK(q.empty()); 1247*51c0b2f7Stbbdev CHECK(!q.size()); 1248*51c0b2f7Stbbdev typename Queue::value_type elem; 1249*51c0b2f7Stbbdev CHECK(!q.try_pop(elem)); 1250*51c0b2f7Stbbdev } 1251*51c0b2f7Stbbdev 1252*51c0b2f7Stbbdev template <push_t push_op, typename Queue> 1253*51c0b2f7Stbbdev void FillTest(Queue &q, const std::vector<typename Queue::value_type> &vec) { 1254*51c0b2f7Stbbdev for (typename std::vector<typename Queue::value_type>::const_iterator it = vec.begin(); it != vec.end(); ++it) 1255*51c0b2f7Stbbdev CHECK(pusher<push_op>::push(q, *it)); 1256*51c0b2f7Stbbdev AssertEquality(q, vec); 1257*51c0b2f7Stbbdev } 1258*51c0b2f7Stbbdev 1259*51c0b2f7Stbbdev template <pop_t pop_op, typename Queue> 1260*51c0b2f7Stbbdev void EmptyTest(Queue &q, const std::vector<typename Queue::value_type> &vec) { 1261*51c0b2f7Stbbdev typedef typename Queue::value_type value_type; 1262*51c0b2f7Stbbdev 1263*51c0b2f7Stbbdev value_type elem; 1264*51c0b2f7Stbbdev typename std::vector<value_type>::const_iterator it = vec.begin(); 1265*51c0b2f7Stbbdev while (popper<pop_op>::pop(q, elem)) { 1266*51c0b2f7Stbbdev CHECK(elem == *it); 1267*51c0b2f7Stbbdev ++it; 1268*51c0b2f7Stbbdev } 1269*51c0b2f7Stbbdev CHECK(it == vec.end()); 1270*51c0b2f7Stbbdev AssertEmptiness(q); 1271*51c0b2f7Stbbdev } 1272*51c0b2f7Stbbdev 1273*51c0b2f7Stbbdev template <typename T, typename A> 1274*51c0b2f7Stbbdev void bounded_queue_specific_test(tbb::concurrent_queue<T, A> &, const std::vector<T> &) { /* do nothing */ } 1275*51c0b2f7Stbbdev 1276*51c0b2f7Stbbdev template <typename T, typename A> 1277*51c0b2f7Stbbdev void bounded_queue_specific_test(tbb::concurrent_bounded_queue<T, A> &q, const std::vector<T> &vec) { 1278*51c0b2f7Stbbdev typedef typename tbb::concurrent_bounded_queue<T, A>::size_type size_type; 1279*51c0b2f7Stbbdev 1280*51c0b2f7Stbbdev FillTest<try_push_op>(q, vec); 1281*51c0b2f7Stbbdev tbb::concurrent_bounded_queue<T, A> q2 = q; 1282*51c0b2f7Stbbdev EmptyTest<pop_op>(q, vec); 1283*51c0b2f7Stbbdev 1284*51c0b2f7Stbbdev // capacity 1285*51c0b2f7Stbbdev q2.set_capacity(size_type(vec.size())); 1286*51c0b2f7Stbbdev CHECK(q2.capacity() == size_type(vec.size())); 1287*51c0b2f7Stbbdev CHECK(q2.size() == size_type(vec.size())); 1288*51c0b2f7Stbbdev CHECK(!q2.try_push(vec[0])); 1289*51c0b2f7Stbbdev q.abort(); 1290*51c0b2f7Stbbdev } 1291*51c0b2f7Stbbdev 1292*51c0b2f7Stbbdev // Checks operability of the queue the data was moved from 1293*51c0b2f7Stbbdev template<typename T, typename CQ> 1294*51c0b2f7Stbbdev void TestQueueOperabilityAfterDataMove( CQ& queue ) { 1295*51c0b2f7Stbbdev const std::size_t size = 10; 1296*51c0b2f7Stbbdev std::vector<T> v(size); 1297*51c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i ) v[i] = T( i * i + i ); 1298*51c0b2f7Stbbdev 1299*51c0b2f7Stbbdev FillTest<push_op>(queue, v); 1300*51c0b2f7Stbbdev EmptyTest<try_pop_op>(queue, v); 1301*51c0b2f7Stbbdev bounded_queue_specific_test(queue, v); 1302*51c0b2f7Stbbdev } 1303*51c0b2f7Stbbdev 1304*51c0b2f7Stbbdev template<class CQ, class T> 1305*51c0b2f7Stbbdev void TestMoveConstructors() { 1306*51c0b2f7Stbbdev T::construction_num = T::destruction_num = 0; 1307*51c0b2f7Stbbdev CQ src_queue( allocator<T>(0) ); 1308*51c0b2f7Stbbdev const std::size_t size = 10; 1309*51c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i ) 1310*51c0b2f7Stbbdev src_queue.push( T(i + (i ^ size)) ); 1311*51c0b2f7Stbbdev CHECK(T::construction_num == 2 * size); 1312*51c0b2f7Stbbdev CHECK(T::destruction_num == size); 1313*51c0b2f7Stbbdev 1314*51c0b2f7Stbbdev const T* locations[size]; 1315*51c0b2f7Stbbdev typename CQ::const_iterator qit = src_queue.unsafe_begin(); 1316*51c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i, ++qit ) 1317*51c0b2f7Stbbdev locations[i] = &(*qit); 1318*51c0b2f7Stbbdev 1319*51c0b2f7Stbbdev // Ensuring allocation operation takes place during move when allocators are different 1320*51c0b2f7Stbbdev T::construction_num = T::destruction_num = 0; 1321*51c0b2f7Stbbdev CQ dst_queue( std::move(src_queue), allocator<T>(1) ); 1322*51c0b2f7Stbbdev CHECK(T::construction_num == size); 1323*51c0b2f7Stbbdev CHECK(T::destruction_num == size * 2); // One item is used by the queue destructor 1324*51c0b2f7Stbbdev 1325*51c0b2f7Stbbdev TestQueueOperabilityAfterDataMove<T>( src_queue ); 1326*51c0b2f7Stbbdev 1327*51c0b2f7Stbbdev qit = dst_queue.unsafe_begin(); 1328*51c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i, ++qit ) { 1329*51c0b2f7Stbbdev REQUIRE_MESSAGE(locations[i] != &(*qit), "an item should have been copied but was not" ); 1330*51c0b2f7Stbbdev locations[i] = &(*qit); 1331*51c0b2f7Stbbdev } 1332*51c0b2f7Stbbdev 1333*51c0b2f7Stbbdev T::construction_num = T::destruction_num = 0; 1334*51c0b2f7Stbbdev // Ensuring there is no allocation operation during move with equal allocators 1335*51c0b2f7Stbbdev CQ dst_queue2( std::move(dst_queue), allocator<T>(1) ); 1336*51c0b2f7Stbbdev CHECK(T::construction_num == 0); 1337*51c0b2f7Stbbdev CHECK(T::destruction_num == 0); 1338*51c0b2f7Stbbdev 1339*51c0b2f7Stbbdev TestQueueOperabilityAfterDataMove<T>( dst_queue ); 1340*51c0b2f7Stbbdev 1341*51c0b2f7Stbbdev qit = dst_queue2.unsafe_begin(); 1342*51c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i, ++qit ) { 1343*51c0b2f7Stbbdev REQUIRE_MESSAGE(locations[i] == &(*qit), "an item should have been moved but was not" ); 1344*51c0b2f7Stbbdev } 1345*51c0b2f7Stbbdev 1346*51c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i) { 1347*51c0b2f7Stbbdev T test(i + (i ^ size)); 1348*51c0b2f7Stbbdev T popped; 1349*51c0b2f7Stbbdev bool pop_result = dst_queue2.try_pop( popped ); 1350*51c0b2f7Stbbdev CHECK(pop_result); 1351*51c0b2f7Stbbdev CHECK(test == popped); 1352*51c0b2f7Stbbdev } 1353*51c0b2f7Stbbdev CHECK(dst_queue2.empty()); 1354*51c0b2f7Stbbdev CHECK(dst_queue2.size() == 0); 1355*51c0b2f7Stbbdev } 1356*51c0b2f7Stbbdev 1357*51c0b2f7Stbbdev void TestMoveConstruction() { 1358*51c0b2f7Stbbdev TestMoveConstructors<ConcQWithSizeWrapper<Bar, allocator<Bar>>, Bar>(); 1359*51c0b2f7Stbbdev TestMoveConstructors<tbb::concurrent_bounded_queue<Bar, allocator<Bar>>, Bar>(); 1360*51c0b2f7Stbbdev } 1361*51c0b2f7Stbbdev 1362*51c0b2f7Stbbdev class NonTrivialConstructorType { 1363*51c0b2f7Stbbdev public: 1364*51c0b2f7Stbbdev NonTrivialConstructorType( int a = 0 ) : m_a( a ), m_str( "" ) {} 1365*51c0b2f7Stbbdev NonTrivialConstructorType( const std::string& str ) : m_a( 0 ), m_str( str ) {} 1366*51c0b2f7Stbbdev NonTrivialConstructorType( int a, const std::string& str ) : m_a( a ), m_str( str ) {} 1367*51c0b2f7Stbbdev int get_a() const { return m_a; } 1368*51c0b2f7Stbbdev std::string get_str() const { return m_str; } 1369*51c0b2f7Stbbdev private: 1370*51c0b2f7Stbbdev int m_a; 1371*51c0b2f7Stbbdev std::string m_str; 1372*51c0b2f7Stbbdev }; 1373*51c0b2f7Stbbdev 1374*51c0b2f7Stbbdev enum emplace_t { emplace_op, try_emplace_op }; 1375*51c0b2f7Stbbdev 1376*51c0b2f7Stbbdev template<emplace_t emplace_op> 1377*51c0b2f7Stbbdev struct emplacer { 1378*51c0b2f7Stbbdev template<typename CQ, typename... Args> 1379*51c0b2f7Stbbdev static void emplace( CQ& queue, Args&&... val ) { queue.emplace( std::forward<Args>( val )... ); } 1380*51c0b2f7Stbbdev }; 1381*51c0b2f7Stbbdev 1382*51c0b2f7Stbbdev template<> 1383*51c0b2f7Stbbdev struct emplacer <try_emplace_op> { 1384*51c0b2f7Stbbdev template<typename CQ, typename... Args> 1385*51c0b2f7Stbbdev static void emplace( CQ& queue, Args&&... val ) { 1386*51c0b2f7Stbbdev bool result = queue.try_emplace( std::forward<Args>( val )... ); 1387*51c0b2f7Stbbdev REQUIRE_MESSAGE(result, "try_emplace error\n"); 1388*51c0b2f7Stbbdev } 1389*51c0b2f7Stbbdev }; 1390*51c0b2f7Stbbdev 1391*51c0b2f7Stbbdev template<typename CQ, emplace_t emplace_op> 1392*51c0b2f7Stbbdev void TestEmplaceInQueue() { 1393*51c0b2f7Stbbdev CQ cq; 1394*51c0b2f7Stbbdev std::string test_str = "I'm being emplaced!"; 1395*51c0b2f7Stbbdev { 1396*51c0b2f7Stbbdev emplacer<emplace_op>::emplace( cq, 5 ); 1397*51c0b2f7Stbbdev CHECK(cq.size() == 1); 1398*51c0b2f7Stbbdev NonTrivialConstructorType popped( -1 ); 1399*51c0b2f7Stbbdev bool result = cq.try_pop( popped ); 1400*51c0b2f7Stbbdev CHECK(result); 1401*51c0b2f7Stbbdev CHECK(popped.get_a() == 5); 1402*51c0b2f7Stbbdev CHECK(popped.get_str() == std::string( "" )); 1403*51c0b2f7Stbbdev } 1404*51c0b2f7Stbbdev 1405*51c0b2f7Stbbdev CHECK(cq.empty()); 1406*51c0b2f7Stbbdev 1407*51c0b2f7Stbbdev { 1408*51c0b2f7Stbbdev NonTrivialConstructorType popped( -1 ); 1409*51c0b2f7Stbbdev emplacer<emplace_op>::emplace( cq, std::string(test_str) ); 1410*51c0b2f7Stbbdev bool result = cq.try_pop( popped ); 1411*51c0b2f7Stbbdev CHECK(result); 1412*51c0b2f7Stbbdev CHECK(popped.get_a() == 0); 1413*51c0b2f7Stbbdev CHECK(popped.get_str() == test_str); 1414*51c0b2f7Stbbdev } 1415*51c0b2f7Stbbdev 1416*51c0b2f7Stbbdev CHECK(cq.empty()); 1417*51c0b2f7Stbbdev 1418*51c0b2f7Stbbdev { 1419*51c0b2f7Stbbdev NonTrivialConstructorType popped( -1, "" ); 1420*51c0b2f7Stbbdev emplacer<emplace_op>::emplace( cq, 5, std::string(test_str) ); 1421*51c0b2f7Stbbdev bool result = cq.try_pop( popped ); 1422*51c0b2f7Stbbdev CHECK(result); 1423*51c0b2f7Stbbdev CHECK(popped.get_a() == 5); 1424*51c0b2f7Stbbdev CHECK(popped.get_str() == test_str); 1425*51c0b2f7Stbbdev } 1426*51c0b2f7Stbbdev } 1427*51c0b2f7Stbbdev void TestEmplace() { 1428*51c0b2f7Stbbdev TestEmplaceInQueue<ConcQWithSizeWrapper<NonTrivialConstructorType>, emplace_op>(); 1429*51c0b2f7Stbbdev TestEmplaceInQueue<tbb::concurrent_bounded_queue<NonTrivialConstructorType>, emplace_op>(); 1430*51c0b2f7Stbbdev TestEmplaceInQueue<tbb::concurrent_bounded_queue<NonTrivialConstructorType>, try_emplace_op>(); 1431*51c0b2f7Stbbdev } 1432*51c0b2f7Stbbdev 1433*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 1434*51c0b2f7Stbbdev template <template <typename...> typename TQueue> 1435*51c0b2f7Stbbdev void TestDeductionGuides() { 1436*51c0b2f7Stbbdev using ComplexType = const std::string*; 1437*51c0b2f7Stbbdev std::vector<ComplexType> v; 1438*51c0b2f7Stbbdev 1439*51c0b2f7Stbbdev // check TQueue(InputIterator, InputIterator) 1440*51c0b2f7Stbbdev TQueue q1(v.begin(), v.end()); 1441*51c0b2f7Stbbdev static_assert(std::is_same<decltype(q1), TQueue<ComplexType>>::value); 1442*51c0b2f7Stbbdev 1443*51c0b2f7Stbbdev // check TQueue(InputIterator, InputIterator, Allocator) 1444*51c0b2f7Stbbdev TQueue q2(v.begin(), v.end(), std::allocator<ComplexType>()); 1445*51c0b2f7Stbbdev static_assert(std::is_same<decltype(q2), TQueue<ComplexType, std::allocator<ComplexType>>>::value); 1446*51c0b2f7Stbbdev 1447*51c0b2f7Stbbdev // check TQueue(TQueue &) 1448*51c0b2f7Stbbdev TQueue q3(q1); 1449*51c0b2f7Stbbdev static_assert(std::is_same<decltype(q3), decltype(q1)>::value); 1450*51c0b2f7Stbbdev 1451*51c0b2f7Stbbdev // check TQueue(TQueue &, Allocator) 1452*51c0b2f7Stbbdev TQueue q4(q2, std::allocator<ComplexType>()); 1453*51c0b2f7Stbbdev static_assert(std::is_same<decltype(q4), decltype(q2)>::value); 1454*51c0b2f7Stbbdev 1455*51c0b2f7Stbbdev // check TQueue(TQueue &&) 1456*51c0b2f7Stbbdev TQueue q5(std::move(q1)); 1457*51c0b2f7Stbbdev static_assert(std::is_same<decltype(q5), decltype(q1)>::value); 1458*51c0b2f7Stbbdev 1459*51c0b2f7Stbbdev // check TQueue(TQueue &&, Allocator) 1460*51c0b2f7Stbbdev TQueue q6(std::move(q4), std::allocator<ComplexType>()); 1461*51c0b2f7Stbbdev static_assert(std::is_same<decltype(q6), decltype(q4)>::value); 1462*51c0b2f7Stbbdev } 1463*51c0b2f7Stbbdev #endif 1464*51c0b2f7Stbbdev 1465*51c0b2f7Stbbdev 1466*51c0b2f7Stbbdev //! Test constructors 1467*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement 1468*51c0b2f7Stbbdev TEST_CASE("testing constructors") { 1469*51c0b2f7Stbbdev TestQueueConstructors(); 1470*51c0b2f7Stbbdev } 1471*51c0b2f7Stbbdev 1472*51c0b2f7Stbbdev //! Test work with empty queue 1473*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement 1474*51c0b2f7Stbbdev TEST_CASE("testing work with empty queue") { 1475*51c0b2f7Stbbdev TestEmptiness(); 1476*51c0b2f7Stbbdev } 1477*51c0b2f7Stbbdev 1478*51c0b2f7Stbbdev //! Test set capacity operation 1479*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement 1480*51c0b2f7Stbbdev TEST_CASE("testing set capacity operation") { 1481*51c0b2f7Stbbdev TestFullness(); 1482*51c0b2f7Stbbdev } 1483*51c0b2f7Stbbdev 1484*51c0b2f7Stbbdev //! Test clean operation 1485*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement 1486*51c0b2f7Stbbdev TEST_CASE("testing clean operation") { 1487*51c0b2f7Stbbdev TestClearWorks(); 1488*51c0b2f7Stbbdev } 1489*51c0b2f7Stbbdev 1490*51c0b2f7Stbbdev //! Test move constructors 1491*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement 1492*51c0b2f7Stbbdev TEST_CASE("testing move constructor") { 1493*51c0b2f7Stbbdev TestMoveConstruction(); 1494*51c0b2f7Stbbdev } 1495*51c0b2f7Stbbdev 1496*51c0b2f7Stbbdev //! Test move support in push and pop 1497*51c0b2f7Stbbdev //! \brief \ref requirement 1498*51c0b2f7Stbbdev TEST_CASE("testing move support in push and pop") { 1499*51c0b2f7Stbbdev TestMoveSupportInPushPop(); 1500*51c0b2f7Stbbdev } 1501*51c0b2f7Stbbdev 1502*51c0b2f7Stbbdev //! Test emplace operation 1503*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement 1504*51c0b2f7Stbbdev TEST_CASE("testing emplace") { 1505*51c0b2f7Stbbdev TestEmplace(); 1506*51c0b2f7Stbbdev } 1507*51c0b2f7Stbbdev 1508*51c0b2f7Stbbdev //! Test concurrent_queues member types 1509*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement 1510*51c0b2f7Stbbdev TEST_CASE("testing concurrent_queues member types"){ 1511*51c0b2f7Stbbdev test_member_types<tbb::concurrent_queue>(); 1512*51c0b2f7Stbbdev test_member_types<tbb::concurrent_bounded_queue>(); 1513*51c0b2f7Stbbdev 1514*51c0b2f7Stbbdev // Test size_type 1515*51c0b2f7Stbbdev static_assert(std::is_unsigned<typename tbb::concurrent_queue<int>::size_type>::value, 1516*51c0b2f7Stbbdev "Incorrect tbb::concurrent_queue::size_type member type"); 1517*51c0b2f7Stbbdev static_assert(std::is_signed<typename tbb::concurrent_bounded_queue<int>::size_type>::value, 1518*51c0b2f7Stbbdev "Incorrect tbb::concurrent_bounded_queue::size_type member type"); 1519*51c0b2f7Stbbdev } 1520*51c0b2f7Stbbdev 1521*51c0b2f7Stbbdev //! Test iterators 1522*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement 1523*51c0b2f7Stbbdev TEST_CASE("testing iterators") { 1524*51c0b2f7Stbbdev TestQueueIteratorWorks(); 1525*51c0b2f7Stbbdev } 1526*51c0b2f7Stbbdev 1527*51c0b2f7Stbbdev //! Test concurrent oprations support 1528*51c0b2f7Stbbdev //! \brief \ref requirement 1529*51c0b2f7Stbbdev TEST_CASE("testing concurrent oprations support") { 1530*51c0b2f7Stbbdev TestConcurrentPushPop(); 1531*51c0b2f7Stbbdev } 1532*51c0b2f7Stbbdev 1533*51c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 1534*51c0b2f7Stbbdev //! Test exception safety 1535*51c0b2f7Stbbdev //! \brief \ref requirement 1536*51c0b2f7Stbbdev TEST_CASE("testing exception safety") { 1537*51c0b2f7Stbbdev TestExceptions(); 1538*51c0b2f7Stbbdev } 1539*51c0b2f7Stbbdev 1540*51c0b2f7Stbbdev //! Test abort operation 1541*51c0b2f7Stbbdev //! \brief \ref interface \ref requirement 1542*51c0b2f7Stbbdev TEST_CASE("testing abort operation") { 1543*51c0b2f7Stbbdev TestAbort(); 1544*51c0b2f7Stbbdev } 1545*51c0b2f7Stbbdev #endif 1546*51c0b2f7Stbbdev 1547*51c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 1548*51c0b2f7Stbbdev //! Test deduction guides 1549*51c0b2f7Stbbdev //! \brief \ref interface 1550*51c0b2f7Stbbdev TEST_CASE("testing deduction guides") { 1551*51c0b2f7Stbbdev TestDeductionGuides<tbb::concurrent_queue>(); 1552*51c0b2f7Stbbdev TestDeductionGuides<tbb::concurrent_bounded_queue>(); 1553*51c0b2f7Stbbdev } 1554*51c0b2f7Stbbdev #endif 1555