151c0b2f7Stbbdev /*
2*8155aaebSkboyarinov     Copyright (c) 2005-2022 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #include <common/test.h>
1851c0b2f7Stbbdev #include <common/utils.h>
1951c0b2f7Stbbdev #include <common/utils_report.h>
2051c0b2f7Stbbdev #include <common/custom_allocators.h>
2151c0b2f7Stbbdev #include <common/container_move_support.h>
22b15aabb3Stbbdev #include <common/test_comparisons.h>
2351c0b2f7Stbbdev 
2449e08aacStbbdev #include "oneapi/tbb/concurrent_queue.h"
2549e08aacStbbdev #include "oneapi/tbb/cache_aligned_allocator.h"
2651c0b2f7Stbbdev #include <type_traits>
2751c0b2f7Stbbdev #include <atomic>
2851c0b2f7Stbbdev 
2951c0b2f7Stbbdev //! \file conformance_concurrent_queue.cpp
3051c0b2f7Stbbdev //! \brief Test for [containers.concurrent_queue containers.concurrent_bounded_queue] specification
3151c0b2f7Stbbdev 
3251c0b2f7Stbbdev template <typename T>
3349e08aacStbbdev using test_allocator = StaticSharedCountingAllocator<oneapi::tbb::cache_aligned_allocator<T>>;
3451c0b2f7Stbbdev 
3551c0b2f7Stbbdev static constexpr std::size_t MinThread = 1;
3651c0b2f7Stbbdev static constexpr std::size_t MaxThread = 4;
3751c0b2f7Stbbdev 
3851c0b2f7Stbbdev static constexpr std::size_t MAXTHREAD = 256;
3951c0b2f7Stbbdev 
4051c0b2f7Stbbdev static constexpr std::size_t M = 10000;
4151c0b2f7Stbbdev static std::atomic<long> PopKind[3];
4251c0b2f7Stbbdev 
4351c0b2f7Stbbdev static int Sum[MAXTHREAD];
4451c0b2f7Stbbdev 
4551c0b2f7Stbbdev template<typename CQ, typename ValueType, typename CounterType>
4651c0b2f7Stbbdev void push(CQ& q, ValueType v, CounterType i) {
4751c0b2f7Stbbdev     switch (i % 3) {
4851c0b2f7Stbbdev         case 0: q.push( v); break;
4951c0b2f7Stbbdev         case 1: q.push( std::move(v)); break;
5051c0b2f7Stbbdev         case 2: q.emplace( v); break;
5151c0b2f7Stbbdev         default: CHECK(false); break;
5251c0b2f7Stbbdev     }
5351c0b2f7Stbbdev }
5451c0b2f7Stbbdev 
5551c0b2f7Stbbdev template<typename T>
5649e08aacStbbdev class ConcQWithCapacity : public oneapi::tbb::concurrent_queue<T, test_allocator<T>> {
5749e08aacStbbdev     using base_type = oneapi::tbb::concurrent_queue<T, test_allocator<T>>;
5851c0b2f7Stbbdev public:
5951c0b2f7Stbbdev     ConcQWithCapacity() : my_capacity( std::size_t(-1) / (sizeof(void*) + sizeof(T)) ) {}
6051c0b2f7Stbbdev     std::size_t size() const {
6151c0b2f7Stbbdev         return this->unsafe_size();
6251c0b2f7Stbbdev     }
6351c0b2f7Stbbdev 
6451c0b2f7Stbbdev     std::size_t capacity() const {
6551c0b2f7Stbbdev         return my_capacity;
6651c0b2f7Stbbdev     }
6751c0b2f7Stbbdev 
6851c0b2f7Stbbdev     void set_capacity( const std::size_t n ) {
6951c0b2f7Stbbdev         my_capacity = n;
7051c0b2f7Stbbdev     }
7151c0b2f7Stbbdev 
7251c0b2f7Stbbdev     bool try_push( const T& source ) {
7351c0b2f7Stbbdev         base_type::push( source);
7451c0b2f7Stbbdev         return source.get_serial() < my_capacity;
7551c0b2f7Stbbdev     }
7651c0b2f7Stbbdev 
7751c0b2f7Stbbdev     bool try_pop( T& dest ) {
7851c0b2f7Stbbdev         base_type::try_pop( dest);
7951c0b2f7Stbbdev         return dest.get_serial() < my_capacity;
8051c0b2f7Stbbdev     }
8151c0b2f7Stbbdev 
8251c0b2f7Stbbdev private:
8351c0b2f7Stbbdev     std::size_t my_capacity;
8451c0b2f7Stbbdev };
8551c0b2f7Stbbdev 
8651c0b2f7Stbbdev template<typename CQ, typename T>
8751c0b2f7Stbbdev void TestEmptyQueue() {
8851c0b2f7Stbbdev     const CQ queue;
8951c0b2f7Stbbdev     CHECK(queue.size() == 0);
9051c0b2f7Stbbdev     CHECK(queue.capacity()> 0);
9151c0b2f7Stbbdev     CHECK(size_t(queue.capacity())>= std::size_t(-1)/(sizeof(void*)+sizeof(T)));
9251c0b2f7Stbbdev }
9351c0b2f7Stbbdev 
9451c0b2f7Stbbdev void TestEmptiness() {
9551c0b2f7Stbbdev     TestEmptyQueue<ConcQWithCapacity<char>, char>();
9651c0b2f7Stbbdev     TestEmptyQueue<ConcQWithCapacity<move_support_tests::Foo>, move_support_tests::Foo>();
9749e08aacStbbdev     TestEmptyQueue<oneapi::tbb::concurrent_bounded_queue<char, test_allocator<char>>, char>();
9849e08aacStbbdev     TestEmptyQueue<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo,
9951c0b2f7Stbbdev            test_allocator<move_support_tests::Foo>>, move_support_tests::Foo>();
10051c0b2f7Stbbdev }
10151c0b2f7Stbbdev 
10251c0b2f7Stbbdev template<typename CQ, typename T>
10351c0b2f7Stbbdev void TestFullQueue() {
10451c0b2f7Stbbdev     using allocator_type = decltype(std::declval<CQ>().get_allocator());
10551c0b2f7Stbbdev 
10651c0b2f7Stbbdev     for (std::size_t n = 0; n < 100; ++n) {
10751c0b2f7Stbbdev         allocator_type::init_counters();
10851c0b2f7Stbbdev         {
10951c0b2f7Stbbdev             CQ queue;
11051c0b2f7Stbbdev             queue.set_capacity(n);
11151c0b2f7Stbbdev             for (std::size_t i = 0; i <= n; ++i) {
11251c0b2f7Stbbdev                 T f;
11351c0b2f7Stbbdev                 f.set_serial(i);
11451c0b2f7Stbbdev                 bool result = queue.try_push( f);
11551c0b2f7Stbbdev                 CHECK((result == (i < n)));
11651c0b2f7Stbbdev             }
11751c0b2f7Stbbdev 
11851c0b2f7Stbbdev             for (std::size_t i = 0; i <= n; ++i) {
11951c0b2f7Stbbdev                 T f;
12051c0b2f7Stbbdev                 bool result = queue.try_pop(f);
12151c0b2f7Stbbdev                 CHECK((result == (i < n)));
12251c0b2f7Stbbdev                 CHECK((result == 0 || f.get_serial() == i));
12351c0b2f7Stbbdev             }
12451c0b2f7Stbbdev         }
12551c0b2f7Stbbdev         CHECK(allocator_type::items_allocated == allocator_type::items_freed);
12651c0b2f7Stbbdev         CHECK(allocator_type::allocations == allocator_type::frees);
12751c0b2f7Stbbdev     }
12851c0b2f7Stbbdev }
12951c0b2f7Stbbdev 
13051c0b2f7Stbbdev void TestFullness() {
13151c0b2f7Stbbdev     TestFullQueue<ConcQWithCapacity<move_support_tests::Foo>, move_support_tests::Foo>();
13249e08aacStbbdev     TestFullQueue<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, move_support_tests::Foo>();
13351c0b2f7Stbbdev }
13451c0b2f7Stbbdev 
13551c0b2f7Stbbdev template<typename CQ>
13651c0b2f7Stbbdev void TestClear() {
13751c0b2f7Stbbdev     using allocator_type = decltype(std::declval<CQ>().get_allocator());
13851c0b2f7Stbbdev     allocator_type::init_counters();
13951c0b2f7Stbbdev     const std::size_t n = 5;
14051c0b2f7Stbbdev 
14151c0b2f7Stbbdev     CQ queue;
14251c0b2f7Stbbdev     const std::size_t q_capacity = 10;
14351c0b2f7Stbbdev     queue.set_capacity(q_capacity);
14451c0b2f7Stbbdev 
14551c0b2f7Stbbdev     for (std::size_t i = 0; i < n; ++i) {
14651c0b2f7Stbbdev         move_support_tests::Foo f;
14751c0b2f7Stbbdev         f.set_serial(i);
14851c0b2f7Stbbdev         queue.push(f);
14951c0b2f7Stbbdev     }
15051c0b2f7Stbbdev 
15151c0b2f7Stbbdev     CHECK(queue.size() == n);
15251c0b2f7Stbbdev 
15351c0b2f7Stbbdev     queue.clear();
15451c0b2f7Stbbdev     CHECK(queue.size()==0);
15551c0b2f7Stbbdev     for (std::size_t i = 0; i < n; ++i) {
15651c0b2f7Stbbdev         move_support_tests::Foo f;
15751c0b2f7Stbbdev         f.set_serial(i);
15851c0b2f7Stbbdev         queue.push( f);
15951c0b2f7Stbbdev     }
16051c0b2f7Stbbdev 
16151c0b2f7Stbbdev     CHECK(queue.size() == n);
16251c0b2f7Stbbdev     queue.clear();
16351c0b2f7Stbbdev     CHECK(queue.size() == 0);
16451c0b2f7Stbbdev 
16551c0b2f7Stbbdev     for (std::size_t i = 0; i < n; ++i) {
16651c0b2f7Stbbdev         move_support_tests::Foo f;
16751c0b2f7Stbbdev         f.set_serial(i);
16851c0b2f7Stbbdev         queue.push(f);
16951c0b2f7Stbbdev     }
17051c0b2f7Stbbdev 
17151c0b2f7Stbbdev     CHECK(queue.size()==n);
17251c0b2f7Stbbdev }
17351c0b2f7Stbbdev 
17451c0b2f7Stbbdev void TestClearWorks() {
17551c0b2f7Stbbdev     TestClear<ConcQWithCapacity<move_support_tests::Foo>>();
17649e08aacStbbdev     TestClear<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>();
17751c0b2f7Stbbdev }
17851c0b2f7Stbbdev 
17951c0b2f7Stbbdev template<typename Iterator1, typename Iterator2>
18051c0b2f7Stbbdev void TestIteratorAux( Iterator1 i, Iterator2 j, int size ) {
18151c0b2f7Stbbdev     Iterator1 old_i; // assigned at first iteration below
18251c0b2f7Stbbdev     for (std::size_t k = 0; k < (std::size_t)size; ++k) {
183b15aabb3Stbbdev         CHECK_FAST(i != j);
184b15aabb3Stbbdev         CHECK_FAST(!(i == j));
18551c0b2f7Stbbdev         // Test "->"
186b15aabb3Stbbdev         CHECK_FAST((k+1 == i->get_serial()));
18751c0b2f7Stbbdev         if (k & 1) {
18851c0b2f7Stbbdev             // Test post-increment
18951c0b2f7Stbbdev             move_support_tests::Foo f = *old_i++;
190b15aabb3Stbbdev             CHECK_FAST((k + 1 == f.get_serial()));
19151c0b2f7Stbbdev             // Test assignment
19251c0b2f7Stbbdev             i = old_i;
19351c0b2f7Stbbdev         } else {
19451c0b2f7Stbbdev             // Test pre-increment
19551c0b2f7Stbbdev             if (k < std::size_t(size - 1)) {
19651c0b2f7Stbbdev                 move_support_tests::Foo f = *++i;
197b15aabb3Stbbdev                 CHECK_FAST((k + 2 == f.get_serial()));
19851c0b2f7Stbbdev             } else ++i;
19951c0b2f7Stbbdev             // Test assignment
20051c0b2f7Stbbdev             old_i = i;
20151c0b2f7Stbbdev         }
20251c0b2f7Stbbdev     }
203b15aabb3Stbbdev     CHECK_FAST(!(i != j));
204b15aabb3Stbbdev     CHECK_FAST(i == j);
20551c0b2f7Stbbdev }
20651c0b2f7Stbbdev 
20751c0b2f7Stbbdev template<typename Iterator1, typename Iterator2>
20851c0b2f7Stbbdev void TestIteratorAssignment( Iterator2 j ) {
20951c0b2f7Stbbdev     Iterator1 i(j);
21051c0b2f7Stbbdev     CHECK(i == j);
21151c0b2f7Stbbdev     CHECK(!(i != j));
21251c0b2f7Stbbdev 
21351c0b2f7Stbbdev     Iterator1 k;
21451c0b2f7Stbbdev     k = j;
21551c0b2f7Stbbdev     CHECK(k == j);
21651c0b2f7Stbbdev     CHECK(!(k != j));
21751c0b2f7Stbbdev }
21851c0b2f7Stbbdev 
21951c0b2f7Stbbdev template<typename Iterator, typename T>
22051c0b2f7Stbbdev void TestIteratorTraits() {
22151c0b2f7Stbbdev     static_assert( std::is_same<typename Iterator::iterator_category, std::forward_iterator_tag>::value, "wrong iterator category");
22251c0b2f7Stbbdev 
22351c0b2f7Stbbdev     T x;
22451c0b2f7Stbbdev 
22551c0b2f7Stbbdev     typename Iterator::reference xr = x;
22651c0b2f7Stbbdev     typename Iterator::pointer xp = &x;
22751c0b2f7Stbbdev     CHECK((&xr == xp));
22851c0b2f7Stbbdev }
22951c0b2f7Stbbdev 
23051c0b2f7Stbbdev // Test the iterators for concurrent_queue
23151c0b2f7Stbbdev template <typename CQ>
23251c0b2f7Stbbdev void TestIterator() {
23351c0b2f7Stbbdev     CQ queue;
23451c0b2f7Stbbdev     const CQ& const_queue = queue;
23551c0b2f7Stbbdev     for (int j=0; j < 500; ++j) {
23651c0b2f7Stbbdev         TestIteratorAux( queue.unsafe_begin()      , queue.unsafe_end()      , j);
23751c0b2f7Stbbdev         TestIteratorAux( queue.unsafe_cbegin()      , queue.unsafe_cend()      , j);
23851c0b2f7Stbbdev         TestIteratorAux( const_queue.unsafe_begin(), const_queue.unsafe_end(), j);
23951c0b2f7Stbbdev         TestIteratorAux( const_queue.unsafe_begin(), queue.unsafe_end()      , j);
24051c0b2f7Stbbdev         TestIteratorAux( queue.unsafe_begin()      , const_queue.unsafe_end(), j);
24151c0b2f7Stbbdev         move_support_tests::Foo f;
24251c0b2f7Stbbdev         f.set_serial(j+1);
24351c0b2f7Stbbdev         queue.push(f);
24451c0b2f7Stbbdev     }
24551c0b2f7Stbbdev     TestIteratorAssignment<typename CQ::const_iterator>( const_queue.unsafe_begin());
24651c0b2f7Stbbdev     TestIteratorAssignment<typename CQ::const_iterator>( queue.unsafe_begin());
24751c0b2f7Stbbdev     TestIteratorAssignment<typename CQ::iterator>( queue.unsafe_begin());
24851c0b2f7Stbbdev     TestIteratorTraits<typename CQ::const_iterator, const move_support_tests::Foo>();
24951c0b2f7Stbbdev     TestIteratorTraits<typename CQ::iterator, move_support_tests::Foo>();
25051c0b2f7Stbbdev }
25151c0b2f7Stbbdev 
25251c0b2f7Stbbdev void TestQueueIteratorWorks() {
25349e08aacStbbdev     TestIterator<oneapi::tbb::concurrent_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>();
25449e08aacStbbdev     TestIterator<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>();
25551c0b2f7Stbbdev }
25651c0b2f7Stbbdev 
25749e08aacStbbdev // Define wrapper classes to test oneapi::tbb::concurrent_queue<T>
25849e08aacStbbdev template<typename T, typename A = oneapi::tbb::cache_aligned_allocator<T>>
25949e08aacStbbdev class ConcQWithSizeWrapper : public oneapi::tbb::concurrent_queue<T, A> {
26051c0b2f7Stbbdev public:
26151c0b2f7Stbbdev     ConcQWithSizeWrapper() {}
26249e08aacStbbdev     ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q ) : oneapi::tbb::concurrent_queue<T, A>(q) {}
26349e08aacStbbdev     ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q, const A& a ) : oneapi::tbb::concurrent_queue<T, A>(q, a) {}
26449e08aacStbbdev     ConcQWithSizeWrapper( const A& a ) : oneapi::tbb::concurrent_queue<T, A>( a ) {}
26551c0b2f7Stbbdev 
26649e08aacStbbdev     ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q ) : oneapi::tbb::concurrent_queue<T>(std::move(q)) {}
26751c0b2f7Stbbdev     ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q, const A& a )
26849e08aacStbbdev         : oneapi::tbb::concurrent_queue<T, A>(std::move(q), a) { }
26951c0b2f7Stbbdev 
27051c0b2f7Stbbdev     template<typename InputIterator>
27151c0b2f7Stbbdev     ConcQWithSizeWrapper( InputIterator begin, InputIterator end, const A& a = A() )
27249e08aacStbbdev         : oneapi::tbb::concurrent_queue<T, A>(begin, end, a) {}
27349e08aacStbbdev     typename oneapi::tbb::concurrent_queue<T, A>::size_type size() const { return this->unsafe_size(); }
27451c0b2f7Stbbdev };
27551c0b2f7Stbbdev 
27651c0b2f7Stbbdev enum state_type {
27751c0b2f7Stbbdev     LIVE = 0x1234,
27851c0b2f7Stbbdev     DEAD = 0xDEAD
27951c0b2f7Stbbdev };
28051c0b2f7Stbbdev 
28151c0b2f7Stbbdev class Bar {
28251c0b2f7Stbbdev     state_type state;
28351c0b2f7Stbbdev public:
28451c0b2f7Stbbdev     static std::size_t construction_num, destruction_num;
28551c0b2f7Stbbdev     std::ptrdiff_t my_id;
28651c0b2f7Stbbdev     Bar() : state(LIVE), my_id(-1)
28751c0b2f7Stbbdev     {}
28851c0b2f7Stbbdev 
28951c0b2f7Stbbdev     Bar( std::size_t _i ) : state(LIVE), my_id(_i) { construction_num++; }
29051c0b2f7Stbbdev 
29151c0b2f7Stbbdev     Bar( const Bar& a_bar ) : state(LIVE) {
292b15aabb3Stbbdev         CHECK_FAST(a_bar.state == LIVE);
29351c0b2f7Stbbdev         my_id = a_bar.my_id;
29451c0b2f7Stbbdev         construction_num++;
29551c0b2f7Stbbdev     }
29651c0b2f7Stbbdev 
29751c0b2f7Stbbdev     ~Bar() {
298b15aabb3Stbbdev         CHECK_FAST(state == LIVE);
29951c0b2f7Stbbdev         state = DEAD;
30051c0b2f7Stbbdev         my_id = DEAD;
30151c0b2f7Stbbdev         destruction_num++;
30251c0b2f7Stbbdev     }
30351c0b2f7Stbbdev 
30451c0b2f7Stbbdev     void operator=( const Bar& a_bar ) {
305b15aabb3Stbbdev         CHECK_FAST(a_bar.state == LIVE);
306b15aabb3Stbbdev         CHECK_FAST(state == LIVE);
30751c0b2f7Stbbdev         my_id = a_bar.my_id;
30851c0b2f7Stbbdev     }
30951c0b2f7Stbbdev     friend bool operator==( const Bar& bar1, const Bar& bar2 ) ;
31051c0b2f7Stbbdev };
31151c0b2f7Stbbdev 
31251c0b2f7Stbbdev std::size_t Bar::construction_num = 0;
31351c0b2f7Stbbdev std::size_t Bar::destruction_num = 0;
31451c0b2f7Stbbdev 
31551c0b2f7Stbbdev bool operator==( const Bar& bar1, const Bar& bar2 ) {
316b15aabb3Stbbdev     CHECK_FAST(bar1.state == LIVE);
317b15aabb3Stbbdev     CHECK_FAST(bar2.state == LIVE);
31851c0b2f7Stbbdev     return bar1.my_id == bar2.my_id;
31951c0b2f7Stbbdev }
32051c0b2f7Stbbdev 
32151c0b2f7Stbbdev class BarIterator {
32251c0b2f7Stbbdev     Bar* bar_ptr;
32351c0b2f7Stbbdev     BarIterator(Bar* bp_) : bar_ptr(bp_) {}
32451c0b2f7Stbbdev public:
32551c0b2f7Stbbdev     Bar& operator*() const {
32651c0b2f7Stbbdev         return *bar_ptr;
32751c0b2f7Stbbdev     }
32851c0b2f7Stbbdev     BarIterator& operator++() {
32951c0b2f7Stbbdev         ++bar_ptr;
33051c0b2f7Stbbdev         return *this;
33151c0b2f7Stbbdev     }
33251c0b2f7Stbbdev     Bar* operator++(int) {
33351c0b2f7Stbbdev         Bar* result = &operator*();
33451c0b2f7Stbbdev         operator++();
33551c0b2f7Stbbdev         return result;
33651c0b2f7Stbbdev     }
33751c0b2f7Stbbdev     friend bool operator==(const BarIterator& bia, const BarIterator& bib) ;
33851c0b2f7Stbbdev     friend bool operator!=(const BarIterator& bia, const BarIterator& bib) ;
33951c0b2f7Stbbdev     template<typename CQ, typename T, typename TIter, typename CQ_EX, typename T_EX>
34051c0b2f7Stbbdev     friend void TestConstructors ();
34151c0b2f7Stbbdev } ;
34251c0b2f7Stbbdev 
34351c0b2f7Stbbdev bool operator==(const BarIterator& bia, const BarIterator& bib) {
34451c0b2f7Stbbdev     return bia.bar_ptr==bib.bar_ptr;
34551c0b2f7Stbbdev }
34651c0b2f7Stbbdev 
34751c0b2f7Stbbdev bool operator!=(const BarIterator& bia, const BarIterator& bib) {
34851c0b2f7Stbbdev     return bia.bar_ptr!=bib.bar_ptr;
34951c0b2f7Stbbdev }
35051c0b2f7Stbbdev 
35151c0b2f7Stbbdev 
35251c0b2f7Stbbdev class Bar_exception : public std::bad_alloc {
35351c0b2f7Stbbdev public:
35451c0b2f7Stbbdev     virtual const char *what() const noexcept override { return "making the entry invalid"; }
35551c0b2f7Stbbdev     virtual ~Bar_exception() noexcept {}
35651c0b2f7Stbbdev };
35751c0b2f7Stbbdev 
35851c0b2f7Stbbdev class BarEx {
35951c0b2f7Stbbdev     static int count;
36051c0b2f7Stbbdev public:
36151c0b2f7Stbbdev     state_type state;
36251c0b2f7Stbbdev     typedef enum {
36351c0b2f7Stbbdev         PREPARATION,
36451c0b2f7Stbbdev         COPY_CONSTRUCT
36551c0b2f7Stbbdev     } mode_type;
36651c0b2f7Stbbdev     static mode_type mode;
36751c0b2f7Stbbdev     std::ptrdiff_t my_id;
36851c0b2f7Stbbdev     std::ptrdiff_t my_tilda_id;
36951c0b2f7Stbbdev 
37051c0b2f7Stbbdev     static int button;
37151c0b2f7Stbbdev 
37251c0b2f7Stbbdev     BarEx() : state(LIVE), my_id(-1), my_tilda_id(-1)
37351c0b2f7Stbbdev     {}
37451c0b2f7Stbbdev 
37551c0b2f7Stbbdev     BarEx(std::size_t _i) : state(LIVE), my_id(_i), my_tilda_id(my_id^(-1))
37651c0b2f7Stbbdev     {}
37751c0b2f7Stbbdev 
37851c0b2f7Stbbdev     BarEx( const BarEx& a_bar ) : state(LIVE) {
379b15aabb3Stbbdev         CHECK_FAST(a_bar.state == LIVE);
38051c0b2f7Stbbdev         my_id = a_bar.my_id;
38151c0b2f7Stbbdev         if (mode == PREPARATION)
38251c0b2f7Stbbdev             if (!(++count % 100)) {
38351c0b2f7Stbbdev                 TBB_TEST_THROW(Bar_exception());
38451c0b2f7Stbbdev             }
38551c0b2f7Stbbdev         my_tilda_id = a_bar.my_tilda_id;
38651c0b2f7Stbbdev     }
38751c0b2f7Stbbdev 
38851c0b2f7Stbbdev     ~BarEx() {
389b15aabb3Stbbdev         CHECK_FAST(state == LIVE);
39051c0b2f7Stbbdev         state = DEAD;
39151c0b2f7Stbbdev         my_id = DEAD;
39251c0b2f7Stbbdev     }
39351c0b2f7Stbbdev     static void set_mode( mode_type m ) { mode = m; }
39451c0b2f7Stbbdev 
39551c0b2f7Stbbdev     void operator=( const BarEx& a_bar ) {
396b15aabb3Stbbdev         CHECK_FAST(a_bar.state == LIVE);
397b15aabb3Stbbdev         CHECK_FAST(state == LIVE);
39851c0b2f7Stbbdev         my_id = a_bar.my_id;
39951c0b2f7Stbbdev         my_tilda_id = a_bar.my_tilda_id;
40051c0b2f7Stbbdev     }
40151c0b2f7Stbbdev 
40251c0b2f7Stbbdev     friend bool operator==(const BarEx& bar1, const BarEx& bar2 ) ;
40351c0b2f7Stbbdev };
40451c0b2f7Stbbdev 
40551c0b2f7Stbbdev int BarEx::count = 0;
40651c0b2f7Stbbdev BarEx::mode_type BarEx::mode = BarEx::PREPARATION;
40751c0b2f7Stbbdev 
40851c0b2f7Stbbdev bool operator==(const BarEx& bar1, const BarEx& bar2) {
409b15aabb3Stbbdev     CHECK_FAST(bar1.state == LIVE);
410b15aabb3Stbbdev     CHECK_FAST(bar2.state == LIVE);
411b15aabb3Stbbdev     CHECK_FAST((bar1.my_id ^ bar1.my_tilda_id) == -1);
412b15aabb3Stbbdev     CHECK_FAST((bar2.my_id ^ bar2.my_tilda_id) == -1);
41351c0b2f7Stbbdev     return bar1.my_id == bar2.my_id && bar1.my_tilda_id == bar2.my_tilda_id;
41451c0b2f7Stbbdev }
41551c0b2f7Stbbdev 
41651c0b2f7Stbbdev template<typename CQ, typename T, typename TIter, typename CQ_EX, typename T_EX>
41751c0b2f7Stbbdev void TestConstructors () {
41851c0b2f7Stbbdev     CQ src_queue;
41951c0b2f7Stbbdev     typename CQ::const_iterator dqb;
42051c0b2f7Stbbdev     typename CQ::const_iterator dqe;
42151c0b2f7Stbbdev     typename CQ::const_iterator iter;
422b15aabb3Stbbdev     using size_type = typename CQ::size_type;
42351c0b2f7Stbbdev 
424b15aabb3Stbbdev     for (size_type size = 0; size < 1001; ++size) {
425b15aabb3Stbbdev         for (size_type i = 0; i < size; ++i)
42651c0b2f7Stbbdev             src_queue.push(T(i + (i ^ size)));
42751c0b2f7Stbbdev         typename CQ::const_iterator sqb( src_queue.unsafe_begin());
42851c0b2f7Stbbdev         typename CQ::const_iterator sqe( src_queue.unsafe_end()  );
42951c0b2f7Stbbdev 
43051c0b2f7Stbbdev         CQ dst_queue(sqb, sqe);
43151c0b2f7Stbbdev         CQ copy_with_alloc(src_queue, typename CQ::allocator_type());
43251c0b2f7Stbbdev 
433b15aabb3Stbbdev         CHECK_FAST_MESSAGE(src_queue.size() == dst_queue.size(), "different size");
434b15aabb3Stbbdev         CHECK_FAST_MESSAGE(src_queue.size() == copy_with_alloc.size(), "different size");
43551c0b2f7Stbbdev 
43651c0b2f7Stbbdev         src_queue.clear();
43751c0b2f7Stbbdev     }
43851c0b2f7Stbbdev 
43951c0b2f7Stbbdev     T bar_array[1001];
440b15aabb3Stbbdev     for (size_type size=0; size < 1001; ++size) {
441b15aabb3Stbbdev         for (size_type i=0; i < size; ++i) {
44251c0b2f7Stbbdev             bar_array[i] = T(i+(i^size));
44351c0b2f7Stbbdev         }
44451c0b2f7Stbbdev 
44551c0b2f7Stbbdev         const TIter sab(bar_array + 0);
44651c0b2f7Stbbdev         const TIter sae(bar_array + size);
44751c0b2f7Stbbdev 
44851c0b2f7Stbbdev         CQ dst_queue2(sab, sae);
44951c0b2f7Stbbdev 
450b15aabb3Stbbdev         CHECK_FAST(size == dst_queue2.size());
451b15aabb3Stbbdev         CHECK_FAST(sab == TIter(bar_array+0));
452b15aabb3Stbbdev         CHECK_FAST(sae == TIter(bar_array+size));
45351c0b2f7Stbbdev 
45451c0b2f7Stbbdev         dqb = dst_queue2.unsafe_begin();
45551c0b2f7Stbbdev         dqe = dst_queue2.unsafe_end();
456b15aabb3Stbbdev         auto res = std::mismatch(dqb, dqe, bar_array);
457b15aabb3Stbbdev         CHECK_FAST_MESSAGE(res.first == dqe,  "unexpected element");
458b15aabb3Stbbdev         CHECK_FAST_MESSAGE(res.second == bar_array + size, "different size?");
45951c0b2f7Stbbdev     }
46051c0b2f7Stbbdev 
46151c0b2f7Stbbdev     src_queue.clear();
46251c0b2f7Stbbdev 
46351c0b2f7Stbbdev     CQ dst_queue3(src_queue);
46451c0b2f7Stbbdev     CHECK(src_queue.size() == dst_queue3.size());
46551c0b2f7Stbbdev     CHECK(0 == dst_queue3.size());
46651c0b2f7Stbbdev 
46751c0b2f7Stbbdev     int k = 0;
468b15aabb3Stbbdev     for (size_type i = 0; i < 1001; ++i) {
46951c0b2f7Stbbdev         T tmp_bar;
47051c0b2f7Stbbdev         src_queue.push(T(++k));
47151c0b2f7Stbbdev         src_queue.push(T(++k));
47251c0b2f7Stbbdev         src_queue.try_pop(tmp_bar);
47351c0b2f7Stbbdev 
47451c0b2f7Stbbdev         CQ dst_queue4( src_queue);
47551c0b2f7Stbbdev 
476b15aabb3Stbbdev         CHECK_FAST(src_queue.size() == dst_queue4.size());
47751c0b2f7Stbbdev 
47851c0b2f7Stbbdev         dqb = dst_queue4.unsafe_begin();
47951c0b2f7Stbbdev         dqe = dst_queue4.unsafe_end();
48051c0b2f7Stbbdev         iter = src_queue.unsafe_begin();
481b15aabb3Stbbdev         auto res = std::mismatch(dqb, dqe, iter);
482b15aabb3Stbbdev         CHECK_FAST_MESSAGE(res.first == dqe, "unexpected element");
483b15aabb3Stbbdev         CHECK_FAST_MESSAGE(res.second == src_queue.unsafe_end(), "different size?");
48451c0b2f7Stbbdev     }
48551c0b2f7Stbbdev 
48651c0b2f7Stbbdev     CQ dst_queue5(src_queue);
48751c0b2f7Stbbdev 
48851c0b2f7Stbbdev     CHECK(src_queue.size() == dst_queue5.size());
48951c0b2f7Stbbdev     dqb = dst_queue5.unsafe_begin();
49051c0b2f7Stbbdev     dqe = dst_queue5.unsafe_end();
49151c0b2f7Stbbdev     iter = src_queue.unsafe_begin();
492b15aabb3Stbbdev     REQUIRE_MESSAGE(std::equal(dqb, dqe, iter), "unexpected element");
49351c0b2f7Stbbdev 
494b15aabb3Stbbdev     for (size_type i=0; i<100; ++i) {
49551c0b2f7Stbbdev         T tmp_bar;
49651c0b2f7Stbbdev         src_queue.push(T(i + 1000));
49751c0b2f7Stbbdev         src_queue.push(T(i + 1000));
49851c0b2f7Stbbdev         src_queue.try_pop(tmp_bar);
49951c0b2f7Stbbdev 
50051c0b2f7Stbbdev         dst_queue5.push(T(i + 1000));
50151c0b2f7Stbbdev         dst_queue5.push(T(i + 1000));
50251c0b2f7Stbbdev         dst_queue5.try_pop(tmp_bar);
50351c0b2f7Stbbdev     }
50451c0b2f7Stbbdev 
50551c0b2f7Stbbdev     CHECK(src_queue.size() == dst_queue5.size());
50651c0b2f7Stbbdev     dqb = dst_queue5.unsafe_begin();
50751c0b2f7Stbbdev     dqe = dst_queue5.unsafe_end();
50851c0b2f7Stbbdev     iter = src_queue.unsafe_begin();
509b15aabb3Stbbdev     auto res = std::mismatch(dqb, dqe, iter);
510b15aabb3Stbbdev     REQUIRE_MESSAGE(res.first == dqe, "unexpected element");
511b15aabb3Stbbdev     REQUIRE_MESSAGE(res.second == src_queue.unsafe_end(), "different size?");
51251c0b2f7Stbbdev 
51351c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
51451c0b2f7Stbbdev     k = 0;
51551c0b2f7Stbbdev     typename CQ_EX::size_type n_elements = 0;
51651c0b2f7Stbbdev     CQ_EX src_queue_ex;
517b15aabb3Stbbdev     for (size_type size = 0; size < 1001; ++size) {
51851c0b2f7Stbbdev         T_EX tmp_bar_ex;
51951c0b2f7Stbbdev         typename CQ_EX::size_type n_successful_pushes = 0;
52051c0b2f7Stbbdev         T_EX::set_mode(T_EX::PREPARATION);
52151c0b2f7Stbbdev         try {
52251c0b2f7Stbbdev             src_queue_ex.push(T_EX(k + (k ^ size)));
52351c0b2f7Stbbdev             ++n_successful_pushes;
52451c0b2f7Stbbdev         } catch (...) {
52551c0b2f7Stbbdev         }
52651c0b2f7Stbbdev         ++k;
52751c0b2f7Stbbdev         try {
52851c0b2f7Stbbdev             src_queue_ex.push(T_EX(k + (k ^ size)));
52951c0b2f7Stbbdev             ++n_successful_pushes;
53051c0b2f7Stbbdev         } catch (...) {
53151c0b2f7Stbbdev         }
53251c0b2f7Stbbdev         ++k;
53351c0b2f7Stbbdev         src_queue_ex.try_pop(tmp_bar_ex);
53451c0b2f7Stbbdev         n_elements += (n_successful_pushes - 1);
535b15aabb3Stbbdev         CHECK_FAST(src_queue_ex.size() == n_elements);
53651c0b2f7Stbbdev 
53751c0b2f7Stbbdev         T_EX::set_mode(T_EX::COPY_CONSTRUCT);
53851c0b2f7Stbbdev         CQ_EX dst_queue_ex(src_queue_ex);
53951c0b2f7Stbbdev 
540b15aabb3Stbbdev         CHECK_FAST(src_queue_ex.size() == dst_queue_ex.size());
54151c0b2f7Stbbdev 
54251c0b2f7Stbbdev         typename CQ_EX::const_iterator dqb_ex = dst_queue_ex.unsafe_begin();
54351c0b2f7Stbbdev         typename CQ_EX::const_iterator dqe_ex = dst_queue_ex.unsafe_end();
54451c0b2f7Stbbdev         typename CQ_EX::const_iterator iter_ex = src_queue_ex.unsafe_begin();
54551c0b2f7Stbbdev 
546b15aabb3Stbbdev         auto res2 = std::mismatch(dqb_ex, dqe_ex, iter_ex);
547b15aabb3Stbbdev         CHECK_FAST_MESSAGE(res2.first == dqe_ex, "unexpected element");
548b15aabb3Stbbdev         CHECK_FAST_MESSAGE(res2.second == src_queue_ex.unsafe_end(), "different size?");
54951c0b2f7Stbbdev     }
55051c0b2f7Stbbdev #endif
55151c0b2f7Stbbdev     src_queue.clear();
55251c0b2f7Stbbdev 
553b15aabb3Stbbdev     for (size_type size = 0; size < 1001; ++size) {
554b15aabb3Stbbdev         for (size_type i = 0; i < size; ++i) {
55551c0b2f7Stbbdev             src_queue.push(T(i + (i ^ size)));
55651c0b2f7Stbbdev         }
55751c0b2f7Stbbdev         std::vector<const T*> locations(size);
55851c0b2f7Stbbdev         typename CQ::const_iterator qit = src_queue.unsafe_begin();
559b15aabb3Stbbdev         for (size_type i = 0; i < size; ++i, ++qit) {
56051c0b2f7Stbbdev             locations[i] = &(*qit);
56151c0b2f7Stbbdev         }
56251c0b2f7Stbbdev 
563b15aabb3Stbbdev         size_type size_of_queue = src_queue.size();
56451c0b2f7Stbbdev         CQ dst_queue(std::move(src_queue));
56551c0b2f7Stbbdev 
566b15aabb3Stbbdev         CHECK_FAST_MESSAGE((src_queue.empty() && src_queue.size() == 0), "not working move constructor?");
567b15aabb3Stbbdev         CHECK_FAST_MESSAGE((size == size_of_queue && size_of_queue == dst_queue.size()), "not working move constructor?");
56851c0b2f7Stbbdev 
569b15aabb3Stbbdev         CHECK_FAST_MESSAGE(
570b15aabb3Stbbdev             std::equal(locations.begin(), locations.end(), dst_queue.unsafe_begin(), [](const T* t1, const T& r2) { return t1 == &r2; }),
571b15aabb3Stbbdev             "there was data movement during move constructor"
572b15aabb3Stbbdev         );
57351c0b2f7Stbbdev 
574b15aabb3Stbbdev         for (size_type i = 0; i < size; ++i) {
57551c0b2f7Stbbdev             T test(i + (i ^ size));
57651c0b2f7Stbbdev             T popped;
57751c0b2f7Stbbdev             bool pop_result = dst_queue.try_pop( popped);
57851c0b2f7Stbbdev 
579b15aabb3Stbbdev             CHECK_FAST(pop_result);
580b15aabb3Stbbdev             CHECK_FAST(test == popped);
58151c0b2f7Stbbdev         }
58251c0b2f7Stbbdev     }
58351c0b2f7Stbbdev }
58451c0b2f7Stbbdev 
58551c0b2f7Stbbdev void TestQueueConstructors() {
58651c0b2f7Stbbdev     TestConstructors<ConcQWithSizeWrapper<Bar>, Bar, BarIterator, ConcQWithSizeWrapper<BarEx>, BarEx>();
58749e08aacStbbdev     TestConstructors<oneapi::tbb::concurrent_bounded_queue<Bar>, Bar, BarIterator, oneapi::tbb::concurrent_bounded_queue<BarEx>, BarEx>();
58851c0b2f7Stbbdev }
58951c0b2f7Stbbdev 
59051c0b2f7Stbbdev template<typename T>
59151c0b2f7Stbbdev struct TestNegativeQueueBody {
59249e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<T>& queue;
59351c0b2f7Stbbdev     const std::size_t nthread;
59449e08aacStbbdev     TestNegativeQueueBody( oneapi::tbb::concurrent_bounded_queue<T>& q, std::size_t n ) : queue(q), nthread(n) {}
59551c0b2f7Stbbdev     void operator()( std::size_t k ) const {
59651c0b2f7Stbbdev         if (k == 0) {
59751c0b2f7Stbbdev             int number_of_pops = int(nthread) - 1;
59851c0b2f7Stbbdev             // Wait for all pops to pend.
59951c0b2f7Stbbdev             while (int(queue.size())> -number_of_pops) {
600b15aabb3Stbbdev                 utils::yield();
60151c0b2f7Stbbdev             }
60251c0b2f7Stbbdev 
60351c0b2f7Stbbdev             for (int i = 0; ; ++i) {
60451c0b2f7Stbbdev                 CHECK(queue.size() == std::size_t(i - number_of_pops));
60551c0b2f7Stbbdev                 CHECK((queue.empty() == (queue.size() <= 0)));
60651c0b2f7Stbbdev                 if (i == number_of_pops) break;
60751c0b2f7Stbbdev                 // Satisfy another pop
60851c0b2f7Stbbdev                 queue.push(T());
60951c0b2f7Stbbdev             }
61051c0b2f7Stbbdev         } else {
61151c0b2f7Stbbdev             // Pop item from queue
61251c0b2f7Stbbdev             T item;
61351c0b2f7Stbbdev             queue.pop(item);
61451c0b2f7Stbbdev         }
61551c0b2f7Stbbdev     }
61651c0b2f7Stbbdev };
61751c0b2f7Stbbdev 
61851c0b2f7Stbbdev //! Test a queue with a negative size.
61951c0b2f7Stbbdev template<typename T>
62051c0b2f7Stbbdev void TestNegativeQueue( std::size_t nthread ) {
62149e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<T> queue;
62251c0b2f7Stbbdev     utils::NativeParallelFor( nthread, TestNegativeQueueBody<T>(queue,nthread));
62351c0b2f7Stbbdev }
62451c0b2f7Stbbdev 
62551c0b2f7Stbbdev template<typename T>
62649e08aacStbbdev class ConcQPushPopWrapper : public oneapi::tbb::concurrent_queue<T, test_allocator<T>> {
62751c0b2f7Stbbdev public:
62851c0b2f7Stbbdev     ConcQPushPopWrapper() : my_capacity(std::size_t(-1) / (sizeof(void*) + sizeof(T)))
62951c0b2f7Stbbdev     {}
63051c0b2f7Stbbdev 
63151c0b2f7Stbbdev     std::size_t size() const { return this->unsafe_size(); }
63251c0b2f7Stbbdev     void set_capacity( const ptrdiff_t n ) { my_capacity = n; }
63351c0b2f7Stbbdev     bool try_push( const T& source ) { return this->push( source); }
63449e08aacStbbdev     bool try_pop( T& dest ) { return this->oneapi::tbb::concurrent_queue<T, test_allocator<T>>::try_pop(dest); }
63551c0b2f7Stbbdev     std::size_t my_capacity;
63651c0b2f7Stbbdev };
63751c0b2f7Stbbdev 
63851c0b2f7Stbbdev template<typename CQ, typename T>
63951c0b2f7Stbbdev struct Body {
64051c0b2f7Stbbdev     CQ* queue;
64151c0b2f7Stbbdev     const std::size_t nthread;
64251c0b2f7Stbbdev     Body( std::size_t nthread_ ) : nthread(nthread_) {}
64351c0b2f7Stbbdev     void operator()( std::size_t thread_id ) const {
64451c0b2f7Stbbdev         long pop_kind[3] = {0, 0, 0};
64551c0b2f7Stbbdev         std::size_t serial[MAXTHREAD + 1];
64651c0b2f7Stbbdev         memset(serial, 0, nthread * sizeof(std::size_t));
64751c0b2f7Stbbdev         CHECK(thread_id < nthread);
64851c0b2f7Stbbdev 
64951c0b2f7Stbbdev         long sum = 0;
65051c0b2f7Stbbdev         for (std::size_t j = 0; j < M; ++j) {
65151c0b2f7Stbbdev             T f;
65251c0b2f7Stbbdev             f.set_thread_id(move_support_tests::serial_dead_state);
65351c0b2f7Stbbdev             f.set_serial(move_support_tests::serial_dead_state);
65451c0b2f7Stbbdev             bool prepopped = false;
65551c0b2f7Stbbdev             if (j & 1) {
65651c0b2f7Stbbdev                 prepopped = queue->try_pop(f);
65751c0b2f7Stbbdev                 ++pop_kind[prepopped];
65851c0b2f7Stbbdev             }
65951c0b2f7Stbbdev             T g;
66051c0b2f7Stbbdev             g.set_thread_id(thread_id);
66151c0b2f7Stbbdev             g.set_serial(j + 1);
66251c0b2f7Stbbdev             push(*queue, g, j);
66351c0b2f7Stbbdev             if (!prepopped) {
664b15aabb3Stbbdev                 while(!(queue)->try_pop(f)) utils::yield();
66551c0b2f7Stbbdev                 ++pop_kind[2];
66651c0b2f7Stbbdev             }
667b15aabb3Stbbdev             CHECK_FAST(f.get_thread_id() <= nthread);
668b15aabb3Stbbdev             CHECK_FAST_MESSAGE((f.get_thread_id() == nthread || serial[f.get_thread_id()] < f.get_serial()), "partial order violation");
66951c0b2f7Stbbdev             serial[f.get_thread_id()] = f.get_serial();
67051c0b2f7Stbbdev             sum += int(f.get_serial() - 1);
67151c0b2f7Stbbdev         }
67251c0b2f7Stbbdev         Sum[thread_id] = sum;
67351c0b2f7Stbbdev         for (std::size_t k = 0; k < 3; ++k)
67451c0b2f7Stbbdev             PopKind[k] += pop_kind[k];
67551c0b2f7Stbbdev     }
67651c0b2f7Stbbdev };
67751c0b2f7Stbbdev 
67851c0b2f7Stbbdev template<typename CQ, typename T>
679b15aabb3Stbbdev void TestPushPop(typename CQ::size_type prefill, std::ptrdiff_t capacity, std::size_t nthread ) {
68051c0b2f7Stbbdev     using allocator_type = decltype(std::declval<CQ>().get_allocator());
68151c0b2f7Stbbdev     CHECK(nthread> 0);
68251c0b2f7Stbbdev     std::ptrdiff_t signed_prefill = std::ptrdiff_t(prefill);
68351c0b2f7Stbbdev 
68451c0b2f7Stbbdev     if (signed_prefill + 1>= capacity) {
68551c0b2f7Stbbdev         return;
68651c0b2f7Stbbdev     }
68751c0b2f7Stbbdev 
68851c0b2f7Stbbdev     bool success = false;
68951c0b2f7Stbbdev     for (std::size_t k=0; k < 3; ++k) {
69051c0b2f7Stbbdev         PopKind[k] = 0;
69151c0b2f7Stbbdev     }
69251c0b2f7Stbbdev 
69351c0b2f7Stbbdev     for (std::size_t trial = 0; !success; ++trial) {
69451c0b2f7Stbbdev         allocator_type::init_counters();
69551c0b2f7Stbbdev         Body<CQ,T> body(nthread);
69651c0b2f7Stbbdev         CQ queue;
69751c0b2f7Stbbdev         queue.set_capacity(capacity);
69851c0b2f7Stbbdev         body.queue = &queue;
699b15aabb3Stbbdev         for (typename CQ::size_type i = 0; i < prefill; ++i) {
70051c0b2f7Stbbdev             T f;
70151c0b2f7Stbbdev             f.set_thread_id(nthread);
70251c0b2f7Stbbdev             f.set_serial(1 + i);
70351c0b2f7Stbbdev             push(queue, f, i);
704b15aabb3Stbbdev             CHECK_FAST(queue.size() == i + 1);
705b15aabb3Stbbdev             CHECK_FAST(!queue.empty());
70651c0b2f7Stbbdev         }
70751c0b2f7Stbbdev 
70851c0b2f7Stbbdev         utils::NativeParallelFor( nthread, body);
70951c0b2f7Stbbdev 
71051c0b2f7Stbbdev         int sum = 0;
71151c0b2f7Stbbdev         for (std::size_t k = 0; k < nthread; ++k) {
71251c0b2f7Stbbdev             sum += Sum[k];
71351c0b2f7Stbbdev         }
71451c0b2f7Stbbdev 
71551c0b2f7Stbbdev         int expected = int( nthread * ((M - 1) * M / 2) + ((prefill - 1) * prefill) / 2);
71651c0b2f7Stbbdev         for (int i = int(prefill); --i>=0;) {
717b15aabb3Stbbdev             CHECK_FAST(!queue.empty());
71851c0b2f7Stbbdev             T f;
71951c0b2f7Stbbdev             bool result = queue.try_pop(f);
720b15aabb3Stbbdev             CHECK_FAST(result);
721b15aabb3Stbbdev             CHECK_FAST(int(queue.size()) == i);
72251c0b2f7Stbbdev             sum += int(f.get_serial()) - 1;
72351c0b2f7Stbbdev         }
72451c0b2f7Stbbdev         REQUIRE_MESSAGE(queue.empty(), "The queue should be empty");
72551c0b2f7Stbbdev         REQUIRE_MESSAGE(queue.size() == 0, "The queue should have zero size");
72651c0b2f7Stbbdev         if (sum != expected) {
72751c0b2f7Stbbdev             REPORT("sum=%d expected=%d\n",sum,expected);
72851c0b2f7Stbbdev         }
72951c0b2f7Stbbdev 
73051c0b2f7Stbbdev         success = true;
73151c0b2f7Stbbdev         if (nthread> 1 && prefill == 0) {
73251c0b2f7Stbbdev             // Check that pop_if_present got sufficient exercise
73351c0b2f7Stbbdev             for (std::size_t k = 0; k < 2; ++k) {
73451c0b2f7Stbbdev                 const int min_requirement = 100;
73551c0b2f7Stbbdev                 const int max_trial = 20;
73651c0b2f7Stbbdev 
73751c0b2f7Stbbdev                 if (PopKind[k] < min_requirement) {
73851c0b2f7Stbbdev                     if (trial>= max_trial) {
73951c0b2f7Stbbdev                         REPORT("Warning: %d threads had only %ld pop_if_present operations %s after %d trials (expected at least %d). "
74051c0b2f7Stbbdev                             "This problem may merely be unlucky scheduling. "
74151c0b2f7Stbbdev                             "Investigate only if it happens repeatedly.\n",
74251c0b2f7Stbbdev                             nthread, long(PopKind[k]), k==0?"failed":"succeeded", max_trial, min_requirement);
74351c0b2f7Stbbdev                     } else {
74451c0b2f7Stbbdev                         success = false;
74551c0b2f7Stbbdev                     }
74651c0b2f7Stbbdev                }
74751c0b2f7Stbbdev             }
74851c0b2f7Stbbdev         }
74951c0b2f7Stbbdev     }
75051c0b2f7Stbbdev }
75151c0b2f7Stbbdev 
75251c0b2f7Stbbdev void TestConcurrentPushPop() {
75351c0b2f7Stbbdev     for (std::size_t nthread = MinThread; nthread <= MaxThread; ++nthread) {
75451c0b2f7Stbbdev         INFO(" Testing with "<< nthread << " thread(s)");
75551c0b2f7Stbbdev         TestNegativeQueue<move_support_tests::Foo>(nthread);
75651c0b2f7Stbbdev         for (std::size_t prefill=0; prefill < 64; prefill += (1 + prefill / 3)) {
75751c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(-1), nthread);
75851c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(1), nthread);
75951c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(2), nthread);
76051c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(10), nthread);
76151c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(100), nthread);
76251c0b2f7Stbbdev         }
76351c0b2f7Stbbdev         for (std::size_t prefill = 0; prefill < 64; prefill += (1 + prefill / 3) ) {
76449e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
76551c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(-1), nthread);
76649e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
76751c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(1), nthread);
76849e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
76951c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(2), nthread);
77049e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
77151c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(10), nthread);
77249e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
77351c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(100), nthread);
77451c0b2f7Stbbdev         }
77551c0b2f7Stbbdev     }
77651c0b2f7Stbbdev }
77751c0b2f7Stbbdev 
77851c0b2f7Stbbdev class Foo_exception : public std::bad_alloc {
77951c0b2f7Stbbdev public:
78051c0b2f7Stbbdev     virtual const char *what() const throw() override { return "out of Foo limit"; }
78151c0b2f7Stbbdev     virtual ~Foo_exception() throw() {}
78251c0b2f7Stbbdev };
78351c0b2f7Stbbdev 
78451c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
78551c0b2f7Stbbdev static std::atomic<long> FooExConstructed;
78651c0b2f7Stbbdev static std::atomic<long> FooExDestroyed;
78751c0b2f7Stbbdev static std::atomic<long> serial_source;
78851c0b2f7Stbbdev static long MaxFooCount = 0;
78951c0b2f7Stbbdev static const long Threshold = 400;
79051c0b2f7Stbbdev 
79151c0b2f7Stbbdev class FooEx {
79251c0b2f7Stbbdev     state_type state;
79351c0b2f7Stbbdev public:
79451c0b2f7Stbbdev     int serial;
79551c0b2f7Stbbdev     FooEx() : state(LIVE) {
79651c0b2f7Stbbdev         ++FooExConstructed;
79751c0b2f7Stbbdev         serial = serial_source++;
79851c0b2f7Stbbdev     }
79951c0b2f7Stbbdev 
80051c0b2f7Stbbdev     FooEx( const FooEx& item ) : state(LIVE) {
80151c0b2f7Stbbdev         CHECK(item.state == LIVE);
80251c0b2f7Stbbdev         ++FooExConstructed;
80351c0b2f7Stbbdev         if (MaxFooCount && (FooExConstructed - FooExDestroyed) >= MaxFooCount) { // in push()
80451c0b2f7Stbbdev             throw Foo_exception();
80551c0b2f7Stbbdev         }
80651c0b2f7Stbbdev         serial = item.serial;
80751c0b2f7Stbbdev     }
80851c0b2f7Stbbdev 
80951c0b2f7Stbbdev     ~FooEx() {
81051c0b2f7Stbbdev         CHECK(state==LIVE);
81151c0b2f7Stbbdev         ++FooExDestroyed;
81251c0b2f7Stbbdev         state=DEAD;
81351c0b2f7Stbbdev         serial=DEAD;
81451c0b2f7Stbbdev     }
81551c0b2f7Stbbdev 
81651c0b2f7Stbbdev     void operator=( FooEx& item ) {
81751c0b2f7Stbbdev         CHECK(item.state==LIVE);
81851c0b2f7Stbbdev         CHECK(state==LIVE);
81951c0b2f7Stbbdev         serial = item.serial;
82051c0b2f7Stbbdev         if( MaxFooCount==2*Threshold && (FooExConstructed-FooExDestroyed) <= MaxFooCount/4 ) // in pop()
82151c0b2f7Stbbdev             throw Foo_exception();
82251c0b2f7Stbbdev     }
82351c0b2f7Stbbdev 
82451c0b2f7Stbbdev     void operator=( FooEx&& item ) {
82551c0b2f7Stbbdev         operator=( item );
82651c0b2f7Stbbdev         item.serial = 0;
82751c0b2f7Stbbdev     }
82851c0b2f7Stbbdev 
82951c0b2f7Stbbdev };
83051c0b2f7Stbbdev 
83151c0b2f7Stbbdev template <template <typename, typename> class CQ, typename A1, typename A2, typename T>
83251c0b2f7Stbbdev void TestExceptionBody() {
83351c0b2f7Stbbdev     enum methods {
83451c0b2f7Stbbdev         m_push = 0,
83551c0b2f7Stbbdev         m_pop
83651c0b2f7Stbbdev     };
83751c0b2f7Stbbdev 
83851c0b2f7Stbbdev     const int N = 1000;     // # of bytes
83951c0b2f7Stbbdev 
84051c0b2f7Stbbdev     MaxFooCount = 5;
84151c0b2f7Stbbdev 
84251c0b2f7Stbbdev     try {
84351c0b2f7Stbbdev         int n_pushed=0, n_popped=0;
84451c0b2f7Stbbdev         for(int t = 0; t <= 1; t++)// exception type -- 0 : from allocator(), 1 : from Foo's constructor
84551c0b2f7Stbbdev         {
84651c0b2f7Stbbdev             CQ<T,A1> queue_test;
84751c0b2f7Stbbdev             for( int m=m_push; m<=m_pop; m++ ) {
84851c0b2f7Stbbdev                 // concurrent_queue internally rebinds the allocator to the one for 'char'
84951c0b2f7Stbbdev                 A2::init_counters();
85051c0b2f7Stbbdev 
85151c0b2f7Stbbdev                 if(t) MaxFooCount = MaxFooCount + 400;
85251c0b2f7Stbbdev                 else A2::set_limits(N/2);
85351c0b2f7Stbbdev 
85451c0b2f7Stbbdev                 try {
85551c0b2f7Stbbdev                     switch(m) {
85651c0b2f7Stbbdev                     case m_push:
85751c0b2f7Stbbdev                         for( int k=0; k<N; k++ ) {
85851c0b2f7Stbbdev                             push( queue_test, T(), k);
85951c0b2f7Stbbdev                             n_pushed++;
86051c0b2f7Stbbdev                         }
86151c0b2f7Stbbdev                         break;
86251c0b2f7Stbbdev                     case m_pop:
86351c0b2f7Stbbdev                         n_popped=0;
86451c0b2f7Stbbdev                         for( int k=0; k<n_pushed; k++ ) {
86551c0b2f7Stbbdev                             T elt;
86651c0b2f7Stbbdev                             queue_test.try_pop( elt);
86751c0b2f7Stbbdev                             n_popped++;
86851c0b2f7Stbbdev                         }
86951c0b2f7Stbbdev                         n_pushed = 0;
87051c0b2f7Stbbdev                         A2::set_limits();
87151c0b2f7Stbbdev                         break;
87251c0b2f7Stbbdev                     }
87351c0b2f7Stbbdev                     if( !t && m==m_push ) REQUIRE_MESSAGE(false, "should throw an exception");
87451c0b2f7Stbbdev                 } catch ( Foo_exception & ) {
87551c0b2f7Stbbdev                     long tc = MaxFooCount;
87651c0b2f7Stbbdev                     MaxFooCount = 0; // disable exception
87751c0b2f7Stbbdev                     switch(m) {
87851c0b2f7Stbbdev                     case m_push:
87951c0b2f7Stbbdev                         REQUIRE_MESSAGE(ptrdiff_t(queue_test.size())==n_pushed, "incorrect queue size");
88051c0b2f7Stbbdev                         for( int k=0; k<(int)tc; k++ ) {
88151c0b2f7Stbbdev                             push( queue_test, T(), k);
88251c0b2f7Stbbdev                             n_pushed++;
88351c0b2f7Stbbdev                         }
88451c0b2f7Stbbdev                         break;
88551c0b2f7Stbbdev                     case m_pop:
88651c0b2f7Stbbdev                         n_pushed -= (n_popped+1); // including one that threw the exception
88751c0b2f7Stbbdev                         REQUIRE_MESSAGE(n_pushed>=0, "n_pushed cannot be less than 0");
88851c0b2f7Stbbdev                         for( int k=0; k<1000; k++ ) {
88951c0b2f7Stbbdev                             push( queue_test, T(), k);
89051c0b2f7Stbbdev                             n_pushed++;
89151c0b2f7Stbbdev                         }
89251c0b2f7Stbbdev                         REQUIRE_MESSAGE(!queue_test.empty(), "queue must not be empty");
89351c0b2f7Stbbdev                         REQUIRE_MESSAGE(ptrdiff_t(queue_test.size())==n_pushed, "queue size must be equal to n pushed");
89451c0b2f7Stbbdev                         for( int k=0; k<n_pushed; k++ ) {
89551c0b2f7Stbbdev                             T elt;
89651c0b2f7Stbbdev                             queue_test.try_pop( elt);
89751c0b2f7Stbbdev                         }
89851c0b2f7Stbbdev                         REQUIRE_MESSAGE(queue_test.empty(), "queue must be empty");
89951c0b2f7Stbbdev                         REQUIRE_MESSAGE(queue_test.size()==0, "queue must be empty");
90051c0b2f7Stbbdev                         break;
90151c0b2f7Stbbdev                     }
90251c0b2f7Stbbdev                     MaxFooCount = tc;
90351c0b2f7Stbbdev                 } catch ( std::bad_alloc & ) {
90451c0b2f7Stbbdev                     A2::set_limits(); // disable exception from allocator
90551c0b2f7Stbbdev                     std::size_t size = queue_test.size();
90651c0b2f7Stbbdev                     switch(m) {
90751c0b2f7Stbbdev                         case m_push:
90851c0b2f7Stbbdev                             REQUIRE_MESSAGE(size>0, "incorrect queue size");
90951c0b2f7Stbbdev                             break;
91051c0b2f7Stbbdev                         case m_pop:
91151c0b2f7Stbbdev                             if( !t ) REQUIRE_MESSAGE(false, "should not throw an exception");
91251c0b2f7Stbbdev                             break;
91351c0b2f7Stbbdev                     }
91451c0b2f7Stbbdev                 }
91551c0b2f7Stbbdev                 INFO("for t= " << t << "and m= " << m << " exception test passed");
91651c0b2f7Stbbdev             }
91751c0b2f7Stbbdev         }
91851c0b2f7Stbbdev     } catch(...) {
91951c0b2f7Stbbdev         REQUIRE_MESSAGE(false, "unexpected exception");
92051c0b2f7Stbbdev     }
92151c0b2f7Stbbdev }
92251c0b2f7Stbbdev 
92351c0b2f7Stbbdev void TestExceptions() {
92449e08aacStbbdev     using allocator_t = StaticSharedCountingAllocator<oneapi::tbb::cache_aligned_allocator<std::size_t>>;
92549e08aacStbbdev     using allocator_char_t = StaticSharedCountingAllocator<oneapi::tbb::cache_aligned_allocator<char>>;
92651c0b2f7Stbbdev     TestExceptionBody<ConcQWithSizeWrapper, allocator_t, allocator_char_t, FooEx>();
92749e08aacStbbdev     TestExceptionBody<oneapi::tbb::concurrent_bounded_queue, allocator_t, allocator_char_t, FooEx>();
92851c0b2f7Stbbdev 
92951c0b2f7Stbbdev }
93051c0b2f7Stbbdev 
93151c0b2f7Stbbdev std::atomic<std::size_t> num_pushed;
93251c0b2f7Stbbdev std::atomic<std::size_t> num_popped;
93351c0b2f7Stbbdev std::atomic<std::size_t> failed_pushes;
93451c0b2f7Stbbdev std::atomic<std::size_t> failed_pops;
93551c0b2f7Stbbdev 
93651c0b2f7Stbbdev class SimplePushBody {
93749e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<int>* q;
93851c0b2f7Stbbdev     std::size_t max;
93951c0b2f7Stbbdev public:
94049e08aacStbbdev     SimplePushBody(oneapi::tbb::concurrent_bounded_queue<int>* _q, std::size_t hi_thr) : q(_q), max(hi_thr) {}
94151c0b2f7Stbbdev 
94251c0b2f7Stbbdev     void operator()(std::size_t thread_id) const {
94351c0b2f7Stbbdev         if (thread_id == max) {
94451c0b2f7Stbbdev             while ( q->size() < std::ptrdiff_t(max) ) {
945b15aabb3Stbbdev                 utils::yield();
94651c0b2f7Stbbdev             }
94751c0b2f7Stbbdev             q->abort();
94851c0b2f7Stbbdev             return;
94951c0b2f7Stbbdev         }
95051c0b2f7Stbbdev         try {
95151c0b2f7Stbbdev             q->push(42);
95251c0b2f7Stbbdev             ++num_pushed;
95351c0b2f7Stbbdev         } catch (...) {
95451c0b2f7Stbbdev             ++failed_pushes;
95551c0b2f7Stbbdev         }
95651c0b2f7Stbbdev     }
95751c0b2f7Stbbdev };
95851c0b2f7Stbbdev 
95951c0b2f7Stbbdev class SimplePopBody {
96049e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<int>* q;
96151c0b2f7Stbbdev     std::ptrdiff_t max;
96251c0b2f7Stbbdev     std::ptrdiff_t prefill;
96351c0b2f7Stbbdev public:
96449e08aacStbbdev     SimplePopBody(oneapi::tbb::concurrent_bounded_queue<int>* _q, std::size_t hi_thr, std::size_t nitems)
96551c0b2f7Stbbdev     : q(_q), max(hi_thr), prefill(nitems) {}
96651c0b2f7Stbbdev 
96751c0b2f7Stbbdev     void operator()(std::size_t thread_id) const {
96851c0b2f7Stbbdev         int e;
96951c0b2f7Stbbdev         if (thread_id == std::size_t(max)) {
97051c0b2f7Stbbdev             while (q->size()> prefill - max) {
971b15aabb3Stbbdev                 utils::yield();
97251c0b2f7Stbbdev             }
97351c0b2f7Stbbdev 
97451c0b2f7Stbbdev             q->abort();
97551c0b2f7Stbbdev             return;
97651c0b2f7Stbbdev         }
97751c0b2f7Stbbdev         try {
97851c0b2f7Stbbdev             q->pop(e);
97951c0b2f7Stbbdev             ++num_popped;
98051c0b2f7Stbbdev         } catch ( ... ) {
98151c0b2f7Stbbdev             ++failed_pops;
98251c0b2f7Stbbdev         }
98351c0b2f7Stbbdev     }
98451c0b2f7Stbbdev };
98551c0b2f7Stbbdev 
98651c0b2f7Stbbdev void TestAbort() {
98751c0b2f7Stbbdev     for (std::size_t nthreads = MinThread; nthreads <= MaxThread; ++nthreads) {
98849e08aacStbbdev         oneapi::tbb::concurrent_bounded_queue<int> iq1;
98951c0b2f7Stbbdev         iq1.set_capacity(0);
99051c0b2f7Stbbdev         for (std::size_t i = 0; i < 10; ++i) {
99151c0b2f7Stbbdev             num_pushed.store(0, std::memory_order_relaxed);
99251c0b2f7Stbbdev             num_popped.store(0, std::memory_order_relaxed);
99351c0b2f7Stbbdev             failed_pushes.store(0, std::memory_order_relaxed);
99451c0b2f7Stbbdev             failed_pops.store(0, std::memory_order_relaxed);
99551c0b2f7Stbbdev             SimplePushBody my_push_body1(&iq1, nthreads);
99651c0b2f7Stbbdev             utils::NativeParallelFor(nthreads + 1, my_push_body1);
99751c0b2f7Stbbdev             REQUIRE_MESSAGE(num_pushed == 0, "no elements should have been pushed to zero-sized queue");
99851c0b2f7Stbbdev             REQUIRE_MESSAGE(failed_pushes == nthreads, "All threads should have failed to push an element to zero-sized queue");
99951c0b2f7Stbbdev             // Do not test popping each time in order to test queue destruction with no previous pops
100051c0b2f7Stbbdev             if (nthreads < (MaxThread + MinThread) / 2) {
100151c0b2f7Stbbdev                 int e;
100251c0b2f7Stbbdev                 bool queue_empty = !iq1.try_pop(e);
100351c0b2f7Stbbdev                 REQUIRE_MESSAGE(queue_empty, "no elements should have been popped from zero-sized queue");
100451c0b2f7Stbbdev             }
100551c0b2f7Stbbdev         }
100651c0b2f7Stbbdev 
100749e08aacStbbdev         oneapi::tbb::concurrent_bounded_queue<int> iq2;
100851c0b2f7Stbbdev         iq2.set_capacity(2);
100951c0b2f7Stbbdev         for (std::size_t i=0; i < 10; ++i) {
101051c0b2f7Stbbdev             num_pushed.store(0, std::memory_order_relaxed);
101151c0b2f7Stbbdev             num_popped.store(0, std::memory_order_relaxed);
101251c0b2f7Stbbdev             failed_pushes.store(0, std::memory_order_relaxed);
101351c0b2f7Stbbdev             failed_pops.store(0, std::memory_order_relaxed);
101451c0b2f7Stbbdev             SimplePushBody my_push_body2(&iq2, nthreads);
101551c0b2f7Stbbdev             utils::NativeParallelFor(nthreads + 1, my_push_body2);
101651c0b2f7Stbbdev             REQUIRE_MESSAGE(num_pushed <= 2, "at most 2 elements should have been pushed to queue of size 2");
101751c0b2f7Stbbdev             if (nthreads>= 2)
101851c0b2f7Stbbdev                 REQUIRE_MESSAGE(failed_pushes == nthreads - 2, "nthreads-2 threads should have failed to push an element to queue of size 2");
101951c0b2f7Stbbdev             int e;
102051c0b2f7Stbbdev             while (iq2.try_pop(e)) ;
102151c0b2f7Stbbdev         }
102251c0b2f7Stbbdev 
102349e08aacStbbdev         oneapi::tbb::concurrent_bounded_queue<int> iq3;
102451c0b2f7Stbbdev         iq3.set_capacity(2);
102551c0b2f7Stbbdev         for (std::size_t i = 0; i < 10; ++i) {
102651c0b2f7Stbbdev             num_pushed.store(0, std::memory_order_relaxed);
102751c0b2f7Stbbdev             num_popped.store(0, std::memory_order_relaxed);
102851c0b2f7Stbbdev             failed_pushes.store(0, std::memory_order_relaxed);
102951c0b2f7Stbbdev             failed_pops.store(0, std::memory_order_relaxed);
103051c0b2f7Stbbdev             iq3.push(42);
103151c0b2f7Stbbdev             iq3.push(42);
103251c0b2f7Stbbdev             SimplePopBody my_pop_body(&iq3, nthreads, 2);
103351c0b2f7Stbbdev             utils::NativeParallelFor( nthreads+1, my_pop_body );
103451c0b2f7Stbbdev             REQUIRE_MESSAGE(num_popped <= 2, "at most 2 elements should have been popped from queue of size 2");
103551c0b2f7Stbbdev             if (nthreads>= 2)
103651c0b2f7Stbbdev                 REQUIRE_MESSAGE(failed_pops == nthreads - 2, "nthreads-2 threads should have failed to pop an element from queue of size 2");
103751c0b2f7Stbbdev             else {
103851c0b2f7Stbbdev                 int e;
103951c0b2f7Stbbdev                 iq3.pop(e);
104051c0b2f7Stbbdev             }
104151c0b2f7Stbbdev         }
104251c0b2f7Stbbdev 
104349e08aacStbbdev         oneapi::tbb::concurrent_bounded_queue<int> iq4;
104451c0b2f7Stbbdev         std::size_t cap = nthreads / 2;
104551c0b2f7Stbbdev         if (!cap) cap = 1;
104651c0b2f7Stbbdev         iq4.set_capacity(cap);
104751c0b2f7Stbbdev         for (int i=0; i<10; ++i) {
104851c0b2f7Stbbdev             num_pushed.store(0, std::memory_order_relaxed);
104951c0b2f7Stbbdev             num_popped.store(0, std::memory_order_relaxed);
105051c0b2f7Stbbdev             failed_pushes.store(0, std::memory_order_relaxed);
105151c0b2f7Stbbdev             failed_pops.store(0, std::memory_order_relaxed);
105251c0b2f7Stbbdev             SimplePushBody my_push_body2(&iq4, nthreads);
105351c0b2f7Stbbdev             utils::NativeParallelFor(nthreads + 1, my_push_body2);
105451c0b2f7Stbbdev             REQUIRE_MESSAGE(num_pushed <= cap, "at most cap elements should have been pushed to queue of size cap");
105551c0b2f7Stbbdev             if (nthreads>= cap)
105651c0b2f7Stbbdev                 REQUIRE_MESSAGE(failed_pushes == nthreads-cap, "nthreads-cap threads should have failed to push an element to queue of size cap");
105751c0b2f7Stbbdev             SimplePopBody my_pop_body(&iq4, nthreads, num_pushed);
105851c0b2f7Stbbdev             utils::NativeParallelFor( nthreads+1, my_pop_body );
105951c0b2f7Stbbdev             REQUIRE_MESSAGE((int)num_popped <= cap, "at most cap elements should have been popped from queue of size cap");
106051c0b2f7Stbbdev             if (nthreads>= cap)
106151c0b2f7Stbbdev                 REQUIRE_MESSAGE(failed_pops == nthreads-cap, "nthreads-cap threads should have failed to pop an element from queue of size cap");
106251c0b2f7Stbbdev             else {
106351c0b2f7Stbbdev                 int e;
106451c0b2f7Stbbdev                 while (iq4.try_pop(e)) ;
106551c0b2f7Stbbdev             }
106651c0b2f7Stbbdev         }
106751c0b2f7Stbbdev     }
106851c0b2f7Stbbdev }
106951c0b2f7Stbbdev #endif
107051c0b2f7Stbbdev 
107151c0b2f7Stbbdev template <template <typename...> class ContainerType>
107251c0b2f7Stbbdev void test_member_types() {
107351c0b2f7Stbbdev     using container_type = ContainerType<int>;
107449e08aacStbbdev     static_assert(std::is_same<typename container_type::allocator_type, oneapi::tbb::cache_aligned_allocator<int>>::value,
107551c0b2f7Stbbdev                   "Incorrect default template allocator");
107651c0b2f7Stbbdev 
107751c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::value_type, int>::value,
107851c0b2f7Stbbdev                   "Incorrect container value_type member type");
107951c0b2f7Stbbdev 
108051c0b2f7Stbbdev     static_assert(std::is_signed<typename container_type::difference_type>::value,
108151c0b2f7Stbbdev                   "Incorrect container difference_type member type");
108251c0b2f7Stbbdev 
108351c0b2f7Stbbdev     using value_type = typename container_type::value_type;
108451c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::reference, value_type&>::value,
108551c0b2f7Stbbdev                   "Incorrect container reference member type");
108651c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::const_reference, const value_type&>::value,
108751c0b2f7Stbbdev                   "Incorrect container const_reference member type");
108851c0b2f7Stbbdev     using allocator_type = typename container_type::allocator_type;
108951c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::pointer, typename std::allocator_traits<allocator_type>::pointer>::value,
109051c0b2f7Stbbdev                   "Incorrect container pointer member type");
109151c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::const_pointer, typename std::allocator_traits<allocator_type>::const_pointer>::value,
109251c0b2f7Stbbdev                   "Incorrect container const_pointer member type");
109351c0b2f7Stbbdev 
109451c0b2f7Stbbdev     static_assert(utils::is_forward_iterator<typename container_type::iterator>::value,
109551c0b2f7Stbbdev                   "Incorrect container iterator member type");
109651c0b2f7Stbbdev     static_assert(!std::is_const<typename container_type::iterator::value_type>::value,
109751c0b2f7Stbbdev                   "Incorrect container iterator member type");
109851c0b2f7Stbbdev     static_assert(utils::is_forward_iterator<typename container_type::const_iterator>::value,
109951c0b2f7Stbbdev                   "Incorrect container const_iterator member type");
110051c0b2f7Stbbdev     static_assert(std::is_const<typename container_type::const_iterator::value_type>::value,
110151c0b2f7Stbbdev                   "Incorrect container iterator member type");
110251c0b2f7Stbbdev }
110351c0b2f7Stbbdev 
110451c0b2f7Stbbdev enum push_t { push_op, try_push_op };
110551c0b2f7Stbbdev 
110651c0b2f7Stbbdev template<push_t push_op>
110751c0b2f7Stbbdev struct pusher {
110851c0b2f7Stbbdev     template<typename CQ, typename VType>
110951c0b2f7Stbbdev     static bool push( CQ& queue, VType&& val ) {
111051c0b2f7Stbbdev         queue.push( std::forward<VType>( val ) );
111151c0b2f7Stbbdev         return true;
111251c0b2f7Stbbdev     }
111351c0b2f7Stbbdev };
111451c0b2f7Stbbdev 
111551c0b2f7Stbbdev template<>
111651c0b2f7Stbbdev struct pusher< try_push_op> {
111751c0b2f7Stbbdev     template<typename CQ, typename VType>
111851c0b2f7Stbbdev     static bool push( CQ& queue, VType&& val ) {
111951c0b2f7Stbbdev         return queue.try_push( std::forward<VType>( val ) );
112051c0b2f7Stbbdev     }
112151c0b2f7Stbbdev };
112251c0b2f7Stbbdev 
112351c0b2f7Stbbdev enum pop_t { pop_op, try_pop_op };
112451c0b2f7Stbbdev 
112551c0b2f7Stbbdev template<pop_t pop_op>
112651c0b2f7Stbbdev struct popper {
112751c0b2f7Stbbdev     template<typename CQ, typename VType>
112851c0b2f7Stbbdev     static bool pop( CQ& queue, VType&& val ) {
112951c0b2f7Stbbdev         if( queue.empty() ) return false;
113051c0b2f7Stbbdev         queue.pop( std::forward<VType>( val ) );
113151c0b2f7Stbbdev         return true;
113251c0b2f7Stbbdev     }
113351c0b2f7Stbbdev };
113451c0b2f7Stbbdev 
113551c0b2f7Stbbdev template<>
113651c0b2f7Stbbdev struct popper<try_pop_op> {
113751c0b2f7Stbbdev     template<typename CQ, typename VType>
113851c0b2f7Stbbdev     static bool pop( CQ& queue, VType&& val ) {
113951c0b2f7Stbbdev         return queue.try_pop( std::forward<VType>( val ) );
114051c0b2f7Stbbdev     }
114151c0b2f7Stbbdev };
114251c0b2f7Stbbdev 
114351c0b2f7Stbbdev struct MoveOperationTracker {
114451c0b2f7Stbbdev     static std::size_t copy_constructor_called_times;
114551c0b2f7Stbbdev     static std::size_t move_constructor_called_times;
114651c0b2f7Stbbdev     static std::size_t copy_assignment_called_times;
114751c0b2f7Stbbdev     static std::size_t move_assignment_called_times;
114851c0b2f7Stbbdev 
114951c0b2f7Stbbdev     MoveOperationTracker() {}
115051c0b2f7Stbbdev     MoveOperationTracker(const MoveOperationTracker&) {
115151c0b2f7Stbbdev         ++copy_constructor_called_times;
115251c0b2f7Stbbdev     }
115351c0b2f7Stbbdev     MoveOperationTracker(MoveOperationTracker&&) {
115451c0b2f7Stbbdev         ++move_constructor_called_times;
115551c0b2f7Stbbdev     }
115651c0b2f7Stbbdev     MoveOperationTracker& operator=(MoveOperationTracker const&) {
115751c0b2f7Stbbdev         ++copy_assignment_called_times;
115851c0b2f7Stbbdev         return *this;
115951c0b2f7Stbbdev     }
116051c0b2f7Stbbdev     MoveOperationTracker& operator=(MoveOperationTracker&&) {
116151c0b2f7Stbbdev         ++move_assignment_called_times;
116251c0b2f7Stbbdev         return *this;
116351c0b2f7Stbbdev     }
116451c0b2f7Stbbdev };
116551c0b2f7Stbbdev 
116651c0b2f7Stbbdev size_t MoveOperationTracker::copy_constructor_called_times = 0;
116751c0b2f7Stbbdev size_t MoveOperationTracker::move_constructor_called_times = 0;
116851c0b2f7Stbbdev size_t MoveOperationTracker::copy_assignment_called_times = 0;
116951c0b2f7Stbbdev size_t MoveOperationTracker::move_assignment_called_times = 0;
117051c0b2f7Stbbdev 
117151c0b2f7Stbbdev template <class CQ, push_t push_op, pop_t pop_op>
117251c0b2f7Stbbdev void TestMoveSupport() {
117351c0b2f7Stbbdev     std::size_t &mcct = MoveOperationTracker::move_constructor_called_times;
117451c0b2f7Stbbdev     std::size_t &ccct = MoveOperationTracker::copy_constructor_called_times;
117551c0b2f7Stbbdev     std::size_t &cact = MoveOperationTracker::copy_assignment_called_times;
117651c0b2f7Stbbdev     std::size_t &mact = MoveOperationTracker::move_assignment_called_times;
117751c0b2f7Stbbdev     mcct = ccct = cact = mact = 0;
117851c0b2f7Stbbdev 
117951c0b2f7Stbbdev     CQ q;
118051c0b2f7Stbbdev 
118151c0b2f7Stbbdev     REQUIRE_MESSAGE(mcct == 0, "Value must be zero-initialized");
118251c0b2f7Stbbdev     REQUIRE_MESSAGE(ccct == 0, "Value must be zero-initialized");
118351c0b2f7Stbbdev     CHECK(pusher<push_op>::push( q, MoveOperationTracker() ));
118451c0b2f7Stbbdev     REQUIRE_MESSAGE(mcct == 1, "Not working push(T&&) or try_push(T&&)?");
118551c0b2f7Stbbdev     REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&) or try_push(T&&)");
118651c0b2f7Stbbdev 
118751c0b2f7Stbbdev     MoveOperationTracker ob;
118851c0b2f7Stbbdev     CHECK(pusher<push_op>::push( q, std::move(ob) ));
118951c0b2f7Stbbdev     REQUIRE_MESSAGE(mcct == 2, "Not working push(T&&) or try_push(T&&)?");
119051c0b2f7Stbbdev     REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&) or try_push(T&&)");
119151c0b2f7Stbbdev 
119251c0b2f7Stbbdev     REQUIRE_MESSAGE(cact == 0, "Copy assignment called during push(T&&) or try_push(T&&)");
119351c0b2f7Stbbdev     REQUIRE_MESSAGE(mact == 0, "Move assignment called during push(T&&) or try_push(T&&)");
119451c0b2f7Stbbdev 
119551c0b2f7Stbbdev     bool result = popper<pop_op>::pop( q, ob );
119651c0b2f7Stbbdev     CHECK(result);
119751c0b2f7Stbbdev     REQUIRE_MESSAGE(cact == 0, "Copy assignment called during try_pop(T&&)");
119851c0b2f7Stbbdev     REQUIRE_MESSAGE(mact == 1, "Move assignment was not called during try_pop(T&&)");
119951c0b2f7Stbbdev }
120051c0b2f7Stbbdev 
120151c0b2f7Stbbdev void TestMoveSupportInPushPop() {
120249e08aacStbbdev     TestMoveSupport<oneapi::tbb::concurrent_queue<MoveOperationTracker>, push_op, try_pop_op>();
120349e08aacStbbdev     TestMoveSupport<oneapi::tbb::concurrent_bounded_queue<MoveOperationTracker>, push_op, pop_op>();
120449e08aacStbbdev     TestMoveSupport<oneapi::tbb::concurrent_bounded_queue<MoveOperationTracker>, try_push_op, try_pop_op>();
120551c0b2f7Stbbdev }
120651c0b2f7Stbbdev 
120751c0b2f7Stbbdev template<class T>
120849e08aacStbbdev class allocator: public oneapi::tbb::cache_aligned_allocator<T> {
120951c0b2f7Stbbdev public:
1210fbc48b39Svlserov     state_type state = LIVE;
121151c0b2f7Stbbdev     std::size_t m_unique_id;
121251c0b2f7Stbbdev 
121351c0b2f7Stbbdev     allocator() : m_unique_id( 0 ) {}
121451c0b2f7Stbbdev     allocator(size_t unique_id) { m_unique_id = unique_id; }
121551c0b2f7Stbbdev 
1216fbc48b39Svlserov     ~allocator() {
1217fbc48b39Svlserov         REQUIRE_MESSAGE(state == LIVE, "Destroyed allocator has been used.");
1218fbc48b39Svlserov         state = DEAD;
1219fbc48b39Svlserov     }
1220fbc48b39Svlserov 
122151c0b2f7Stbbdev     template<typename U>
1222fbc48b39Svlserov     allocator(const allocator<U>& a) noexcept {
1223fbc48b39Svlserov         REQUIRE_MESSAGE(a.state == LIVE, "Destroyed allocator has been used.");
1224fbc48b39Svlserov         m_unique_id = a.m_unique_id;
1225fbc48b39Svlserov     }
122651c0b2f7Stbbdev 
122751c0b2f7Stbbdev     template<typename U>
122851c0b2f7Stbbdev     struct rebind { typedef allocator<U> other; };
122951c0b2f7Stbbdev 
123051c0b2f7Stbbdev     friend bool operator==(const allocator& lhs, const allocator& rhs) {
1231fbc48b39Svlserov         REQUIRE_MESSAGE(lhs.state == LIVE, "Destroyed allocator has been used.");
1232fbc48b39Svlserov         REQUIRE_MESSAGE(rhs.state == LIVE, "Destroyed allocator has been used.");
123351c0b2f7Stbbdev         return lhs.m_unique_id == rhs.m_unique_id;
123451c0b2f7Stbbdev     }
123551c0b2f7Stbbdev };
123651c0b2f7Stbbdev 
123751c0b2f7Stbbdev template <typename Queue>
123851c0b2f7Stbbdev void AssertEquality(Queue &q, const std::vector<typename Queue::value_type> &vec) {
123951c0b2f7Stbbdev     CHECK(q.size() == typename Queue::size_type(vec.size()));
124051c0b2f7Stbbdev     CHECK(std::equal(q.unsafe_begin(), q.unsafe_end(), vec.begin()));
124151c0b2f7Stbbdev }
124251c0b2f7Stbbdev 
124351c0b2f7Stbbdev template <typename Queue>
124451c0b2f7Stbbdev void AssertEmptiness(Queue &q) {
124551c0b2f7Stbbdev     CHECK(q.empty());
124651c0b2f7Stbbdev     CHECK(!q.size());
124751c0b2f7Stbbdev     typename Queue::value_type elem;
124851c0b2f7Stbbdev     CHECK(!q.try_pop(elem));
124951c0b2f7Stbbdev }
125051c0b2f7Stbbdev 
125151c0b2f7Stbbdev template <push_t push_op, typename Queue>
125251c0b2f7Stbbdev void FillTest(Queue &q, const std::vector<typename Queue::value_type> &vec) {
125351c0b2f7Stbbdev     for (typename std::vector<typename Queue::value_type>::const_iterator it = vec.begin(); it != vec.end(); ++it)
125451c0b2f7Stbbdev         CHECK(pusher<push_op>::push(q, *it));
125551c0b2f7Stbbdev     AssertEquality(q, vec);
125651c0b2f7Stbbdev }
125751c0b2f7Stbbdev 
125851c0b2f7Stbbdev template <pop_t pop_op, typename Queue>
125951c0b2f7Stbbdev void EmptyTest(Queue &q, const std::vector<typename Queue::value_type> &vec) {
126051c0b2f7Stbbdev     typedef typename Queue::value_type value_type;
126151c0b2f7Stbbdev 
126251c0b2f7Stbbdev     value_type elem;
126351c0b2f7Stbbdev     typename std::vector<value_type>::const_iterator it = vec.begin();
126451c0b2f7Stbbdev     while (popper<pop_op>::pop(q, elem)) {
126551c0b2f7Stbbdev         CHECK(elem == *it);
126651c0b2f7Stbbdev         ++it;
126751c0b2f7Stbbdev     }
126851c0b2f7Stbbdev     CHECK(it == vec.end());
126951c0b2f7Stbbdev     AssertEmptiness(q);
127051c0b2f7Stbbdev }
127151c0b2f7Stbbdev 
127251c0b2f7Stbbdev template <typename T, typename A>
127349e08aacStbbdev void bounded_queue_specific_test(oneapi::tbb::concurrent_queue<T, A> &, const std::vector<T> &) { /* do nothing */ }
127451c0b2f7Stbbdev 
127551c0b2f7Stbbdev template <typename T, typename A>
127649e08aacStbbdev void bounded_queue_specific_test(oneapi::tbb::concurrent_bounded_queue<T, A> &q, const std::vector<T> &vec) {
127749e08aacStbbdev     typedef typename oneapi::tbb::concurrent_bounded_queue<T, A>::size_type size_type;
127851c0b2f7Stbbdev 
127951c0b2f7Stbbdev     FillTest<try_push_op>(q, vec);
128049e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<T, A> q2 = q;
128151c0b2f7Stbbdev     EmptyTest<pop_op>(q, vec);
128251c0b2f7Stbbdev 
128351c0b2f7Stbbdev     // capacity
128451c0b2f7Stbbdev     q2.set_capacity(size_type(vec.size()));
128551c0b2f7Stbbdev     CHECK(q2.capacity() == size_type(vec.size()));
128651c0b2f7Stbbdev     CHECK(q2.size() == size_type(vec.size()));
128751c0b2f7Stbbdev     CHECK(!q2.try_push(vec[0]));
128851c0b2f7Stbbdev     q.abort();
128951c0b2f7Stbbdev }
129051c0b2f7Stbbdev 
129151c0b2f7Stbbdev // Checks operability of the queue the data was moved from
129251c0b2f7Stbbdev template<typename T, typename CQ>
129351c0b2f7Stbbdev void TestQueueOperabilityAfterDataMove( CQ& queue ) {
129451c0b2f7Stbbdev     const std::size_t size = 10;
129551c0b2f7Stbbdev     std::vector<T> v(size);
129651c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i ) v[i] = T( i * i + i );
129751c0b2f7Stbbdev 
129851c0b2f7Stbbdev     FillTest<push_op>(queue, v);
129951c0b2f7Stbbdev     EmptyTest<try_pop_op>(queue, v);
130051c0b2f7Stbbdev     bounded_queue_specific_test(queue, v);
130151c0b2f7Stbbdev }
130251c0b2f7Stbbdev 
130351c0b2f7Stbbdev template<class CQ, class T>
130451c0b2f7Stbbdev void TestMoveConstructors() {
130551c0b2f7Stbbdev     T::construction_num = T::destruction_num = 0;
130651c0b2f7Stbbdev     CQ src_queue( allocator<T>(0) );
130751c0b2f7Stbbdev     const std::size_t size = 10;
130851c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i )
130951c0b2f7Stbbdev         src_queue.push( T(i + (i ^ size)) );
131051c0b2f7Stbbdev     CHECK(T::construction_num == 2 * size);
131151c0b2f7Stbbdev     CHECK(T::destruction_num == size);
131251c0b2f7Stbbdev 
131351c0b2f7Stbbdev     const T* locations[size];
131451c0b2f7Stbbdev     typename CQ::const_iterator qit = src_queue.unsafe_begin();
131551c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i, ++qit )
131651c0b2f7Stbbdev         locations[i] = &(*qit);
131751c0b2f7Stbbdev 
131851c0b2f7Stbbdev     // Ensuring allocation operation takes place during move when allocators are different
131951c0b2f7Stbbdev     T::construction_num = T::destruction_num = 0;
132051c0b2f7Stbbdev     CQ dst_queue( std::move(src_queue), allocator<T>(1) );
132151c0b2f7Stbbdev     CHECK(T::construction_num == size);
1322*8155aaebSkboyarinov     CHECK(T::destruction_num == size);
132351c0b2f7Stbbdev 
132451c0b2f7Stbbdev     TestQueueOperabilityAfterDataMove<T>( src_queue );
132551c0b2f7Stbbdev 
132651c0b2f7Stbbdev     qit = dst_queue.unsafe_begin();
132751c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i, ++qit ) {
132851c0b2f7Stbbdev         REQUIRE_MESSAGE(locations[i] != &(*qit), "an item should have been copied but was not" );
132951c0b2f7Stbbdev         locations[i] = &(*qit);
133051c0b2f7Stbbdev     }
133151c0b2f7Stbbdev 
133251c0b2f7Stbbdev     T::construction_num = T::destruction_num = 0;
133351c0b2f7Stbbdev     // Ensuring there is no allocation operation during move with equal allocators
133451c0b2f7Stbbdev     CQ dst_queue2( std::move(dst_queue), allocator<T>(1) );
133551c0b2f7Stbbdev     CHECK(T::construction_num == 0);
133651c0b2f7Stbbdev     CHECK(T::destruction_num == 0);
133751c0b2f7Stbbdev 
133851c0b2f7Stbbdev     TestQueueOperabilityAfterDataMove<T>( dst_queue );
133951c0b2f7Stbbdev 
134051c0b2f7Stbbdev     qit = dst_queue2.unsafe_begin();
134151c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i, ++qit ) {
134251c0b2f7Stbbdev         REQUIRE_MESSAGE(locations[i] == &(*qit), "an item should have been moved but was not" );
134351c0b2f7Stbbdev     }
134451c0b2f7Stbbdev 
134551c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i) {
134651c0b2f7Stbbdev         T test(i + (i ^ size));
134751c0b2f7Stbbdev         T popped;
134851c0b2f7Stbbdev         bool pop_result = dst_queue2.try_pop( popped );
134951c0b2f7Stbbdev         CHECK(pop_result);
135051c0b2f7Stbbdev         CHECK(test == popped);
135151c0b2f7Stbbdev     }
135251c0b2f7Stbbdev     CHECK(dst_queue2.empty());
135351c0b2f7Stbbdev     CHECK(dst_queue2.size() == 0);
135451c0b2f7Stbbdev }
135551c0b2f7Stbbdev 
135651c0b2f7Stbbdev void TestMoveConstruction() {
135751c0b2f7Stbbdev     TestMoveConstructors<ConcQWithSizeWrapper<Bar, allocator<Bar>>, Bar>();
135849e08aacStbbdev     TestMoveConstructors<oneapi::tbb::concurrent_bounded_queue<Bar, allocator<Bar>>, Bar>();
135951c0b2f7Stbbdev }
136051c0b2f7Stbbdev 
136151c0b2f7Stbbdev class NonTrivialConstructorType {
136251c0b2f7Stbbdev public:
136351c0b2f7Stbbdev     NonTrivialConstructorType( int a = 0 ) : m_a( a ), m_str( "" ) {}
136451c0b2f7Stbbdev     NonTrivialConstructorType( const std::string& str ) : m_a( 0 ), m_str( str ) {}
136551c0b2f7Stbbdev     NonTrivialConstructorType( int a, const std::string& str ) : m_a( a ), m_str( str ) {}
136651c0b2f7Stbbdev     int get_a() const { return m_a; }
136751c0b2f7Stbbdev     std::string get_str() const { return m_str; }
136851c0b2f7Stbbdev private:
136951c0b2f7Stbbdev     int m_a;
137051c0b2f7Stbbdev     std::string m_str;
137151c0b2f7Stbbdev };
137251c0b2f7Stbbdev 
137351c0b2f7Stbbdev enum emplace_t { emplace_op, try_emplace_op };
137451c0b2f7Stbbdev 
137551c0b2f7Stbbdev template<emplace_t emplace_op>
137651c0b2f7Stbbdev struct emplacer {
137751c0b2f7Stbbdev     template<typename CQ, typename... Args>
137851c0b2f7Stbbdev     static void emplace( CQ& queue, Args&&... val ) { queue.emplace( std::forward<Args>( val )... ); }
137951c0b2f7Stbbdev };
138051c0b2f7Stbbdev 
138151c0b2f7Stbbdev template<>
138251c0b2f7Stbbdev struct emplacer <try_emplace_op> {
138351c0b2f7Stbbdev     template<typename CQ, typename... Args>
138451c0b2f7Stbbdev     static void emplace( CQ& queue, Args&&... val ) {
138551c0b2f7Stbbdev         bool result = queue.try_emplace( std::forward<Args>( val )... );
138651c0b2f7Stbbdev         REQUIRE_MESSAGE(result, "try_emplace error\n");
138751c0b2f7Stbbdev     }
138851c0b2f7Stbbdev };
138951c0b2f7Stbbdev 
139051c0b2f7Stbbdev template<typename CQ, emplace_t emplace_op>
139151c0b2f7Stbbdev void TestEmplaceInQueue() {
139251c0b2f7Stbbdev     CQ cq;
139351c0b2f7Stbbdev     std::string test_str = "I'm being emplaced!";
139451c0b2f7Stbbdev     {
139551c0b2f7Stbbdev         emplacer<emplace_op>::emplace( cq, 5 );
139651c0b2f7Stbbdev         CHECK(cq.size() == 1);
139751c0b2f7Stbbdev         NonTrivialConstructorType popped( -1 );
139851c0b2f7Stbbdev         bool result = cq.try_pop( popped );
139951c0b2f7Stbbdev         CHECK(result);
140051c0b2f7Stbbdev         CHECK(popped.get_a() == 5);
140151c0b2f7Stbbdev         CHECK(popped.get_str() == std::string( "" ));
140251c0b2f7Stbbdev     }
140351c0b2f7Stbbdev 
140451c0b2f7Stbbdev     CHECK(cq.empty());
140551c0b2f7Stbbdev 
140651c0b2f7Stbbdev     {
140751c0b2f7Stbbdev         NonTrivialConstructorType popped( -1 );
140851c0b2f7Stbbdev         emplacer<emplace_op>::emplace( cq, std::string(test_str) );
140951c0b2f7Stbbdev         bool result = cq.try_pop( popped );
141051c0b2f7Stbbdev         CHECK(result);
141151c0b2f7Stbbdev         CHECK(popped.get_a() == 0);
141251c0b2f7Stbbdev         CHECK(popped.get_str() == test_str);
141351c0b2f7Stbbdev     }
141451c0b2f7Stbbdev 
141551c0b2f7Stbbdev     CHECK(cq.empty());
141651c0b2f7Stbbdev 
141751c0b2f7Stbbdev     {
141851c0b2f7Stbbdev         NonTrivialConstructorType popped( -1, "" );
141951c0b2f7Stbbdev         emplacer<emplace_op>::emplace( cq, 5, std::string(test_str) );
142051c0b2f7Stbbdev         bool result = cq.try_pop( popped );
142151c0b2f7Stbbdev         CHECK(result);
142251c0b2f7Stbbdev         CHECK(popped.get_a() == 5);
142351c0b2f7Stbbdev         CHECK(popped.get_str() == test_str);
142451c0b2f7Stbbdev     }
142551c0b2f7Stbbdev }
142651c0b2f7Stbbdev void TestEmplace() {
142751c0b2f7Stbbdev     TestEmplaceInQueue<ConcQWithSizeWrapper<NonTrivialConstructorType>, emplace_op>();
142849e08aacStbbdev     TestEmplaceInQueue<oneapi::tbb::concurrent_bounded_queue<NonTrivialConstructorType>, emplace_op>();
142949e08aacStbbdev     TestEmplaceInQueue<oneapi::tbb::concurrent_bounded_queue<NonTrivialConstructorType>, try_emplace_op>();
143051c0b2f7Stbbdev }
143151c0b2f7Stbbdev 
143251c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
143351c0b2f7Stbbdev template <template <typename...> typename TQueue>
143451c0b2f7Stbbdev void TestDeductionGuides() {
143551c0b2f7Stbbdev     using ComplexType = const std::string*;
143651c0b2f7Stbbdev     std::vector<ComplexType> v;
143751c0b2f7Stbbdev 
143851c0b2f7Stbbdev     // check TQueue(InputIterator, InputIterator)
143951c0b2f7Stbbdev     TQueue q1(v.begin(), v.end());
144051c0b2f7Stbbdev     static_assert(std::is_same<decltype(q1), TQueue<ComplexType>>::value);
144151c0b2f7Stbbdev 
144251c0b2f7Stbbdev     // check TQueue(InputIterator, InputIterator, Allocator)
144351c0b2f7Stbbdev     TQueue q2(v.begin(), v.end(), std::allocator<ComplexType>());
144451c0b2f7Stbbdev     static_assert(std::is_same<decltype(q2), TQueue<ComplexType, std::allocator<ComplexType>>>::value);
144551c0b2f7Stbbdev 
144651c0b2f7Stbbdev     // check TQueue(TQueue &)
144751c0b2f7Stbbdev     TQueue q3(q1);
144851c0b2f7Stbbdev     static_assert(std::is_same<decltype(q3), decltype(q1)>::value);
144951c0b2f7Stbbdev 
145051c0b2f7Stbbdev     // check TQueue(TQueue &, Allocator)
145151c0b2f7Stbbdev     TQueue q4(q2, std::allocator<ComplexType>());
145251c0b2f7Stbbdev     static_assert(std::is_same<decltype(q4), decltype(q2)>::value);
145351c0b2f7Stbbdev 
145451c0b2f7Stbbdev     // check TQueue(TQueue &&)
145551c0b2f7Stbbdev     TQueue q5(std::move(q1));
145651c0b2f7Stbbdev     static_assert(std::is_same<decltype(q5), decltype(q1)>::value);
145751c0b2f7Stbbdev 
145851c0b2f7Stbbdev     // check TQueue(TQueue &&, Allocator)
145951c0b2f7Stbbdev     TQueue q6(std::move(q4), std::allocator<ComplexType>());
146051c0b2f7Stbbdev     static_assert(std::is_same<decltype(q6), decltype(q4)>::value);
146151c0b2f7Stbbdev }
146251c0b2f7Stbbdev #endif
146351c0b2f7Stbbdev 
1464b15aabb3Stbbdev template <typename Iterator, typename QueueType>
1465b15aabb3Stbbdev void TestQueueIteratorComparisonsBasic( QueueType& q ) {
1466b15aabb3Stbbdev     REQUIRE_MESSAGE(!q.empty(), "Incorrect test setup");
1467b15aabb3Stbbdev     using namespace comparisons_testing;
1468b15aabb3Stbbdev     Iterator it1, it2;
1469b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */true>(it1, it2);
1470b15aabb3Stbbdev     it1 = q.unsafe_begin();
1471b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */false>(it1, it2);
1472b15aabb3Stbbdev     it2 = q.unsafe_begin();
1473b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */true>(it1, it2);
1474b15aabb3Stbbdev     it2 = q.unsafe_end();
1475b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */false>(it1, it2);
1476b15aabb3Stbbdev }
1477b15aabb3Stbbdev 
1478b15aabb3Stbbdev template <typename QueueType>
1479b15aabb3Stbbdev void TestQueueIteratorComparisons() {
1480b15aabb3Stbbdev     QueueType q;
1481b15aabb3Stbbdev     q.emplace(1);
1482b15aabb3Stbbdev     q.emplace(2);
1483b15aabb3Stbbdev     q.emplace(3);
1484b15aabb3Stbbdev     TestQueueIteratorComparisonsBasic<typename QueueType::iterator>(q);
1485b15aabb3Stbbdev     const QueueType& cq = q;
1486b15aabb3Stbbdev     TestQueueIteratorComparisonsBasic<typename QueueType::const_iterator>(cq);
1487b15aabb3Stbbdev }
148851c0b2f7Stbbdev 
148951c0b2f7Stbbdev //! Test constructors
149051c0b2f7Stbbdev //! \brief \ref interface \ref requirement
149151c0b2f7Stbbdev TEST_CASE("testing constructors") {
149251c0b2f7Stbbdev     TestQueueConstructors();
149351c0b2f7Stbbdev }
149451c0b2f7Stbbdev 
149551c0b2f7Stbbdev //! Test work with empty queue
149651c0b2f7Stbbdev //! \brief \ref interface \ref requirement
149751c0b2f7Stbbdev TEST_CASE("testing work with empty queue") {
149851c0b2f7Stbbdev     TestEmptiness();
149951c0b2f7Stbbdev }
150051c0b2f7Stbbdev 
150151c0b2f7Stbbdev //! Test set capacity operation
150251c0b2f7Stbbdev //! \brief \ref interface \ref requirement
150351c0b2f7Stbbdev TEST_CASE("testing set capacity operation") {
150451c0b2f7Stbbdev     TestFullness();
150551c0b2f7Stbbdev }
150651c0b2f7Stbbdev 
150751c0b2f7Stbbdev //! Test clean operation
150851c0b2f7Stbbdev //! \brief \ref interface \ref requirement
150951c0b2f7Stbbdev TEST_CASE("testing clean operation") {
151051c0b2f7Stbbdev     TestClearWorks();
151151c0b2f7Stbbdev }
151251c0b2f7Stbbdev 
151351c0b2f7Stbbdev //! Test move constructors
151451c0b2f7Stbbdev //! \brief \ref interface \ref requirement
151551c0b2f7Stbbdev TEST_CASE("testing move constructor") {
151651c0b2f7Stbbdev     TestMoveConstruction();
151751c0b2f7Stbbdev }
151851c0b2f7Stbbdev 
151951c0b2f7Stbbdev //! Test move support in push and pop
152051c0b2f7Stbbdev //! \brief \ref requirement
152151c0b2f7Stbbdev TEST_CASE("testing move support in push and pop") {
152251c0b2f7Stbbdev     TestMoveSupportInPushPop();
152351c0b2f7Stbbdev }
152451c0b2f7Stbbdev 
152551c0b2f7Stbbdev //! Test emplace operation
152651c0b2f7Stbbdev //! \brief \ref interface \ref requirement
152751c0b2f7Stbbdev TEST_CASE("testing emplace") {
152851c0b2f7Stbbdev     TestEmplace();
152951c0b2f7Stbbdev }
153051c0b2f7Stbbdev 
153151c0b2f7Stbbdev //! Test concurrent_queues member types
153251c0b2f7Stbbdev //! \brief \ref interface \ref requirement
153351c0b2f7Stbbdev TEST_CASE("testing concurrent_queues member types"){
153449e08aacStbbdev     test_member_types<oneapi::tbb::concurrent_queue>();
153549e08aacStbbdev     test_member_types<oneapi::tbb::concurrent_bounded_queue>();
153651c0b2f7Stbbdev 
153751c0b2f7Stbbdev     // Test size_type
153849e08aacStbbdev     static_assert(std::is_unsigned<typename oneapi::tbb::concurrent_queue<int>::size_type>::value,
153949e08aacStbbdev                   "Incorrect oneapi::tbb::concurrent_queue::size_type member type");
154049e08aacStbbdev     static_assert(std::is_signed<typename oneapi::tbb::concurrent_bounded_queue<int>::size_type>::value,
154149e08aacStbbdev                   "Incorrect oneapi::tbb::concurrent_bounded_queue::size_type member type");
154251c0b2f7Stbbdev }
154351c0b2f7Stbbdev 
154451c0b2f7Stbbdev //! Test iterators
154551c0b2f7Stbbdev //! \brief \ref interface \ref requirement
154651c0b2f7Stbbdev TEST_CASE("testing iterators") {
154751c0b2f7Stbbdev     TestQueueIteratorWorks();
154851c0b2f7Stbbdev }
154951c0b2f7Stbbdev 
155051c0b2f7Stbbdev //! Test concurrent oprations support
155151c0b2f7Stbbdev //! \brief \ref requirement
155251c0b2f7Stbbdev TEST_CASE("testing concurrent oprations support") {
155351c0b2f7Stbbdev     TestConcurrentPushPop();
155451c0b2f7Stbbdev }
155551c0b2f7Stbbdev 
155651c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
155751c0b2f7Stbbdev //! Test exception safety
155851c0b2f7Stbbdev //! \brief \ref requirement
155951c0b2f7Stbbdev TEST_CASE("testing exception safety") {
156051c0b2f7Stbbdev     TestExceptions();
156151c0b2f7Stbbdev }
156251c0b2f7Stbbdev 
156351c0b2f7Stbbdev //! Test abort operation
156451c0b2f7Stbbdev //! \brief \ref interface \ref requirement
156551c0b2f7Stbbdev TEST_CASE("testing abort operation") {
156651c0b2f7Stbbdev     TestAbort();
156751c0b2f7Stbbdev }
156851c0b2f7Stbbdev #endif
156951c0b2f7Stbbdev 
157051c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
157151c0b2f7Stbbdev //! Test deduction guides
157251c0b2f7Stbbdev //! \brief \ref interface
157351c0b2f7Stbbdev TEST_CASE("testing deduction guides") {
157449e08aacStbbdev     TestDeductionGuides<oneapi::tbb::concurrent_queue>();
157549e08aacStbbdev     TestDeductionGuides<oneapi::tbb::concurrent_bounded_queue>();
157651c0b2f7Stbbdev }
157751c0b2f7Stbbdev #endif
1578b15aabb3Stbbdev 
1579b15aabb3Stbbdev //! \brief \ref interface \ref requirement
1580b15aabb3Stbbdev TEST_CASE("concurrent_queue iterator comparisons") {
1581b15aabb3Stbbdev     TestQueueIteratorComparisons<oneapi::tbb::concurrent_queue<int>>();
1582b15aabb3Stbbdev }
1583b15aabb3Stbbdev 
1584b15aabb3Stbbdev //! \brief \ref interface \ref requirement
1585b15aabb3Stbbdev TEST_CASE("concurrent_bounded_queue iterator comparisons") {
1586b15aabb3Stbbdev     TestQueueIteratorComparisons<oneapi::tbb::concurrent_bounded_queue<int>>();
1587b15aabb3Stbbdev }
1588*8155aaebSkboyarinov 
1589*8155aaebSkboyarinov class MinimalisticObject {
1590*8155aaebSkboyarinov public:
1591*8155aaebSkboyarinov     struct flag {};
1592*8155aaebSkboyarinov 
1593*8155aaebSkboyarinov     MinimalisticObject() = delete;
1594*8155aaebSkboyarinov     MinimalisticObject(flag) : underlying_obj(default_obj) {}
1595*8155aaebSkboyarinov 
1596*8155aaebSkboyarinov     MinimalisticObject(const MinimalisticObject&) = delete;
1597*8155aaebSkboyarinov     MinimalisticObject& operator=(const MinimalisticObject&) = delete;
1598*8155aaebSkboyarinov 
1599*8155aaebSkboyarinov     std::size_t get_obj() const { return underlying_obj; }
1600*8155aaebSkboyarinov     std::size_t get_default_obj() const { return default_obj; }
1601*8155aaebSkboyarinov 
1602*8155aaebSkboyarinov protected:
1603*8155aaebSkboyarinov     static constexpr std::size_t default_obj = 42;
1604*8155aaebSkboyarinov     std::size_t underlying_obj;
1605*8155aaebSkboyarinov     friend struct MoveAssignableMinimalisticObject;
1606*8155aaebSkboyarinov };
1607*8155aaebSkboyarinov 
1608*8155aaebSkboyarinov struct MoveAssignableMinimalisticObject : MinimalisticObject {
1609*8155aaebSkboyarinov public:
1610*8155aaebSkboyarinov     using MinimalisticObject::MinimalisticObject;
1611*8155aaebSkboyarinov 
1612*8155aaebSkboyarinov     MoveAssignableMinimalisticObject& operator=(MoveAssignableMinimalisticObject&& other) {
1613*8155aaebSkboyarinov         if (this != &other) {
1614*8155aaebSkboyarinov             underlying_obj = other.underlying_obj;
1615*8155aaebSkboyarinov             other.underlying_obj = 0;
1616*8155aaebSkboyarinov         }
1617*8155aaebSkboyarinov         return *this;
1618*8155aaebSkboyarinov     }
1619*8155aaebSkboyarinov };
1620*8155aaebSkboyarinov 
1621*8155aaebSkboyarinov template <typename Container>
1622*8155aaebSkboyarinov void test_basics(Container& container, std::size_t desired_size) {
1623*8155aaebSkboyarinov     CHECK(!container.empty());
1624*8155aaebSkboyarinov 
1625*8155aaebSkboyarinov     std::size_t counter = 0;
1626*8155aaebSkboyarinov     for (auto it = container.unsafe_begin(); it != container.unsafe_end(); ++it) {
1627*8155aaebSkboyarinov         CHECK(it->get_obj() == it->get_default_obj());
1628*8155aaebSkboyarinov         ++counter;
1629*8155aaebSkboyarinov     }
1630*8155aaebSkboyarinov     CHECK(counter == desired_size);
1631*8155aaebSkboyarinov 
1632*8155aaebSkboyarinov     container.clear();
1633*8155aaebSkboyarinov     CHECK(container.empty());
1634*8155aaebSkboyarinov }
1635*8155aaebSkboyarinov 
1636*8155aaebSkboyarinov template <template <class...> class Container>
1637*8155aaebSkboyarinov void test_with_minimalistic_objects() {
1638*8155aaebSkboyarinov     // Test with MinimalisticObject and no pop operations
1639*8155aaebSkboyarinov     const std::size_t elements_count = 100;
1640*8155aaebSkboyarinov     {
1641*8155aaebSkboyarinov         Container<MinimalisticObject> default_container;
1642*8155aaebSkboyarinov 
1643*8155aaebSkboyarinov         for (std::size_t i = 0; i < elements_count; ++i) {
1644*8155aaebSkboyarinov             default_container.emplace(MinimalisticObject::flag{});
1645*8155aaebSkboyarinov         }
1646*8155aaebSkboyarinov         test_basics(default_container, elements_count);
1647*8155aaebSkboyarinov     }
1648*8155aaebSkboyarinov     // Test with MoveAssignableMinimalisticObject with pop operation
1649*8155aaebSkboyarinov     {
1650*8155aaebSkboyarinov         Container<MoveAssignableMinimalisticObject> default_container;
1651*8155aaebSkboyarinov 
1652*8155aaebSkboyarinov         for (std::size_t i = 0; i < elements_count; ++i) {
1653*8155aaebSkboyarinov             default_container.emplace(MinimalisticObject::flag{});
1654*8155aaebSkboyarinov         }
1655*8155aaebSkboyarinov         test_basics(default_container, elements_count);
1656*8155aaebSkboyarinov 
1657*8155aaebSkboyarinov         // Refill again
1658*8155aaebSkboyarinov         for (std::size_t i = 0; i < elements_count; ++i) {
1659*8155aaebSkboyarinov             default_container.emplace(MinimalisticObject::flag{});
1660*8155aaebSkboyarinov         }
1661*8155aaebSkboyarinov 
1662*8155aaebSkboyarinov         MoveAssignableMinimalisticObject result(MinimalisticObject::flag{});
1663*8155aaebSkboyarinov 
1664*8155aaebSkboyarinov         std::size_t element_counter = 0;
1665*8155aaebSkboyarinov         while (!default_container.empty()) {
1666*8155aaebSkboyarinov             CHECK(default_container.try_pop(result));
1667*8155aaebSkboyarinov             ++element_counter;
1668*8155aaebSkboyarinov         }
1669*8155aaebSkboyarinov 
1670*8155aaebSkboyarinov         CHECK(element_counter == elements_count);
1671*8155aaebSkboyarinov         CHECK(default_container.empty());
1672*8155aaebSkboyarinov     }
1673*8155aaebSkboyarinov }
1674*8155aaebSkboyarinov 
1675*8155aaebSkboyarinov //! \brief \ref requirement
1676*8155aaebSkboyarinov TEST_CASE("Test with minimalistic object type") {
1677*8155aaebSkboyarinov     test_with_minimalistic_objects<oneapi::tbb::concurrent_queue>();
1678*8155aaebSkboyarinov     test_with_minimalistic_objects<oneapi::tbb::concurrent_bounded_queue>();
1679*8155aaebSkboyarinov }
1680