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