151c0b2f7Stbbdev /*
251c0b2f7Stbbdev     Copyright (c) 2005-2020 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>
2251c0b2f7Stbbdev 
23*49e08aacStbbdev #include "oneapi/tbb/concurrent_queue.h"
24*49e08aacStbbdev #include "oneapi/tbb/cache_aligned_allocator.h"
2551c0b2f7Stbbdev #include <type_traits>
2651c0b2f7Stbbdev #include <atomic>
2751c0b2f7Stbbdev 
2851c0b2f7Stbbdev //! \file conformance_concurrent_queue.cpp
2951c0b2f7Stbbdev //! \brief Test for [containers.concurrent_queue containers.concurrent_bounded_queue] specification
3051c0b2f7Stbbdev 
3151c0b2f7Stbbdev template <typename T>
32*49e08aacStbbdev using test_allocator = StaticSharedCountingAllocator<oneapi::tbb::cache_aligned_allocator<T>>;
3351c0b2f7Stbbdev 
3451c0b2f7Stbbdev static constexpr std::size_t MinThread = 1;
3551c0b2f7Stbbdev static constexpr std::size_t MaxThread = 4;
3651c0b2f7Stbbdev 
3751c0b2f7Stbbdev static constexpr std::size_t MAXTHREAD = 256;
3851c0b2f7Stbbdev 
3951c0b2f7Stbbdev static constexpr std::size_t M = 10000;
4051c0b2f7Stbbdev static std::atomic<long> PopKind[3];
4151c0b2f7Stbbdev 
4251c0b2f7Stbbdev static int Sum[MAXTHREAD];
4351c0b2f7Stbbdev 
4451c0b2f7Stbbdev template<typename CQ, typename ValueType, typename CounterType>
4551c0b2f7Stbbdev void push(CQ& q, ValueType v, CounterType i) {
4651c0b2f7Stbbdev     switch (i % 3) {
4751c0b2f7Stbbdev         case 0: q.push( v); break;
4851c0b2f7Stbbdev         case 1: q.push( std::move(v)); break;
4951c0b2f7Stbbdev         case 2: q.emplace( v); break;
5051c0b2f7Stbbdev         default: CHECK(false); break;
5151c0b2f7Stbbdev     }
5251c0b2f7Stbbdev }
5351c0b2f7Stbbdev 
5451c0b2f7Stbbdev template<typename T>
55*49e08aacStbbdev class ConcQWithCapacity : public oneapi::tbb::concurrent_queue<T, test_allocator<T>> {
56*49e08aacStbbdev     using base_type = oneapi::tbb::concurrent_queue<T, test_allocator<T>>;
5751c0b2f7Stbbdev public:
5851c0b2f7Stbbdev     ConcQWithCapacity() : my_capacity( std::size_t(-1) / (sizeof(void*) + sizeof(T)) ) {}
5951c0b2f7Stbbdev     std::size_t size() const {
6051c0b2f7Stbbdev         return this->unsafe_size();
6151c0b2f7Stbbdev     }
6251c0b2f7Stbbdev 
6351c0b2f7Stbbdev     std::size_t capacity() const {
6451c0b2f7Stbbdev         return my_capacity;
6551c0b2f7Stbbdev     }
6651c0b2f7Stbbdev 
6751c0b2f7Stbbdev     void set_capacity( const std::size_t n ) {
6851c0b2f7Stbbdev         my_capacity = n;
6951c0b2f7Stbbdev     }
7051c0b2f7Stbbdev 
7151c0b2f7Stbbdev     bool try_push( const T& source ) {
7251c0b2f7Stbbdev         base_type::push( source);
7351c0b2f7Stbbdev         return source.get_serial() < my_capacity;
7451c0b2f7Stbbdev     }
7551c0b2f7Stbbdev 
7651c0b2f7Stbbdev     bool try_pop( T& dest ) {
7751c0b2f7Stbbdev         base_type::try_pop( dest);
7851c0b2f7Stbbdev         return dest.get_serial() < my_capacity;
7951c0b2f7Stbbdev     }
8051c0b2f7Stbbdev 
8151c0b2f7Stbbdev private:
8251c0b2f7Stbbdev     std::size_t my_capacity;
8351c0b2f7Stbbdev };
8451c0b2f7Stbbdev 
8551c0b2f7Stbbdev template<typename CQ, typename T>
8651c0b2f7Stbbdev void TestEmptyQueue() {
8751c0b2f7Stbbdev     const CQ queue;
8851c0b2f7Stbbdev     CHECK(queue.size() == 0);
8951c0b2f7Stbbdev     CHECK(queue.capacity()> 0);
9051c0b2f7Stbbdev     CHECK(size_t(queue.capacity())>= std::size_t(-1)/(sizeof(void*)+sizeof(T)));
9151c0b2f7Stbbdev }
9251c0b2f7Stbbdev 
9351c0b2f7Stbbdev void TestEmptiness() {
9451c0b2f7Stbbdev     TestEmptyQueue<ConcQWithCapacity<char>, char>();
9551c0b2f7Stbbdev     TestEmptyQueue<ConcQWithCapacity<move_support_tests::Foo>, move_support_tests::Foo>();
96*49e08aacStbbdev     TestEmptyQueue<oneapi::tbb::concurrent_bounded_queue<char, test_allocator<char>>, char>();
97*49e08aacStbbdev     TestEmptyQueue<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo,
9851c0b2f7Stbbdev            test_allocator<move_support_tests::Foo>>, move_support_tests::Foo>();
9951c0b2f7Stbbdev }
10051c0b2f7Stbbdev 
10151c0b2f7Stbbdev template<typename CQ, typename T>
10251c0b2f7Stbbdev void TestFullQueue() {
10351c0b2f7Stbbdev     using allocator_type = decltype(std::declval<CQ>().get_allocator());
10451c0b2f7Stbbdev 
10551c0b2f7Stbbdev     for (std::size_t n = 0; n < 100; ++n) {
10651c0b2f7Stbbdev         allocator_type::init_counters();
10751c0b2f7Stbbdev         {
10851c0b2f7Stbbdev             CQ queue;
10951c0b2f7Stbbdev             queue.set_capacity(n);
11051c0b2f7Stbbdev             for (std::size_t i = 0; i <= n; ++i) {
11151c0b2f7Stbbdev                 T f;
11251c0b2f7Stbbdev                 f.set_serial(i);
11351c0b2f7Stbbdev                 bool result = queue.try_push( f);
11451c0b2f7Stbbdev                 CHECK((result == (i < n)));
11551c0b2f7Stbbdev             }
11651c0b2f7Stbbdev 
11751c0b2f7Stbbdev             for (std::size_t i = 0; i <= n; ++i) {
11851c0b2f7Stbbdev                 T f;
11951c0b2f7Stbbdev                 bool result = queue.try_pop(f);
12051c0b2f7Stbbdev                 CHECK((result == (i < n)));
12151c0b2f7Stbbdev                 CHECK((result == 0 || f.get_serial() == i));
12251c0b2f7Stbbdev             }
12351c0b2f7Stbbdev         }
12451c0b2f7Stbbdev         CHECK(allocator_type::items_allocated == allocator_type::items_freed);
12551c0b2f7Stbbdev         CHECK(allocator_type::allocations == allocator_type::frees);
12651c0b2f7Stbbdev     }
12751c0b2f7Stbbdev }
12851c0b2f7Stbbdev 
12951c0b2f7Stbbdev void TestFullness() {
13051c0b2f7Stbbdev     TestFullQueue<ConcQWithCapacity<move_support_tests::Foo>, move_support_tests::Foo>();
131*49e08aacStbbdev     TestFullQueue<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, move_support_tests::Foo>();
13251c0b2f7Stbbdev }
13351c0b2f7Stbbdev 
13451c0b2f7Stbbdev template<typename CQ>
13551c0b2f7Stbbdev void TestClear() {
13651c0b2f7Stbbdev     using allocator_type = decltype(std::declval<CQ>().get_allocator());
13751c0b2f7Stbbdev     allocator_type::init_counters();
13851c0b2f7Stbbdev     const std::size_t n = 5;
13951c0b2f7Stbbdev 
14051c0b2f7Stbbdev     CQ queue;
14151c0b2f7Stbbdev     const std::size_t q_capacity = 10;
14251c0b2f7Stbbdev     queue.set_capacity(q_capacity);
14351c0b2f7Stbbdev 
14451c0b2f7Stbbdev     for (std::size_t i = 0; i < n; ++i) {
14551c0b2f7Stbbdev         move_support_tests::Foo f;
14651c0b2f7Stbbdev         f.set_serial(i);
14751c0b2f7Stbbdev         queue.push(f);
14851c0b2f7Stbbdev     }
14951c0b2f7Stbbdev 
15051c0b2f7Stbbdev     CHECK(queue.size() == n);
15151c0b2f7Stbbdev 
15251c0b2f7Stbbdev     queue.clear();
15351c0b2f7Stbbdev     CHECK(queue.size()==0);
15451c0b2f7Stbbdev     for (std::size_t i = 0; i < n; ++i) {
15551c0b2f7Stbbdev         move_support_tests::Foo f;
15651c0b2f7Stbbdev         f.set_serial(i);
15751c0b2f7Stbbdev         queue.push( f);
15851c0b2f7Stbbdev     }
15951c0b2f7Stbbdev 
16051c0b2f7Stbbdev     CHECK(queue.size() == n);
16151c0b2f7Stbbdev     queue.clear();
16251c0b2f7Stbbdev     CHECK(queue.size() == 0);
16351c0b2f7Stbbdev 
16451c0b2f7Stbbdev     for (std::size_t i = 0; i < n; ++i) {
16551c0b2f7Stbbdev         move_support_tests::Foo f;
16651c0b2f7Stbbdev         f.set_serial(i);
16751c0b2f7Stbbdev         queue.push(f);
16851c0b2f7Stbbdev     }
16951c0b2f7Stbbdev 
17051c0b2f7Stbbdev     CHECK(queue.size()==n);
17151c0b2f7Stbbdev }
17251c0b2f7Stbbdev 
17351c0b2f7Stbbdev void TestClearWorks() {
17451c0b2f7Stbbdev     TestClear<ConcQWithCapacity<move_support_tests::Foo>>();
175*49e08aacStbbdev     TestClear<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>();
17651c0b2f7Stbbdev }
17751c0b2f7Stbbdev 
17851c0b2f7Stbbdev template<typename Iterator1, typename Iterator2>
17951c0b2f7Stbbdev void TestIteratorAux( Iterator1 i, Iterator2 j, int size ) {
18051c0b2f7Stbbdev     Iterator1 old_i; // assigned at first iteration below
18151c0b2f7Stbbdev     for (std::size_t k = 0; k < (std::size_t)size; ++k) {
18251c0b2f7Stbbdev         CHECK(i != j);
18351c0b2f7Stbbdev         CHECK(!(i == j));
18451c0b2f7Stbbdev         // Test "->"
18551c0b2f7Stbbdev         CHECK((k+1 == i->get_serial()));
18651c0b2f7Stbbdev         if (k & 1) {
18751c0b2f7Stbbdev             // Test post-increment
18851c0b2f7Stbbdev             move_support_tests::Foo f = *old_i++;
18951c0b2f7Stbbdev             CHECK((k + 1 == f.get_serial()));
19051c0b2f7Stbbdev             // Test assignment
19151c0b2f7Stbbdev             i = old_i;
19251c0b2f7Stbbdev         } else {
19351c0b2f7Stbbdev             // Test pre-increment
19451c0b2f7Stbbdev             if (k < std::size_t(size - 1)) {
19551c0b2f7Stbbdev                 move_support_tests::Foo f = *++i;
19651c0b2f7Stbbdev                 CHECK((k + 2 == f.get_serial()));
19751c0b2f7Stbbdev             } else ++i;
19851c0b2f7Stbbdev             // Test assignment
19951c0b2f7Stbbdev             old_i = i;
20051c0b2f7Stbbdev         }
20151c0b2f7Stbbdev     }
20251c0b2f7Stbbdev     CHECK(!(i != j));
20351c0b2f7Stbbdev     CHECK(i == j);
20451c0b2f7Stbbdev }
20551c0b2f7Stbbdev 
20651c0b2f7Stbbdev template<typename Iterator1, typename Iterator2>
20751c0b2f7Stbbdev void TestIteratorAssignment( Iterator2 j ) {
20851c0b2f7Stbbdev     Iterator1 i(j);
20951c0b2f7Stbbdev     CHECK(i == j);
21051c0b2f7Stbbdev     CHECK(!(i != j));
21151c0b2f7Stbbdev 
21251c0b2f7Stbbdev     Iterator1 k;
21351c0b2f7Stbbdev     k = j;
21451c0b2f7Stbbdev     CHECK(k == j);
21551c0b2f7Stbbdev     CHECK(!(k != j));
21651c0b2f7Stbbdev }
21751c0b2f7Stbbdev 
21851c0b2f7Stbbdev template<typename Iterator, typename T>
21951c0b2f7Stbbdev void TestIteratorTraits() {
22051c0b2f7Stbbdev     static_assert( std::is_same<typename Iterator::iterator_category, std::forward_iterator_tag>::value, "wrong iterator category");
22151c0b2f7Stbbdev 
22251c0b2f7Stbbdev     T x;
22351c0b2f7Stbbdev 
22451c0b2f7Stbbdev     typename Iterator::reference xr = x;
22551c0b2f7Stbbdev     typename Iterator::pointer xp = &x;
22651c0b2f7Stbbdev     CHECK((&xr == xp));
22751c0b2f7Stbbdev }
22851c0b2f7Stbbdev 
22951c0b2f7Stbbdev // Test the iterators for concurrent_queue
23051c0b2f7Stbbdev template <typename CQ>
23151c0b2f7Stbbdev void TestIterator() {
23251c0b2f7Stbbdev     CQ queue;
23351c0b2f7Stbbdev     const CQ& const_queue = queue;
23451c0b2f7Stbbdev     for (int j=0; j < 500; ++j) {
23551c0b2f7Stbbdev         TestIteratorAux( queue.unsafe_begin()      , queue.unsafe_end()      , j);
23651c0b2f7Stbbdev         TestIteratorAux( queue.unsafe_cbegin()      , queue.unsafe_cend()      , j);
23751c0b2f7Stbbdev         TestIteratorAux( const_queue.unsafe_begin(), const_queue.unsafe_end(), j);
23851c0b2f7Stbbdev         TestIteratorAux( const_queue.unsafe_begin(), queue.unsafe_end()      , j);
23951c0b2f7Stbbdev         TestIteratorAux( queue.unsafe_begin()      , const_queue.unsafe_end(), j);
24051c0b2f7Stbbdev         move_support_tests::Foo f;
24151c0b2f7Stbbdev         f.set_serial(j+1);
24251c0b2f7Stbbdev         queue.push(f);
24351c0b2f7Stbbdev     }
24451c0b2f7Stbbdev     TestIteratorAssignment<typename CQ::const_iterator>( const_queue.unsafe_begin());
24551c0b2f7Stbbdev     TestIteratorAssignment<typename CQ::const_iterator>( queue.unsafe_begin());
24651c0b2f7Stbbdev     TestIteratorAssignment<typename CQ::iterator>( queue.unsafe_begin());
24751c0b2f7Stbbdev     TestIteratorTraits<typename CQ::const_iterator, const move_support_tests::Foo>();
24851c0b2f7Stbbdev     TestIteratorTraits<typename CQ::iterator, move_support_tests::Foo>();
24951c0b2f7Stbbdev }
25051c0b2f7Stbbdev 
25151c0b2f7Stbbdev void TestQueueIteratorWorks() {
252*49e08aacStbbdev     TestIterator<oneapi::tbb::concurrent_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>();
253*49e08aacStbbdev     TestIterator<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>();
25451c0b2f7Stbbdev }
25551c0b2f7Stbbdev 
256*49e08aacStbbdev // Define wrapper classes to test oneapi::tbb::concurrent_queue<T>
257*49e08aacStbbdev template<typename T, typename A = oneapi::tbb::cache_aligned_allocator<T>>
258*49e08aacStbbdev class ConcQWithSizeWrapper : public oneapi::tbb::concurrent_queue<T, A> {
25951c0b2f7Stbbdev public:
26051c0b2f7Stbbdev     ConcQWithSizeWrapper() {}
261*49e08aacStbbdev     ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q ) : oneapi::tbb::concurrent_queue<T, A>(q) {}
262*49e08aacStbbdev     ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q, const A& a ) : oneapi::tbb::concurrent_queue<T, A>(q, a) {}
263*49e08aacStbbdev     ConcQWithSizeWrapper( const A& a ) : oneapi::tbb::concurrent_queue<T, A>( a ) {}
26451c0b2f7Stbbdev 
265*49e08aacStbbdev     ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q ) : oneapi::tbb::concurrent_queue<T>(std::move(q)) {}
26651c0b2f7Stbbdev     ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q, const A& a )
267*49e08aacStbbdev         : oneapi::tbb::concurrent_queue<T, A>(std::move(q), a) { }
26851c0b2f7Stbbdev 
26951c0b2f7Stbbdev     template<typename InputIterator>
27051c0b2f7Stbbdev     ConcQWithSizeWrapper( InputIterator begin, InputIterator end, const A& a = A() )
271*49e08aacStbbdev         : oneapi::tbb::concurrent_queue<T, A>(begin, end, a) {}
272*49e08aacStbbdev     typename oneapi::tbb::concurrent_queue<T, A>::size_type size() const { return this->unsafe_size(); }
27351c0b2f7Stbbdev };
27451c0b2f7Stbbdev 
27551c0b2f7Stbbdev enum state_type {
27651c0b2f7Stbbdev     LIVE = 0x1234,
27751c0b2f7Stbbdev     DEAD = 0xDEAD
27851c0b2f7Stbbdev };
27951c0b2f7Stbbdev 
28051c0b2f7Stbbdev class Bar {
28151c0b2f7Stbbdev     state_type state;
28251c0b2f7Stbbdev public:
28351c0b2f7Stbbdev     static std::size_t construction_num, destruction_num;
28451c0b2f7Stbbdev     std::ptrdiff_t my_id;
28551c0b2f7Stbbdev     Bar() : state(LIVE), my_id(-1)
28651c0b2f7Stbbdev     {}
28751c0b2f7Stbbdev 
28851c0b2f7Stbbdev     Bar( std::size_t _i ) : state(LIVE), my_id(_i) { construction_num++; }
28951c0b2f7Stbbdev 
29051c0b2f7Stbbdev     Bar( const Bar& a_bar ) : state(LIVE) {
29151c0b2f7Stbbdev         CHECK(a_bar.state == LIVE);
29251c0b2f7Stbbdev         my_id = a_bar.my_id;
29351c0b2f7Stbbdev         construction_num++;
29451c0b2f7Stbbdev     }
29551c0b2f7Stbbdev 
29651c0b2f7Stbbdev     ~Bar() {
29751c0b2f7Stbbdev         CHECK(state == LIVE);
29851c0b2f7Stbbdev         state = DEAD;
29951c0b2f7Stbbdev         my_id = DEAD;
30051c0b2f7Stbbdev         destruction_num++;
30151c0b2f7Stbbdev     }
30251c0b2f7Stbbdev 
30351c0b2f7Stbbdev     void operator=( const Bar& a_bar ) {
30451c0b2f7Stbbdev         CHECK(a_bar.state == LIVE);
30551c0b2f7Stbbdev         CHECK(state == LIVE);
30651c0b2f7Stbbdev         my_id = a_bar.my_id;
30751c0b2f7Stbbdev     }
30851c0b2f7Stbbdev     friend bool operator==( const Bar& bar1, const Bar& bar2 ) ;
30951c0b2f7Stbbdev };
31051c0b2f7Stbbdev 
31151c0b2f7Stbbdev std::size_t Bar::construction_num = 0;
31251c0b2f7Stbbdev std::size_t Bar::destruction_num = 0;
31351c0b2f7Stbbdev 
31451c0b2f7Stbbdev bool operator==( const Bar& bar1, const Bar& bar2 ) {
31551c0b2f7Stbbdev     CHECK(bar1.state == LIVE);
31651c0b2f7Stbbdev     CHECK(bar2.state == LIVE);
31751c0b2f7Stbbdev     return bar1.my_id == bar2.my_id;
31851c0b2f7Stbbdev }
31951c0b2f7Stbbdev 
32051c0b2f7Stbbdev class BarIterator {
32151c0b2f7Stbbdev     Bar* bar_ptr;
32251c0b2f7Stbbdev     BarIterator(Bar* bp_) : bar_ptr(bp_) {}
32351c0b2f7Stbbdev public:
32451c0b2f7Stbbdev     Bar& operator*() const {
32551c0b2f7Stbbdev         return *bar_ptr;
32651c0b2f7Stbbdev     }
32751c0b2f7Stbbdev     BarIterator& operator++() {
32851c0b2f7Stbbdev         ++bar_ptr;
32951c0b2f7Stbbdev         return *this;
33051c0b2f7Stbbdev     }
33151c0b2f7Stbbdev     Bar* operator++(int) {
33251c0b2f7Stbbdev         Bar* result = &operator*();
33351c0b2f7Stbbdev         operator++();
33451c0b2f7Stbbdev         return result;
33551c0b2f7Stbbdev     }
33651c0b2f7Stbbdev     friend bool operator==(const BarIterator& bia, const BarIterator& bib) ;
33751c0b2f7Stbbdev     friend bool operator!=(const BarIterator& bia, const BarIterator& bib) ;
33851c0b2f7Stbbdev     template<typename CQ, typename T, typename TIter, typename CQ_EX, typename T_EX>
33951c0b2f7Stbbdev     friend void TestConstructors ();
34051c0b2f7Stbbdev } ;
34151c0b2f7Stbbdev 
34251c0b2f7Stbbdev bool operator==(const BarIterator& bia, const BarIterator& bib) {
34351c0b2f7Stbbdev     return bia.bar_ptr==bib.bar_ptr;
34451c0b2f7Stbbdev }
34551c0b2f7Stbbdev 
34651c0b2f7Stbbdev bool operator!=(const BarIterator& bia, const BarIterator& bib) {
34751c0b2f7Stbbdev     return bia.bar_ptr!=bib.bar_ptr;
34851c0b2f7Stbbdev }
34951c0b2f7Stbbdev 
35051c0b2f7Stbbdev 
35151c0b2f7Stbbdev class Bar_exception : public std::bad_alloc {
35251c0b2f7Stbbdev public:
35351c0b2f7Stbbdev     virtual const char *what() const noexcept override { return "making the entry invalid"; }
35451c0b2f7Stbbdev     virtual ~Bar_exception() noexcept {}
35551c0b2f7Stbbdev };
35651c0b2f7Stbbdev 
35751c0b2f7Stbbdev class BarEx {
35851c0b2f7Stbbdev     static int count;
35951c0b2f7Stbbdev public:
36051c0b2f7Stbbdev     state_type state;
36151c0b2f7Stbbdev     typedef enum {
36251c0b2f7Stbbdev         PREPARATION,
36351c0b2f7Stbbdev         COPY_CONSTRUCT
36451c0b2f7Stbbdev     } mode_type;
36551c0b2f7Stbbdev     static mode_type mode;
36651c0b2f7Stbbdev     std::ptrdiff_t my_id;
36751c0b2f7Stbbdev     std::ptrdiff_t my_tilda_id;
36851c0b2f7Stbbdev 
36951c0b2f7Stbbdev     static int button;
37051c0b2f7Stbbdev 
37151c0b2f7Stbbdev     BarEx() : state(LIVE), my_id(-1), my_tilda_id(-1)
37251c0b2f7Stbbdev     {}
37351c0b2f7Stbbdev 
37451c0b2f7Stbbdev     BarEx(std::size_t _i) : state(LIVE), my_id(_i), my_tilda_id(my_id^(-1))
37551c0b2f7Stbbdev     {}
37651c0b2f7Stbbdev 
37751c0b2f7Stbbdev     BarEx( const BarEx& a_bar ) : state(LIVE) {
37851c0b2f7Stbbdev         CHECK(a_bar.state==LIVE);
37951c0b2f7Stbbdev         my_id = a_bar.my_id;
38051c0b2f7Stbbdev         if (mode == PREPARATION)
38151c0b2f7Stbbdev             if (!(++count % 100)) {
38251c0b2f7Stbbdev                 TBB_TEST_THROW(Bar_exception());
38351c0b2f7Stbbdev             }
38451c0b2f7Stbbdev         my_tilda_id = a_bar.my_tilda_id;
38551c0b2f7Stbbdev     }
38651c0b2f7Stbbdev 
38751c0b2f7Stbbdev     ~BarEx() {
38851c0b2f7Stbbdev         CHECK(state == LIVE);
38951c0b2f7Stbbdev         state = DEAD;
39051c0b2f7Stbbdev         my_id = DEAD;
39151c0b2f7Stbbdev     }
39251c0b2f7Stbbdev     static void set_mode( mode_type m ) { mode = m; }
39351c0b2f7Stbbdev 
39451c0b2f7Stbbdev     void operator=( const BarEx& a_bar ) {
39551c0b2f7Stbbdev         CHECK(a_bar.state == LIVE);
39651c0b2f7Stbbdev         CHECK(state == LIVE);
39751c0b2f7Stbbdev         my_id = a_bar.my_id;
39851c0b2f7Stbbdev         my_tilda_id = a_bar.my_tilda_id;
39951c0b2f7Stbbdev     }
40051c0b2f7Stbbdev 
40151c0b2f7Stbbdev     friend bool operator==(const BarEx& bar1, const BarEx& bar2 ) ;
40251c0b2f7Stbbdev };
40351c0b2f7Stbbdev 
40451c0b2f7Stbbdev int BarEx::count = 0;
40551c0b2f7Stbbdev BarEx::mode_type BarEx::mode = BarEx::PREPARATION;
40651c0b2f7Stbbdev 
40751c0b2f7Stbbdev bool operator==(const BarEx& bar1, const BarEx& bar2) {
40851c0b2f7Stbbdev     CHECK(bar1.state == LIVE);
40951c0b2f7Stbbdev     CHECK(bar2.state == LIVE);
41051c0b2f7Stbbdev     CHECK(((bar1.my_id ^ bar1.my_tilda_id) == -1));
41151c0b2f7Stbbdev     CHECK(((bar2.my_id ^ bar2.my_tilda_id) == -1));
41251c0b2f7Stbbdev     return bar1.my_id == bar2.my_id && bar1.my_tilda_id == bar2.my_tilda_id;
41351c0b2f7Stbbdev }
41451c0b2f7Stbbdev 
41551c0b2f7Stbbdev template<typename CQ, typename T, typename TIter, typename CQ_EX, typename T_EX>
41651c0b2f7Stbbdev void TestConstructors () {
41751c0b2f7Stbbdev     CQ src_queue;
41851c0b2f7Stbbdev     typename CQ::const_iterator dqb;
41951c0b2f7Stbbdev     typename CQ::const_iterator dqe;
42051c0b2f7Stbbdev     typename CQ::const_iterator iter;
42151c0b2f7Stbbdev 
42251c0b2f7Stbbdev     for (std::size_t size = 0; size < 1001; ++size) {
42351c0b2f7Stbbdev         for (std::size_t i = 0; i < size; ++i)
42451c0b2f7Stbbdev             src_queue.push(T(i + (i ^ size)));
42551c0b2f7Stbbdev         typename CQ::const_iterator sqb( src_queue.unsafe_begin());
42651c0b2f7Stbbdev         typename CQ::const_iterator sqe( src_queue.unsafe_end()  );
42751c0b2f7Stbbdev 
42851c0b2f7Stbbdev         CQ dst_queue(sqb, sqe);
42951c0b2f7Stbbdev         CQ copy_with_alloc(src_queue, typename CQ::allocator_type());
43051c0b2f7Stbbdev 
43151c0b2f7Stbbdev         REQUIRE_MESSAGE(src_queue.size() == dst_queue.size(), "different size");
43251c0b2f7Stbbdev         REQUIRE_MESSAGE(src_queue.size() == copy_with_alloc.size(), "different size");
43351c0b2f7Stbbdev 
43451c0b2f7Stbbdev         src_queue.clear();
43551c0b2f7Stbbdev     }
43651c0b2f7Stbbdev 
43751c0b2f7Stbbdev     T bar_array[1001];
43851c0b2f7Stbbdev     for (std::size_t size=0; size < 1001; ++size) {
43951c0b2f7Stbbdev         for (std::size_t i=0; i < size; ++i) {
44051c0b2f7Stbbdev             bar_array[i] = T(i+(i^size));
44151c0b2f7Stbbdev         }
44251c0b2f7Stbbdev 
44351c0b2f7Stbbdev         const TIter sab(bar_array + 0);
44451c0b2f7Stbbdev         const TIter sae(bar_array + size);
44551c0b2f7Stbbdev 
44651c0b2f7Stbbdev         CQ dst_queue2(sab, sae);
44751c0b2f7Stbbdev 
44851c0b2f7Stbbdev         CHECK(size == dst_queue2.size());
44951c0b2f7Stbbdev         CHECK(sab == TIter(bar_array+0));
45051c0b2f7Stbbdev         CHECK(sae == TIter(bar_array+size));
45151c0b2f7Stbbdev 
45251c0b2f7Stbbdev         dqb = dst_queue2.unsafe_begin();
45351c0b2f7Stbbdev         dqe = dst_queue2.unsafe_end();
45451c0b2f7Stbbdev         TIter v_iter(sab);
45551c0b2f7Stbbdev         for (; dqb != dqe; ++dqb, ++v_iter) {
45651c0b2f7Stbbdev             REQUIRE_MESSAGE((*dqb == *v_iter), "unexpected element");
45751c0b2f7Stbbdev         }
45851c0b2f7Stbbdev 
45951c0b2f7Stbbdev         REQUIRE_MESSAGE(v_iter==sae, "different size?");
46051c0b2f7Stbbdev     }
46151c0b2f7Stbbdev 
46251c0b2f7Stbbdev     src_queue.clear();
46351c0b2f7Stbbdev 
46451c0b2f7Stbbdev     CQ dst_queue3(src_queue);
46551c0b2f7Stbbdev     CHECK(src_queue.size() == dst_queue3.size());
46651c0b2f7Stbbdev     CHECK(0 == dst_queue3.size());
46751c0b2f7Stbbdev 
46851c0b2f7Stbbdev     int k = 0;
46951c0b2f7Stbbdev     for (std::size_t i = 0; i < 1001; ++i) {
47051c0b2f7Stbbdev         T tmp_bar;
47151c0b2f7Stbbdev         src_queue.push(T(++k));
47251c0b2f7Stbbdev         src_queue.push(T(++k));
47351c0b2f7Stbbdev         src_queue.try_pop(tmp_bar);
47451c0b2f7Stbbdev 
47551c0b2f7Stbbdev         CQ dst_queue4( src_queue);
47651c0b2f7Stbbdev 
47751c0b2f7Stbbdev         CHECK(src_queue.size() == dst_queue4.size());
47851c0b2f7Stbbdev 
47951c0b2f7Stbbdev         dqb = dst_queue4.unsafe_begin();
48051c0b2f7Stbbdev         dqe = dst_queue4.unsafe_end();
48151c0b2f7Stbbdev         iter = src_queue.unsafe_begin();
48251c0b2f7Stbbdev 
48351c0b2f7Stbbdev         for (; dqb != dqe; ++dqb, ++iter) {
48451c0b2f7Stbbdev             REQUIRE_MESSAGE((*dqb == *iter), "unexpected element");
48551c0b2f7Stbbdev         }
48651c0b2f7Stbbdev 
48751c0b2f7Stbbdev         REQUIRE_MESSAGE(iter == src_queue.unsafe_end(), "different size?");
48851c0b2f7Stbbdev     }
48951c0b2f7Stbbdev 
49051c0b2f7Stbbdev     CQ dst_queue5(src_queue);
49151c0b2f7Stbbdev 
49251c0b2f7Stbbdev     CHECK(src_queue.size() == dst_queue5.size());
49351c0b2f7Stbbdev     dqb = dst_queue5.unsafe_begin();
49451c0b2f7Stbbdev     dqe = dst_queue5.unsafe_end();
49551c0b2f7Stbbdev     iter = src_queue.unsafe_begin();
49651c0b2f7Stbbdev     for (; dqb != dqe; ++dqb, ++iter) {
49751c0b2f7Stbbdev         REQUIRE_MESSAGE(*dqb == *iter, "unexpected element");
49851c0b2f7Stbbdev     }
49951c0b2f7Stbbdev 
50051c0b2f7Stbbdev     for (std::size_t i=0; i<100; ++i) {
50151c0b2f7Stbbdev         T tmp_bar;
50251c0b2f7Stbbdev         src_queue.push(T(i + 1000));
50351c0b2f7Stbbdev         src_queue.push(T(i + 1000));
50451c0b2f7Stbbdev         src_queue.try_pop(tmp_bar);
50551c0b2f7Stbbdev 
50651c0b2f7Stbbdev         dst_queue5.push(T(i + 1000));
50751c0b2f7Stbbdev         dst_queue5.push(T(i + 1000));
50851c0b2f7Stbbdev         dst_queue5.try_pop(tmp_bar);
50951c0b2f7Stbbdev     }
51051c0b2f7Stbbdev 
51151c0b2f7Stbbdev     CHECK(src_queue.size() == dst_queue5.size());
51251c0b2f7Stbbdev     dqb = dst_queue5.unsafe_begin();
51351c0b2f7Stbbdev     dqe = dst_queue5.unsafe_end();
51451c0b2f7Stbbdev     iter = src_queue.unsafe_begin();
51551c0b2f7Stbbdev     for (; dqb != dqe; ++dqb, ++iter) {
51651c0b2f7Stbbdev         REQUIRE_MESSAGE((*dqb == *iter), "unexpected element");
51751c0b2f7Stbbdev     }
51851c0b2f7Stbbdev 
51951c0b2f7Stbbdev     REQUIRE_MESSAGE(iter == src_queue.unsafe_end(), "different size?");
52051c0b2f7Stbbdev 
52151c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
52251c0b2f7Stbbdev     k = 0;
52351c0b2f7Stbbdev     typename CQ_EX::size_type n_elements = 0;
52451c0b2f7Stbbdev     CQ_EX src_queue_ex;
52551c0b2f7Stbbdev     for (std::size_t size = 0; size < 1001; ++size) {
52651c0b2f7Stbbdev         T_EX tmp_bar_ex;
52751c0b2f7Stbbdev         typename CQ_EX::size_type n_successful_pushes = 0;
52851c0b2f7Stbbdev         T_EX::set_mode(T_EX::PREPARATION);
52951c0b2f7Stbbdev         try {
53051c0b2f7Stbbdev             src_queue_ex.push(T_EX(k + (k ^ size)));
53151c0b2f7Stbbdev             ++n_successful_pushes;
53251c0b2f7Stbbdev         } catch (...) {
53351c0b2f7Stbbdev         }
53451c0b2f7Stbbdev         ++k;
53551c0b2f7Stbbdev         try {
53651c0b2f7Stbbdev             src_queue_ex.push(T_EX(k + (k ^ size)));
53751c0b2f7Stbbdev             ++n_successful_pushes;
53851c0b2f7Stbbdev         } catch (...) {
53951c0b2f7Stbbdev         }
54051c0b2f7Stbbdev         ++k;
54151c0b2f7Stbbdev         src_queue_ex.try_pop(tmp_bar_ex);
54251c0b2f7Stbbdev         n_elements += (n_successful_pushes - 1);
54351c0b2f7Stbbdev         CHECK(src_queue_ex.size() == n_elements);
54451c0b2f7Stbbdev 
54551c0b2f7Stbbdev         T_EX::set_mode(T_EX::COPY_CONSTRUCT);
54651c0b2f7Stbbdev         CQ_EX dst_queue_ex(src_queue_ex);
54751c0b2f7Stbbdev 
54851c0b2f7Stbbdev         CHECK(src_queue_ex.size() == dst_queue_ex.size());
54951c0b2f7Stbbdev 
55051c0b2f7Stbbdev         typename CQ_EX::const_iterator dqb_ex = dst_queue_ex.unsafe_begin();
55151c0b2f7Stbbdev         typename CQ_EX::const_iterator dqe_ex = dst_queue_ex.unsafe_end();
55251c0b2f7Stbbdev         typename CQ_EX::const_iterator iter_ex = src_queue_ex.unsafe_begin();
55351c0b2f7Stbbdev 
55451c0b2f7Stbbdev         for (; dqb_ex != dqe_ex; ++dqb_ex, ++iter_ex) {
55551c0b2f7Stbbdev             REQUIRE_MESSAGE(*dqb_ex == *iter_ex, "unexpected element");
55651c0b2f7Stbbdev         }
55751c0b2f7Stbbdev 
55851c0b2f7Stbbdev         REQUIRE_MESSAGE(iter_ex==src_queue_ex.unsafe_end(), "different size?");
55951c0b2f7Stbbdev     }
56051c0b2f7Stbbdev #endif
56151c0b2f7Stbbdev     src_queue.clear();
56251c0b2f7Stbbdev 
56351c0b2f7Stbbdev     using qsize_t = typename CQ::size_type;
56451c0b2f7Stbbdev     for (qsize_t size = 0; size < 1001; ++size) {
56551c0b2f7Stbbdev         for (qsize_t i = 0; i < size; ++i) {
56651c0b2f7Stbbdev             src_queue.push(T(i + (i ^ size)));
56751c0b2f7Stbbdev         }
56851c0b2f7Stbbdev         std::vector<const T*> locations(size);
56951c0b2f7Stbbdev         typename CQ::const_iterator qit = src_queue.unsafe_begin();
57051c0b2f7Stbbdev         for (qsize_t i = 0; i < size; ++i, ++qit) {
57151c0b2f7Stbbdev             locations[i] = &(*qit);
57251c0b2f7Stbbdev         }
57351c0b2f7Stbbdev 
57451c0b2f7Stbbdev         qsize_t size_of_queue = src_queue.size();
57551c0b2f7Stbbdev         CQ dst_queue(std::move(src_queue));
57651c0b2f7Stbbdev 
57751c0b2f7Stbbdev         REQUIRE_MESSAGE((src_queue.empty() && src_queue.size() == 0), "not working move constructor?");
57851c0b2f7Stbbdev         REQUIRE_MESSAGE((size == size_of_queue && size_of_queue == qsize_t(dst_queue.size())), "not working move constructor?");
57951c0b2f7Stbbdev 
58051c0b2f7Stbbdev         qit = dst_queue.unsafe_begin();
58151c0b2f7Stbbdev         for (qsize_t i = 0; i < size; ++i, ++qit) {
58251c0b2f7Stbbdev             REQUIRE_MESSAGE(locations[i] == &(*qit), "there was data movement during move constructor");
58351c0b2f7Stbbdev         }
58451c0b2f7Stbbdev 
58551c0b2f7Stbbdev         for (qsize_t i = 0; i < size; ++i) {
58651c0b2f7Stbbdev             T test(i + (i ^ size));
58751c0b2f7Stbbdev             T popped;
58851c0b2f7Stbbdev             bool pop_result = dst_queue.try_pop( popped);
58951c0b2f7Stbbdev 
59051c0b2f7Stbbdev             CHECK(pop_result);
59151c0b2f7Stbbdev             CHECK(test == popped);
59251c0b2f7Stbbdev         }
59351c0b2f7Stbbdev     }
59451c0b2f7Stbbdev }
59551c0b2f7Stbbdev 
59651c0b2f7Stbbdev void TestQueueConstructors() {
59751c0b2f7Stbbdev     TestConstructors<ConcQWithSizeWrapper<Bar>, Bar, BarIterator, ConcQWithSizeWrapper<BarEx>, BarEx>();
598*49e08aacStbbdev     TestConstructors<oneapi::tbb::concurrent_bounded_queue<Bar>, Bar, BarIterator, oneapi::tbb::concurrent_bounded_queue<BarEx>, BarEx>();
59951c0b2f7Stbbdev }
60051c0b2f7Stbbdev 
60151c0b2f7Stbbdev template<typename T>
60251c0b2f7Stbbdev struct TestNegativeQueueBody {
603*49e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<T>& queue;
60451c0b2f7Stbbdev     const std::size_t nthread;
605*49e08aacStbbdev     TestNegativeQueueBody( oneapi::tbb::concurrent_bounded_queue<T>& q, std::size_t n ) : queue(q), nthread(n) {}
60651c0b2f7Stbbdev     void operator()( std::size_t k ) const {
60751c0b2f7Stbbdev         if (k == 0) {
60851c0b2f7Stbbdev             int number_of_pops = int(nthread) - 1;
60951c0b2f7Stbbdev             // Wait for all pops to pend.
61051c0b2f7Stbbdev             while (int(queue.size())> -number_of_pops) {
61151c0b2f7Stbbdev                 std::this_thread::yield();
61251c0b2f7Stbbdev             }
61351c0b2f7Stbbdev 
61451c0b2f7Stbbdev             for (int i = 0; ; ++i) {
61551c0b2f7Stbbdev                 CHECK(queue.size() == std::size_t(i - number_of_pops));
61651c0b2f7Stbbdev                 CHECK((queue.empty() == (queue.size() <= 0)));
61751c0b2f7Stbbdev                 if (i == number_of_pops) break;
61851c0b2f7Stbbdev                 // Satisfy another pop
61951c0b2f7Stbbdev                 queue.push(T());
62051c0b2f7Stbbdev             }
62151c0b2f7Stbbdev         } else {
62251c0b2f7Stbbdev             // Pop item from queue
62351c0b2f7Stbbdev             T item;
62451c0b2f7Stbbdev             queue.pop(item);
62551c0b2f7Stbbdev         }
62651c0b2f7Stbbdev     }
62751c0b2f7Stbbdev };
62851c0b2f7Stbbdev 
62951c0b2f7Stbbdev //! Test a queue with a negative size.
63051c0b2f7Stbbdev template<typename T>
63151c0b2f7Stbbdev void TestNegativeQueue( std::size_t nthread ) {
632*49e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<T> queue;
63351c0b2f7Stbbdev     utils::NativeParallelFor( nthread, TestNegativeQueueBody<T>(queue,nthread));
63451c0b2f7Stbbdev }
63551c0b2f7Stbbdev 
63651c0b2f7Stbbdev template<typename T>
637*49e08aacStbbdev class ConcQPushPopWrapper : public oneapi::tbb::concurrent_queue<T, test_allocator<T>> {
63851c0b2f7Stbbdev public:
63951c0b2f7Stbbdev     ConcQPushPopWrapper() : my_capacity(std::size_t(-1) / (sizeof(void*) + sizeof(T)))
64051c0b2f7Stbbdev     {}
64151c0b2f7Stbbdev 
64251c0b2f7Stbbdev     std::size_t size() const { return this->unsafe_size(); }
64351c0b2f7Stbbdev     void set_capacity( const ptrdiff_t n ) { my_capacity = n; }
64451c0b2f7Stbbdev     bool try_push( const T& source ) { return this->push( source); }
645*49e08aacStbbdev     bool try_pop( T& dest ) { return this->oneapi::tbb::concurrent_queue<T, test_allocator<T>>::try_pop(dest); }
64651c0b2f7Stbbdev     std::size_t my_capacity;
64751c0b2f7Stbbdev };
64851c0b2f7Stbbdev 
64951c0b2f7Stbbdev template<typename CQ, typename T>
65051c0b2f7Stbbdev struct Body {
65151c0b2f7Stbbdev     CQ* queue;
65251c0b2f7Stbbdev     const std::size_t nthread;
65351c0b2f7Stbbdev     Body( std::size_t nthread_ ) : nthread(nthread_) {}
65451c0b2f7Stbbdev     void operator()( std::size_t thread_id ) const {
65551c0b2f7Stbbdev         long pop_kind[3] = {0, 0, 0};
65651c0b2f7Stbbdev         std::size_t serial[MAXTHREAD + 1];
65751c0b2f7Stbbdev         memset(serial, 0, nthread * sizeof(std::size_t));
65851c0b2f7Stbbdev         CHECK(thread_id < nthread);
65951c0b2f7Stbbdev 
66051c0b2f7Stbbdev         long sum = 0;
66151c0b2f7Stbbdev         for (std::size_t j = 0; j < M; ++j) {
66251c0b2f7Stbbdev             T f;
66351c0b2f7Stbbdev             f.set_thread_id(move_support_tests::serial_dead_state);
66451c0b2f7Stbbdev             f.set_serial(move_support_tests::serial_dead_state);
66551c0b2f7Stbbdev             bool prepopped = false;
66651c0b2f7Stbbdev             if (j & 1) {
66751c0b2f7Stbbdev                 prepopped = queue->try_pop(f);
66851c0b2f7Stbbdev                 ++pop_kind[prepopped];
66951c0b2f7Stbbdev             }
67051c0b2f7Stbbdev             T g;
67151c0b2f7Stbbdev             g.set_thread_id(thread_id);
67251c0b2f7Stbbdev             g.set_serial(j + 1);
67351c0b2f7Stbbdev             push(*queue, g, j);
67451c0b2f7Stbbdev             if (!prepopped) {
67551c0b2f7Stbbdev                 while(!(queue)->try_pop(f)) std::this_thread::yield();
67651c0b2f7Stbbdev                 ++pop_kind[2];
67751c0b2f7Stbbdev             }
67851c0b2f7Stbbdev             CHECK(f.get_thread_id() <= nthread);
67951c0b2f7Stbbdev             REQUIRE_MESSAGE((f.get_thread_id() == nthread || serial[f.get_thread_id()] < f.get_serial()), "partial order violation");
68051c0b2f7Stbbdev             serial[f.get_thread_id()] = f.get_serial();
68151c0b2f7Stbbdev             sum += int(f.get_serial() - 1);
68251c0b2f7Stbbdev         }
68351c0b2f7Stbbdev         Sum[thread_id] = sum;
68451c0b2f7Stbbdev         for (std::size_t k = 0; k < 3; ++k)
68551c0b2f7Stbbdev             PopKind[k] += pop_kind[k];
68651c0b2f7Stbbdev     }
68751c0b2f7Stbbdev };
68851c0b2f7Stbbdev 
68951c0b2f7Stbbdev template<typename CQ, typename T>
69051c0b2f7Stbbdev void TestPushPop( std::size_t prefill, std::ptrdiff_t capacity, std::size_t nthread ) {
69151c0b2f7Stbbdev     using allocator_type = decltype(std::declval<CQ>().get_allocator());
69251c0b2f7Stbbdev     CHECK(nthread> 0);
69351c0b2f7Stbbdev     std::ptrdiff_t signed_prefill = std::ptrdiff_t(prefill);
69451c0b2f7Stbbdev 
69551c0b2f7Stbbdev     if (signed_prefill + 1>= capacity) {
69651c0b2f7Stbbdev         return;
69751c0b2f7Stbbdev     }
69851c0b2f7Stbbdev 
69951c0b2f7Stbbdev     bool success = false;
70051c0b2f7Stbbdev     for (std::size_t k=0; k < 3; ++k) {
70151c0b2f7Stbbdev         PopKind[k] = 0;
70251c0b2f7Stbbdev     }
70351c0b2f7Stbbdev 
70451c0b2f7Stbbdev     for (std::size_t trial = 0; !success; ++trial) {
70551c0b2f7Stbbdev         allocator_type::init_counters();
70651c0b2f7Stbbdev         Body<CQ,T> body(nthread);
70751c0b2f7Stbbdev         CQ queue;
70851c0b2f7Stbbdev         queue.set_capacity(capacity);
70951c0b2f7Stbbdev         body.queue = &queue;
71051c0b2f7Stbbdev         for (std::size_t i = 0; i < prefill; ++i) {
71151c0b2f7Stbbdev             T f;
71251c0b2f7Stbbdev             f.set_thread_id(nthread);
71351c0b2f7Stbbdev             f.set_serial(1 + i);
71451c0b2f7Stbbdev             push(queue, f, i);
71551c0b2f7Stbbdev             CHECK(queue.size() == i + 1);
71651c0b2f7Stbbdev             CHECK(!queue.empty());
71751c0b2f7Stbbdev         }
71851c0b2f7Stbbdev 
71951c0b2f7Stbbdev         utils::NativeParallelFor( nthread, body);
72051c0b2f7Stbbdev 
72151c0b2f7Stbbdev         int sum = 0;
72251c0b2f7Stbbdev         for (std::size_t k = 0; k < nthread; ++k) {
72351c0b2f7Stbbdev             sum += Sum[k];
72451c0b2f7Stbbdev         }
72551c0b2f7Stbbdev 
72651c0b2f7Stbbdev         int expected = int( nthread * ((M - 1) * M / 2) + ((prefill - 1) * prefill) / 2);
72751c0b2f7Stbbdev         for (int i = int(prefill); --i>=0;) {
72851c0b2f7Stbbdev             CHECK(!queue.empty());
72951c0b2f7Stbbdev             T f;
73051c0b2f7Stbbdev             bool result = queue.try_pop(f);
73151c0b2f7Stbbdev             CHECK(result);
73251c0b2f7Stbbdev             CHECK(int(queue.size()) == i);
73351c0b2f7Stbbdev             sum += int(f.get_serial()) - 1;
73451c0b2f7Stbbdev         }
73551c0b2f7Stbbdev         REQUIRE_MESSAGE(queue.empty(), "The queue should be empty");
73651c0b2f7Stbbdev         REQUIRE_MESSAGE(queue.size() == 0, "The queue should have zero size");
73751c0b2f7Stbbdev         if (sum != expected) {
73851c0b2f7Stbbdev             REPORT("sum=%d expected=%d\n",sum,expected);
73951c0b2f7Stbbdev         }
74051c0b2f7Stbbdev 
74151c0b2f7Stbbdev         success = true;
74251c0b2f7Stbbdev         if (nthread> 1 && prefill == 0) {
74351c0b2f7Stbbdev             // Check that pop_if_present got sufficient exercise
74451c0b2f7Stbbdev             for (std::size_t k = 0; k < 2; ++k) {
74551c0b2f7Stbbdev                 const int min_requirement = 100;
74651c0b2f7Stbbdev                 const int max_trial = 20;
74751c0b2f7Stbbdev 
74851c0b2f7Stbbdev                 if (PopKind[k] < min_requirement) {
74951c0b2f7Stbbdev                     if (trial>= max_trial) {
75051c0b2f7Stbbdev                         REPORT("Warning: %d threads had only %ld pop_if_present operations %s after %d trials (expected at least %d). "
75151c0b2f7Stbbdev                             "This problem may merely be unlucky scheduling. "
75251c0b2f7Stbbdev                             "Investigate only if it happens repeatedly.\n",
75351c0b2f7Stbbdev                             nthread, long(PopKind[k]), k==0?"failed":"succeeded", max_trial, min_requirement);
75451c0b2f7Stbbdev                     } else {
75551c0b2f7Stbbdev                         success = false;
75651c0b2f7Stbbdev                     }
75751c0b2f7Stbbdev                }
75851c0b2f7Stbbdev             }
75951c0b2f7Stbbdev         }
76051c0b2f7Stbbdev     }
76151c0b2f7Stbbdev }
76251c0b2f7Stbbdev 
76351c0b2f7Stbbdev void TestConcurrentPushPop() {
76451c0b2f7Stbbdev     for (std::size_t nthread = MinThread; nthread <= MaxThread; ++nthread) {
76551c0b2f7Stbbdev         INFO(" Testing with "<< nthread << " thread(s)");
76651c0b2f7Stbbdev         TestNegativeQueue<move_support_tests::Foo>(nthread);
76751c0b2f7Stbbdev         for (std::size_t prefill=0; prefill < 64; prefill += (1 + prefill / 3)) {
76851c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(-1), nthread);
76951c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(1), nthread);
77051c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(2), nthread);
77151c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(10), nthread);
77251c0b2f7Stbbdev             TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(100), nthread);
77351c0b2f7Stbbdev         }
77451c0b2f7Stbbdev         for (std::size_t prefill = 0; prefill < 64; prefill += (1 + prefill / 3) ) {
775*49e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
77651c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(-1), nthread);
777*49e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
77851c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(1), nthread);
779*49e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
78051c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(2), nthread);
781*49e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
78251c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(10), nthread);
783*49e08aacStbbdev             TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>,
78451c0b2f7Stbbdev                 move_support_tests::Foo>(prefill, std::ptrdiff_t(100), nthread);
78551c0b2f7Stbbdev         }
78651c0b2f7Stbbdev     }
78751c0b2f7Stbbdev }
78851c0b2f7Stbbdev 
78951c0b2f7Stbbdev class Foo_exception : public std::bad_alloc {
79051c0b2f7Stbbdev public:
79151c0b2f7Stbbdev     virtual const char *what() const throw() override { return "out of Foo limit"; }
79251c0b2f7Stbbdev     virtual ~Foo_exception() throw() {}
79351c0b2f7Stbbdev };
79451c0b2f7Stbbdev 
79551c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
79651c0b2f7Stbbdev static std::atomic<long> FooExConstructed;
79751c0b2f7Stbbdev static std::atomic<long> FooExDestroyed;
79851c0b2f7Stbbdev static std::atomic<long> serial_source;
79951c0b2f7Stbbdev static long MaxFooCount = 0;
80051c0b2f7Stbbdev static const long Threshold = 400;
80151c0b2f7Stbbdev 
80251c0b2f7Stbbdev class FooEx {
80351c0b2f7Stbbdev     state_type state;
80451c0b2f7Stbbdev public:
80551c0b2f7Stbbdev     int serial;
80651c0b2f7Stbbdev     FooEx() : state(LIVE) {
80751c0b2f7Stbbdev         ++FooExConstructed;
80851c0b2f7Stbbdev         serial = serial_source++;
80951c0b2f7Stbbdev     }
81051c0b2f7Stbbdev 
81151c0b2f7Stbbdev     FooEx( const FooEx& item ) : state(LIVE) {
81251c0b2f7Stbbdev         CHECK(item.state == LIVE);
81351c0b2f7Stbbdev         ++FooExConstructed;
81451c0b2f7Stbbdev         if (MaxFooCount && (FooExConstructed - FooExDestroyed) >= MaxFooCount) { // in push()
81551c0b2f7Stbbdev             throw Foo_exception();
81651c0b2f7Stbbdev         }
81751c0b2f7Stbbdev         serial = item.serial;
81851c0b2f7Stbbdev     }
81951c0b2f7Stbbdev 
82051c0b2f7Stbbdev     ~FooEx() {
82151c0b2f7Stbbdev         CHECK(state==LIVE);
82251c0b2f7Stbbdev         ++FooExDestroyed;
82351c0b2f7Stbbdev         state=DEAD;
82451c0b2f7Stbbdev         serial=DEAD;
82551c0b2f7Stbbdev     }
82651c0b2f7Stbbdev 
82751c0b2f7Stbbdev     void operator=( FooEx& item ) {
82851c0b2f7Stbbdev         CHECK(item.state==LIVE);
82951c0b2f7Stbbdev         CHECK(state==LIVE);
83051c0b2f7Stbbdev         serial = item.serial;
83151c0b2f7Stbbdev         if( MaxFooCount==2*Threshold && (FooExConstructed-FooExDestroyed) <= MaxFooCount/4 ) // in pop()
83251c0b2f7Stbbdev             throw Foo_exception();
83351c0b2f7Stbbdev     }
83451c0b2f7Stbbdev 
83551c0b2f7Stbbdev     void operator=( FooEx&& item ) {
83651c0b2f7Stbbdev         operator=( item );
83751c0b2f7Stbbdev         item.serial = 0;
83851c0b2f7Stbbdev     }
83951c0b2f7Stbbdev 
84051c0b2f7Stbbdev };
84151c0b2f7Stbbdev 
84251c0b2f7Stbbdev template <template <typename, typename> class CQ, typename A1, typename A2, typename T>
84351c0b2f7Stbbdev void TestExceptionBody() {
84451c0b2f7Stbbdev     enum methods {
84551c0b2f7Stbbdev         m_push = 0,
84651c0b2f7Stbbdev         m_pop
84751c0b2f7Stbbdev     };
84851c0b2f7Stbbdev 
84951c0b2f7Stbbdev     const int N = 1000;     // # of bytes
85051c0b2f7Stbbdev 
85151c0b2f7Stbbdev     MaxFooCount = 5;
85251c0b2f7Stbbdev 
85351c0b2f7Stbbdev     try {
85451c0b2f7Stbbdev         int n_pushed=0, n_popped=0;
85551c0b2f7Stbbdev         for(int t = 0; t <= 1; t++)// exception type -- 0 : from allocator(), 1 : from Foo's constructor
85651c0b2f7Stbbdev         {
85751c0b2f7Stbbdev             CQ<T,A1> queue_test;
85851c0b2f7Stbbdev             for( int m=m_push; m<=m_pop; m++ ) {
85951c0b2f7Stbbdev                 // concurrent_queue internally rebinds the allocator to the one for 'char'
86051c0b2f7Stbbdev                 A2::init_counters();
86151c0b2f7Stbbdev 
86251c0b2f7Stbbdev                 if(t) MaxFooCount = MaxFooCount + 400;
86351c0b2f7Stbbdev                 else A2::set_limits(N/2);
86451c0b2f7Stbbdev 
86551c0b2f7Stbbdev                 try {
86651c0b2f7Stbbdev                     switch(m) {
86751c0b2f7Stbbdev                     case m_push:
86851c0b2f7Stbbdev                         for( int k=0; k<N; k++ ) {
86951c0b2f7Stbbdev                             push( queue_test, T(), k);
87051c0b2f7Stbbdev                             n_pushed++;
87151c0b2f7Stbbdev                         }
87251c0b2f7Stbbdev                         break;
87351c0b2f7Stbbdev                     case m_pop:
87451c0b2f7Stbbdev                         n_popped=0;
87551c0b2f7Stbbdev                         for( int k=0; k<n_pushed; k++ ) {
87651c0b2f7Stbbdev                             T elt;
87751c0b2f7Stbbdev                             queue_test.try_pop( elt);
87851c0b2f7Stbbdev                             n_popped++;
87951c0b2f7Stbbdev                         }
88051c0b2f7Stbbdev                         n_pushed = 0;
88151c0b2f7Stbbdev                         A2::set_limits();
88251c0b2f7Stbbdev                         break;
88351c0b2f7Stbbdev                     }
88451c0b2f7Stbbdev                     if( !t && m==m_push ) REQUIRE_MESSAGE(false, "should throw an exception");
88551c0b2f7Stbbdev                 } catch ( Foo_exception & ) {
88651c0b2f7Stbbdev                     long tc = MaxFooCount;
88751c0b2f7Stbbdev                     MaxFooCount = 0; // disable exception
88851c0b2f7Stbbdev                     switch(m) {
88951c0b2f7Stbbdev                     case m_push:
89051c0b2f7Stbbdev                         REQUIRE_MESSAGE(ptrdiff_t(queue_test.size())==n_pushed, "incorrect queue size");
89151c0b2f7Stbbdev                         for( int k=0; k<(int)tc; k++ ) {
89251c0b2f7Stbbdev                             push( queue_test, T(), k);
89351c0b2f7Stbbdev                             n_pushed++;
89451c0b2f7Stbbdev                         }
89551c0b2f7Stbbdev                         break;
89651c0b2f7Stbbdev                     case m_pop:
89751c0b2f7Stbbdev                         n_pushed -= (n_popped+1); // including one that threw the exception
89851c0b2f7Stbbdev                         REQUIRE_MESSAGE(n_pushed>=0, "n_pushed cannot be less than 0");
89951c0b2f7Stbbdev                         for( int k=0; k<1000; k++ ) {
90051c0b2f7Stbbdev                             push( queue_test, T(), k);
90151c0b2f7Stbbdev                             n_pushed++;
90251c0b2f7Stbbdev                         }
90351c0b2f7Stbbdev                         REQUIRE_MESSAGE(!queue_test.empty(), "queue must not be empty");
90451c0b2f7Stbbdev                         REQUIRE_MESSAGE(ptrdiff_t(queue_test.size())==n_pushed, "queue size must be equal to n pushed");
90551c0b2f7Stbbdev                         for( int k=0; k<n_pushed; k++ ) {
90651c0b2f7Stbbdev                             T elt;
90751c0b2f7Stbbdev                             queue_test.try_pop( elt);
90851c0b2f7Stbbdev                         }
90951c0b2f7Stbbdev                         REQUIRE_MESSAGE(queue_test.empty(), "queue must be empty");
91051c0b2f7Stbbdev                         REQUIRE_MESSAGE(queue_test.size()==0, "queue must be empty");
91151c0b2f7Stbbdev                         break;
91251c0b2f7Stbbdev                     }
91351c0b2f7Stbbdev                     MaxFooCount = tc;
91451c0b2f7Stbbdev                 } catch ( std::bad_alloc & ) {
91551c0b2f7Stbbdev                     A2::set_limits(); // disable exception from allocator
91651c0b2f7Stbbdev                     std::size_t size = queue_test.size();
91751c0b2f7Stbbdev                     switch(m) {
91851c0b2f7Stbbdev                         case m_push:
91951c0b2f7Stbbdev                             REQUIRE_MESSAGE(size>0, "incorrect queue size");
92051c0b2f7Stbbdev                             break;
92151c0b2f7Stbbdev                         case m_pop:
92251c0b2f7Stbbdev                             if( !t ) REQUIRE_MESSAGE(false, "should not throw an exception");
92351c0b2f7Stbbdev                             break;
92451c0b2f7Stbbdev                     }
92551c0b2f7Stbbdev                 }
92651c0b2f7Stbbdev                 INFO("for t= " << t << "and m= " << m << " exception test passed");
92751c0b2f7Stbbdev             }
92851c0b2f7Stbbdev         }
92951c0b2f7Stbbdev     } catch(...) {
93051c0b2f7Stbbdev         REQUIRE_MESSAGE(false, "unexpected exception");
93151c0b2f7Stbbdev     }
93251c0b2f7Stbbdev }
93351c0b2f7Stbbdev 
93451c0b2f7Stbbdev void TestExceptions() {
935*49e08aacStbbdev     using allocator_t = StaticSharedCountingAllocator<oneapi::tbb::cache_aligned_allocator<std::size_t>>;
936*49e08aacStbbdev     using allocator_char_t = StaticSharedCountingAllocator<oneapi::tbb::cache_aligned_allocator<char>>;
93751c0b2f7Stbbdev     TestExceptionBody<ConcQWithSizeWrapper, allocator_t, allocator_char_t, FooEx>();
938*49e08aacStbbdev     TestExceptionBody<oneapi::tbb::concurrent_bounded_queue, allocator_t, allocator_char_t, FooEx>();
93951c0b2f7Stbbdev 
94051c0b2f7Stbbdev }
94151c0b2f7Stbbdev 
94251c0b2f7Stbbdev std::atomic<std::size_t> num_pushed;
94351c0b2f7Stbbdev std::atomic<std::size_t> num_popped;
94451c0b2f7Stbbdev std::atomic<std::size_t> failed_pushes;
94551c0b2f7Stbbdev std::atomic<std::size_t> failed_pops;
94651c0b2f7Stbbdev 
94751c0b2f7Stbbdev class SimplePushBody {
948*49e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<int>* q;
94951c0b2f7Stbbdev     std::size_t max;
95051c0b2f7Stbbdev public:
951*49e08aacStbbdev     SimplePushBody(oneapi::tbb::concurrent_bounded_queue<int>* _q, std::size_t hi_thr) : q(_q), max(hi_thr) {}
95251c0b2f7Stbbdev 
95351c0b2f7Stbbdev     void operator()(std::size_t thread_id) const {
95451c0b2f7Stbbdev         if (thread_id == max) {
95551c0b2f7Stbbdev             while ( q->size() < std::ptrdiff_t(max) ) {
95651c0b2f7Stbbdev                 std::this_thread::yield();
95751c0b2f7Stbbdev             }
95851c0b2f7Stbbdev             q->abort();
95951c0b2f7Stbbdev             return;
96051c0b2f7Stbbdev         }
96151c0b2f7Stbbdev         try {
96251c0b2f7Stbbdev             q->push(42);
96351c0b2f7Stbbdev             ++num_pushed;
96451c0b2f7Stbbdev         } catch (...) {
96551c0b2f7Stbbdev             ++failed_pushes;
96651c0b2f7Stbbdev         }
96751c0b2f7Stbbdev     }
96851c0b2f7Stbbdev };
96951c0b2f7Stbbdev 
97051c0b2f7Stbbdev class SimplePopBody {
971*49e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<int>* q;
97251c0b2f7Stbbdev     std::ptrdiff_t max;
97351c0b2f7Stbbdev     std::ptrdiff_t prefill;
97451c0b2f7Stbbdev public:
975*49e08aacStbbdev     SimplePopBody(oneapi::tbb::concurrent_bounded_queue<int>* _q, std::size_t hi_thr, std::size_t nitems)
97651c0b2f7Stbbdev     : q(_q), max(hi_thr), prefill(nitems) {}
97751c0b2f7Stbbdev 
97851c0b2f7Stbbdev     void operator()(std::size_t thread_id) const {
97951c0b2f7Stbbdev         int e;
98051c0b2f7Stbbdev         if (thread_id == std::size_t(max)) {
98151c0b2f7Stbbdev             while (q->size()> prefill - max) {
98251c0b2f7Stbbdev                 std::this_thread::yield();
98351c0b2f7Stbbdev             }
98451c0b2f7Stbbdev 
98551c0b2f7Stbbdev             q->abort();
98651c0b2f7Stbbdev             return;
98751c0b2f7Stbbdev         }
98851c0b2f7Stbbdev         try {
98951c0b2f7Stbbdev             q->pop(e);
99051c0b2f7Stbbdev             ++num_popped;
99151c0b2f7Stbbdev         } catch ( ... ) {
99251c0b2f7Stbbdev             ++failed_pops;
99351c0b2f7Stbbdev         }
99451c0b2f7Stbbdev     }
99551c0b2f7Stbbdev };
99651c0b2f7Stbbdev 
99751c0b2f7Stbbdev void TestAbort() {
99851c0b2f7Stbbdev     for (std::size_t nthreads = MinThread; nthreads <= MaxThread; ++nthreads) {
999*49e08aacStbbdev         oneapi::tbb::concurrent_bounded_queue<int> iq1;
100051c0b2f7Stbbdev         iq1.set_capacity(0);
100151c0b2f7Stbbdev         for (std::size_t i = 0; i < 10; ++i) {
100251c0b2f7Stbbdev             num_pushed.store(0, std::memory_order_relaxed);
100351c0b2f7Stbbdev             num_popped.store(0, std::memory_order_relaxed);
100451c0b2f7Stbbdev             failed_pushes.store(0, std::memory_order_relaxed);
100551c0b2f7Stbbdev             failed_pops.store(0, std::memory_order_relaxed);
100651c0b2f7Stbbdev             SimplePushBody my_push_body1(&iq1, nthreads);
100751c0b2f7Stbbdev             utils::NativeParallelFor(nthreads + 1, my_push_body1);
100851c0b2f7Stbbdev             REQUIRE_MESSAGE(num_pushed == 0, "no elements should have been pushed to zero-sized queue");
100951c0b2f7Stbbdev             REQUIRE_MESSAGE(failed_pushes == nthreads, "All threads should have failed to push an element to zero-sized queue");
101051c0b2f7Stbbdev             // Do not test popping each time in order to test queue destruction with no previous pops
101151c0b2f7Stbbdev             if (nthreads < (MaxThread + MinThread) / 2) {
101251c0b2f7Stbbdev                 int e;
101351c0b2f7Stbbdev                 bool queue_empty = !iq1.try_pop(e);
101451c0b2f7Stbbdev                 REQUIRE_MESSAGE(queue_empty, "no elements should have been popped from zero-sized queue");
101551c0b2f7Stbbdev             }
101651c0b2f7Stbbdev         }
101751c0b2f7Stbbdev 
1018*49e08aacStbbdev         oneapi::tbb::concurrent_bounded_queue<int> iq2;
101951c0b2f7Stbbdev         iq2.set_capacity(2);
102051c0b2f7Stbbdev         for (std::size_t i=0; i < 10; ++i) {
102151c0b2f7Stbbdev             num_pushed.store(0, std::memory_order_relaxed);
102251c0b2f7Stbbdev             num_popped.store(0, std::memory_order_relaxed);
102351c0b2f7Stbbdev             failed_pushes.store(0, std::memory_order_relaxed);
102451c0b2f7Stbbdev             failed_pops.store(0, std::memory_order_relaxed);
102551c0b2f7Stbbdev             SimplePushBody my_push_body2(&iq2, nthreads);
102651c0b2f7Stbbdev             utils::NativeParallelFor(nthreads + 1, my_push_body2);
102751c0b2f7Stbbdev             REQUIRE_MESSAGE(num_pushed <= 2, "at most 2 elements should have been pushed to queue of size 2");
102851c0b2f7Stbbdev             if (nthreads>= 2)
102951c0b2f7Stbbdev                 REQUIRE_MESSAGE(failed_pushes == nthreads - 2, "nthreads-2 threads should have failed to push an element to queue of size 2");
103051c0b2f7Stbbdev             int e;
103151c0b2f7Stbbdev             while (iq2.try_pop(e)) ;
103251c0b2f7Stbbdev         }
103351c0b2f7Stbbdev 
1034*49e08aacStbbdev         oneapi::tbb::concurrent_bounded_queue<int> iq3;
103551c0b2f7Stbbdev         iq3.set_capacity(2);
103651c0b2f7Stbbdev         for (std::size_t i = 0; i < 10; ++i) {
103751c0b2f7Stbbdev             num_pushed.store(0, std::memory_order_relaxed);
103851c0b2f7Stbbdev             num_popped.store(0, std::memory_order_relaxed);
103951c0b2f7Stbbdev             failed_pushes.store(0, std::memory_order_relaxed);
104051c0b2f7Stbbdev             failed_pops.store(0, std::memory_order_relaxed);
104151c0b2f7Stbbdev             iq3.push(42);
104251c0b2f7Stbbdev             iq3.push(42);
104351c0b2f7Stbbdev             SimplePopBody my_pop_body(&iq3, nthreads, 2);
104451c0b2f7Stbbdev             utils::NativeParallelFor( nthreads+1, my_pop_body );
104551c0b2f7Stbbdev             REQUIRE_MESSAGE(num_popped <= 2, "at most 2 elements should have been popped from queue of size 2");
104651c0b2f7Stbbdev             if (nthreads>= 2)
104751c0b2f7Stbbdev                 REQUIRE_MESSAGE(failed_pops == nthreads - 2, "nthreads-2 threads should have failed to pop an element from queue of size 2");
104851c0b2f7Stbbdev             else {
104951c0b2f7Stbbdev                 int e;
105051c0b2f7Stbbdev                 iq3.pop(e);
105151c0b2f7Stbbdev             }
105251c0b2f7Stbbdev         }
105351c0b2f7Stbbdev 
1054*49e08aacStbbdev         oneapi::tbb::concurrent_bounded_queue<int> iq4;
105551c0b2f7Stbbdev         std::size_t cap = nthreads / 2;
105651c0b2f7Stbbdev         if (!cap) cap = 1;
105751c0b2f7Stbbdev         iq4.set_capacity(cap);
105851c0b2f7Stbbdev         for (int i=0; i<10; ++i) {
105951c0b2f7Stbbdev             num_pushed.store(0, std::memory_order_relaxed);
106051c0b2f7Stbbdev             num_popped.store(0, std::memory_order_relaxed);
106151c0b2f7Stbbdev             failed_pushes.store(0, std::memory_order_relaxed);
106251c0b2f7Stbbdev             failed_pops.store(0, std::memory_order_relaxed);
106351c0b2f7Stbbdev             SimplePushBody my_push_body2(&iq4, nthreads);
106451c0b2f7Stbbdev             utils::NativeParallelFor(nthreads + 1, my_push_body2);
106551c0b2f7Stbbdev             REQUIRE_MESSAGE(num_pushed <= cap, "at most cap elements should have been pushed to queue of size cap");
106651c0b2f7Stbbdev             if (nthreads>= cap)
106751c0b2f7Stbbdev                 REQUIRE_MESSAGE(failed_pushes == nthreads-cap, "nthreads-cap threads should have failed to push an element to queue of size cap");
106851c0b2f7Stbbdev             SimplePopBody my_pop_body(&iq4, nthreads, num_pushed);
106951c0b2f7Stbbdev             utils::NativeParallelFor( nthreads+1, my_pop_body );
107051c0b2f7Stbbdev             REQUIRE_MESSAGE((int)num_popped <= cap, "at most cap elements should have been popped from queue of size cap");
107151c0b2f7Stbbdev             if (nthreads>= cap)
107251c0b2f7Stbbdev                 REQUIRE_MESSAGE(failed_pops == nthreads-cap, "nthreads-cap threads should have failed to pop an element from queue of size cap");
107351c0b2f7Stbbdev             else {
107451c0b2f7Stbbdev                 int e;
107551c0b2f7Stbbdev                 while (iq4.try_pop(e)) ;
107651c0b2f7Stbbdev             }
107751c0b2f7Stbbdev         }
107851c0b2f7Stbbdev     }
107951c0b2f7Stbbdev }
108051c0b2f7Stbbdev #endif
108151c0b2f7Stbbdev 
108251c0b2f7Stbbdev template <template <typename...> class ContainerType>
108351c0b2f7Stbbdev void test_member_types() {
108451c0b2f7Stbbdev     using container_type = ContainerType<int>;
1085*49e08aacStbbdev     static_assert(std::is_same<typename container_type::allocator_type, oneapi::tbb::cache_aligned_allocator<int>>::value,
108651c0b2f7Stbbdev                   "Incorrect default template allocator");
108751c0b2f7Stbbdev 
108851c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::value_type, int>::value,
108951c0b2f7Stbbdev                   "Incorrect container value_type member type");
109051c0b2f7Stbbdev 
109151c0b2f7Stbbdev     static_assert(std::is_signed<typename container_type::difference_type>::value,
109251c0b2f7Stbbdev                   "Incorrect container difference_type member type");
109351c0b2f7Stbbdev 
109451c0b2f7Stbbdev     using value_type = typename container_type::value_type;
109551c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::reference, value_type&>::value,
109651c0b2f7Stbbdev                   "Incorrect container reference member type");
109751c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::const_reference, const value_type&>::value,
109851c0b2f7Stbbdev                   "Incorrect container const_reference member type");
109951c0b2f7Stbbdev     using allocator_type = typename container_type::allocator_type;
110051c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::pointer, typename std::allocator_traits<allocator_type>::pointer>::value,
110151c0b2f7Stbbdev                   "Incorrect container pointer member type");
110251c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::const_pointer, typename std::allocator_traits<allocator_type>::const_pointer>::value,
110351c0b2f7Stbbdev                   "Incorrect container const_pointer member type");
110451c0b2f7Stbbdev 
110551c0b2f7Stbbdev     static_assert(utils::is_forward_iterator<typename container_type::iterator>::value,
110651c0b2f7Stbbdev                   "Incorrect container iterator member type");
110751c0b2f7Stbbdev     static_assert(!std::is_const<typename container_type::iterator::value_type>::value,
110851c0b2f7Stbbdev                   "Incorrect container iterator member type");
110951c0b2f7Stbbdev     static_assert(utils::is_forward_iterator<typename container_type::const_iterator>::value,
111051c0b2f7Stbbdev                   "Incorrect container const_iterator member type");
111151c0b2f7Stbbdev     static_assert(std::is_const<typename container_type::const_iterator::value_type>::value,
111251c0b2f7Stbbdev                   "Incorrect container iterator member type");
111351c0b2f7Stbbdev }
111451c0b2f7Stbbdev 
111551c0b2f7Stbbdev enum push_t { push_op, try_push_op };
111651c0b2f7Stbbdev 
111751c0b2f7Stbbdev template<push_t push_op>
111851c0b2f7Stbbdev struct pusher {
111951c0b2f7Stbbdev     template<typename CQ, typename VType>
112051c0b2f7Stbbdev     static bool push( CQ& queue, VType&& val ) {
112151c0b2f7Stbbdev         queue.push( std::forward<VType>( val ) );
112251c0b2f7Stbbdev         return true;
112351c0b2f7Stbbdev     }
112451c0b2f7Stbbdev };
112551c0b2f7Stbbdev 
112651c0b2f7Stbbdev template<>
112751c0b2f7Stbbdev struct pusher< try_push_op> {
112851c0b2f7Stbbdev     template<typename CQ, typename VType>
112951c0b2f7Stbbdev     static bool push( CQ& queue, VType&& val ) {
113051c0b2f7Stbbdev         return queue.try_push( std::forward<VType>( val ) );
113151c0b2f7Stbbdev     }
113251c0b2f7Stbbdev };
113351c0b2f7Stbbdev 
113451c0b2f7Stbbdev enum pop_t { pop_op, try_pop_op };
113551c0b2f7Stbbdev 
113651c0b2f7Stbbdev template<pop_t pop_op>
113751c0b2f7Stbbdev struct popper {
113851c0b2f7Stbbdev     template<typename CQ, typename VType>
113951c0b2f7Stbbdev     static bool pop( CQ& queue, VType&& val ) {
114051c0b2f7Stbbdev         if( queue.empty() ) return false;
114151c0b2f7Stbbdev         queue.pop( std::forward<VType>( val ) );
114251c0b2f7Stbbdev         return true;
114351c0b2f7Stbbdev     }
114451c0b2f7Stbbdev };
114551c0b2f7Stbbdev 
114651c0b2f7Stbbdev template<>
114751c0b2f7Stbbdev struct popper<try_pop_op> {
114851c0b2f7Stbbdev     template<typename CQ, typename VType>
114951c0b2f7Stbbdev     static bool pop( CQ& queue, VType&& val ) {
115051c0b2f7Stbbdev         return queue.try_pop( std::forward<VType>( val ) );
115151c0b2f7Stbbdev     }
115251c0b2f7Stbbdev };
115351c0b2f7Stbbdev 
115451c0b2f7Stbbdev struct MoveOperationTracker {
115551c0b2f7Stbbdev     static std::size_t copy_constructor_called_times;
115651c0b2f7Stbbdev     static std::size_t move_constructor_called_times;
115751c0b2f7Stbbdev     static std::size_t copy_assignment_called_times;
115851c0b2f7Stbbdev     static std::size_t move_assignment_called_times;
115951c0b2f7Stbbdev 
116051c0b2f7Stbbdev     MoveOperationTracker() {}
116151c0b2f7Stbbdev     MoveOperationTracker(const MoveOperationTracker&) {
116251c0b2f7Stbbdev         ++copy_constructor_called_times;
116351c0b2f7Stbbdev     }
116451c0b2f7Stbbdev     MoveOperationTracker(MoveOperationTracker&&) {
116551c0b2f7Stbbdev         ++move_constructor_called_times;
116651c0b2f7Stbbdev     }
116751c0b2f7Stbbdev     MoveOperationTracker& operator=(MoveOperationTracker const&) {
116851c0b2f7Stbbdev         ++copy_assignment_called_times;
116951c0b2f7Stbbdev         return *this;
117051c0b2f7Stbbdev     }
117151c0b2f7Stbbdev     MoveOperationTracker& operator=(MoveOperationTracker&&) {
117251c0b2f7Stbbdev         ++move_assignment_called_times;
117351c0b2f7Stbbdev         return *this;
117451c0b2f7Stbbdev     }
117551c0b2f7Stbbdev };
117651c0b2f7Stbbdev 
117751c0b2f7Stbbdev size_t MoveOperationTracker::copy_constructor_called_times = 0;
117851c0b2f7Stbbdev size_t MoveOperationTracker::move_constructor_called_times = 0;
117951c0b2f7Stbbdev size_t MoveOperationTracker::copy_assignment_called_times = 0;
118051c0b2f7Stbbdev size_t MoveOperationTracker::move_assignment_called_times = 0;
118151c0b2f7Stbbdev 
118251c0b2f7Stbbdev template <class CQ, push_t push_op, pop_t pop_op>
118351c0b2f7Stbbdev void TestMoveSupport() {
118451c0b2f7Stbbdev     std::size_t &mcct = MoveOperationTracker::move_constructor_called_times;
118551c0b2f7Stbbdev     std::size_t &ccct = MoveOperationTracker::copy_constructor_called_times;
118651c0b2f7Stbbdev     std::size_t &cact = MoveOperationTracker::copy_assignment_called_times;
118751c0b2f7Stbbdev     std::size_t &mact = MoveOperationTracker::move_assignment_called_times;
118851c0b2f7Stbbdev     mcct = ccct = cact = mact = 0;
118951c0b2f7Stbbdev 
119051c0b2f7Stbbdev     CQ q;
119151c0b2f7Stbbdev 
119251c0b2f7Stbbdev     REQUIRE_MESSAGE(mcct == 0, "Value must be zero-initialized");
119351c0b2f7Stbbdev     REQUIRE_MESSAGE(ccct == 0, "Value must be zero-initialized");
119451c0b2f7Stbbdev     CHECK(pusher<push_op>::push( q, MoveOperationTracker() ));
119551c0b2f7Stbbdev     REQUIRE_MESSAGE(mcct == 1, "Not working push(T&&) or try_push(T&&)?");
119651c0b2f7Stbbdev     REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&) or try_push(T&&)");
119751c0b2f7Stbbdev 
119851c0b2f7Stbbdev     MoveOperationTracker ob;
119951c0b2f7Stbbdev     CHECK(pusher<push_op>::push( q, std::move(ob) ));
120051c0b2f7Stbbdev     REQUIRE_MESSAGE(mcct == 2, "Not working push(T&&) or try_push(T&&)?");
120151c0b2f7Stbbdev     REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&) or try_push(T&&)");
120251c0b2f7Stbbdev 
120351c0b2f7Stbbdev     REQUIRE_MESSAGE(cact == 0, "Copy assignment called during push(T&&) or try_push(T&&)");
120451c0b2f7Stbbdev     REQUIRE_MESSAGE(mact == 0, "Move assignment called during push(T&&) or try_push(T&&)");
120551c0b2f7Stbbdev 
120651c0b2f7Stbbdev     bool result = popper<pop_op>::pop( q, ob );
120751c0b2f7Stbbdev     CHECK(result);
120851c0b2f7Stbbdev     REQUIRE_MESSAGE(cact == 0, "Copy assignment called during try_pop(T&&)");
120951c0b2f7Stbbdev     REQUIRE_MESSAGE(mact == 1, "Move assignment was not called during try_pop(T&&)");
121051c0b2f7Stbbdev }
121151c0b2f7Stbbdev 
121251c0b2f7Stbbdev void TestMoveSupportInPushPop() {
1213*49e08aacStbbdev     TestMoveSupport<oneapi::tbb::concurrent_queue<MoveOperationTracker>, push_op, try_pop_op>();
1214*49e08aacStbbdev     TestMoveSupport<oneapi::tbb::concurrent_bounded_queue<MoveOperationTracker>, push_op, pop_op>();
1215*49e08aacStbbdev     TestMoveSupport<oneapi::tbb::concurrent_bounded_queue<MoveOperationTracker>, try_push_op, try_pop_op>();
121651c0b2f7Stbbdev }
121751c0b2f7Stbbdev 
121851c0b2f7Stbbdev template<class T>
1219*49e08aacStbbdev class allocator: public oneapi::tbb::cache_aligned_allocator<T> {
122051c0b2f7Stbbdev public:
122151c0b2f7Stbbdev     std::size_t m_unique_id;
122251c0b2f7Stbbdev 
122351c0b2f7Stbbdev     allocator() : m_unique_id( 0 ) {}
122451c0b2f7Stbbdev 
122551c0b2f7Stbbdev     allocator(size_t unique_id) { m_unique_id = unique_id; }
122651c0b2f7Stbbdev 
122751c0b2f7Stbbdev     template<typename U>
122851c0b2f7Stbbdev     allocator(const allocator<U>& a) noexcept { m_unique_id = a.m_unique_id; }
122951c0b2f7Stbbdev 
123051c0b2f7Stbbdev     template<typename U>
123151c0b2f7Stbbdev     struct rebind { typedef allocator<U> other; };
123251c0b2f7Stbbdev 
123351c0b2f7Stbbdev     friend bool operator==(const allocator& lhs, const allocator& rhs) {
123451c0b2f7Stbbdev         return lhs.m_unique_id == rhs.m_unique_id;
123551c0b2f7Stbbdev     }
123651c0b2f7Stbbdev };
123751c0b2f7Stbbdev 
123851c0b2f7Stbbdev template <typename Queue>
123951c0b2f7Stbbdev void AssertEquality(Queue &q, const std::vector<typename Queue::value_type> &vec) {
124051c0b2f7Stbbdev     CHECK(q.size() == typename Queue::size_type(vec.size()));
124151c0b2f7Stbbdev     CHECK(std::equal(q.unsafe_begin(), q.unsafe_end(), vec.begin()));
124251c0b2f7Stbbdev }
124351c0b2f7Stbbdev 
124451c0b2f7Stbbdev template <typename Queue>
124551c0b2f7Stbbdev void AssertEmptiness(Queue &q) {
124651c0b2f7Stbbdev     CHECK(q.empty());
124751c0b2f7Stbbdev     CHECK(!q.size());
124851c0b2f7Stbbdev     typename Queue::value_type elem;
124951c0b2f7Stbbdev     CHECK(!q.try_pop(elem));
125051c0b2f7Stbbdev }
125151c0b2f7Stbbdev 
125251c0b2f7Stbbdev template <push_t push_op, typename Queue>
125351c0b2f7Stbbdev void FillTest(Queue &q, const std::vector<typename Queue::value_type> &vec) {
125451c0b2f7Stbbdev     for (typename std::vector<typename Queue::value_type>::const_iterator it = vec.begin(); it != vec.end(); ++it)
125551c0b2f7Stbbdev         CHECK(pusher<push_op>::push(q, *it));
125651c0b2f7Stbbdev     AssertEquality(q, vec);
125751c0b2f7Stbbdev }
125851c0b2f7Stbbdev 
125951c0b2f7Stbbdev template <pop_t pop_op, typename Queue>
126051c0b2f7Stbbdev void EmptyTest(Queue &q, const std::vector<typename Queue::value_type> &vec) {
126151c0b2f7Stbbdev     typedef typename Queue::value_type value_type;
126251c0b2f7Stbbdev 
126351c0b2f7Stbbdev     value_type elem;
126451c0b2f7Stbbdev     typename std::vector<value_type>::const_iterator it = vec.begin();
126551c0b2f7Stbbdev     while (popper<pop_op>::pop(q, elem)) {
126651c0b2f7Stbbdev         CHECK(elem == *it);
126751c0b2f7Stbbdev         ++it;
126851c0b2f7Stbbdev     }
126951c0b2f7Stbbdev     CHECK(it == vec.end());
127051c0b2f7Stbbdev     AssertEmptiness(q);
127151c0b2f7Stbbdev }
127251c0b2f7Stbbdev 
127351c0b2f7Stbbdev template <typename T, typename A>
1274*49e08aacStbbdev void bounded_queue_specific_test(oneapi::tbb::concurrent_queue<T, A> &, const std::vector<T> &) { /* do nothing */ }
127551c0b2f7Stbbdev 
127651c0b2f7Stbbdev template <typename T, typename A>
1277*49e08aacStbbdev void bounded_queue_specific_test(oneapi::tbb::concurrent_bounded_queue<T, A> &q, const std::vector<T> &vec) {
1278*49e08aacStbbdev     typedef typename oneapi::tbb::concurrent_bounded_queue<T, A>::size_type size_type;
127951c0b2f7Stbbdev 
128051c0b2f7Stbbdev     FillTest<try_push_op>(q, vec);
1281*49e08aacStbbdev     oneapi::tbb::concurrent_bounded_queue<T, A> q2 = q;
128251c0b2f7Stbbdev     EmptyTest<pop_op>(q, vec);
128351c0b2f7Stbbdev 
128451c0b2f7Stbbdev     // capacity
128551c0b2f7Stbbdev     q2.set_capacity(size_type(vec.size()));
128651c0b2f7Stbbdev     CHECK(q2.capacity() == size_type(vec.size()));
128751c0b2f7Stbbdev     CHECK(q2.size() == size_type(vec.size()));
128851c0b2f7Stbbdev     CHECK(!q2.try_push(vec[0]));
128951c0b2f7Stbbdev     q.abort();
129051c0b2f7Stbbdev }
129151c0b2f7Stbbdev 
129251c0b2f7Stbbdev // Checks operability of the queue the data was moved from
129351c0b2f7Stbbdev template<typename T, typename CQ>
129451c0b2f7Stbbdev void TestQueueOperabilityAfterDataMove( CQ& queue ) {
129551c0b2f7Stbbdev     const std::size_t size = 10;
129651c0b2f7Stbbdev     std::vector<T> v(size);
129751c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i ) v[i] = T( i * i + i );
129851c0b2f7Stbbdev 
129951c0b2f7Stbbdev     FillTest<push_op>(queue, v);
130051c0b2f7Stbbdev     EmptyTest<try_pop_op>(queue, v);
130151c0b2f7Stbbdev     bounded_queue_specific_test(queue, v);
130251c0b2f7Stbbdev }
130351c0b2f7Stbbdev 
130451c0b2f7Stbbdev template<class CQ, class T>
130551c0b2f7Stbbdev void TestMoveConstructors() {
130651c0b2f7Stbbdev     T::construction_num = T::destruction_num = 0;
130751c0b2f7Stbbdev     CQ src_queue( allocator<T>(0) );
130851c0b2f7Stbbdev     const std::size_t size = 10;
130951c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i )
131051c0b2f7Stbbdev         src_queue.push( T(i + (i ^ size)) );
131151c0b2f7Stbbdev     CHECK(T::construction_num == 2 * size);
131251c0b2f7Stbbdev     CHECK(T::destruction_num == size);
131351c0b2f7Stbbdev 
131451c0b2f7Stbbdev     const T* locations[size];
131551c0b2f7Stbbdev     typename CQ::const_iterator qit = src_queue.unsafe_begin();
131651c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i, ++qit )
131751c0b2f7Stbbdev         locations[i] = &(*qit);
131851c0b2f7Stbbdev 
131951c0b2f7Stbbdev     // Ensuring allocation operation takes place during move when allocators are different
132051c0b2f7Stbbdev     T::construction_num = T::destruction_num = 0;
132151c0b2f7Stbbdev     CQ dst_queue( std::move(src_queue), allocator<T>(1) );
132251c0b2f7Stbbdev     CHECK(T::construction_num == size);
132351c0b2f7Stbbdev     CHECK(T::destruction_num == size * 2); // One item is used by the queue destructor
132451c0b2f7Stbbdev 
132551c0b2f7Stbbdev     TestQueueOperabilityAfterDataMove<T>( src_queue );
132651c0b2f7Stbbdev 
132751c0b2f7Stbbdev     qit = dst_queue.unsafe_begin();
132851c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i, ++qit ) {
132951c0b2f7Stbbdev         REQUIRE_MESSAGE(locations[i] != &(*qit), "an item should have been copied but was not" );
133051c0b2f7Stbbdev         locations[i] = &(*qit);
133151c0b2f7Stbbdev     }
133251c0b2f7Stbbdev 
133351c0b2f7Stbbdev     T::construction_num = T::destruction_num = 0;
133451c0b2f7Stbbdev     // Ensuring there is no allocation operation during move with equal allocators
133551c0b2f7Stbbdev     CQ dst_queue2( std::move(dst_queue), allocator<T>(1) );
133651c0b2f7Stbbdev     CHECK(T::construction_num == 0);
133751c0b2f7Stbbdev     CHECK(T::destruction_num == 0);
133851c0b2f7Stbbdev 
133951c0b2f7Stbbdev     TestQueueOperabilityAfterDataMove<T>( dst_queue );
134051c0b2f7Stbbdev 
134151c0b2f7Stbbdev     qit = dst_queue2.unsafe_begin();
134251c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i, ++qit ) {
134351c0b2f7Stbbdev         REQUIRE_MESSAGE(locations[i] == &(*qit), "an item should have been moved but was not" );
134451c0b2f7Stbbdev     }
134551c0b2f7Stbbdev 
134651c0b2f7Stbbdev     for( std::size_t i = 0; i < size; ++i) {
134751c0b2f7Stbbdev         T test(i + (i ^ size));
134851c0b2f7Stbbdev         T popped;
134951c0b2f7Stbbdev         bool pop_result = dst_queue2.try_pop( popped );
135051c0b2f7Stbbdev         CHECK(pop_result);
135151c0b2f7Stbbdev         CHECK(test == popped);
135251c0b2f7Stbbdev     }
135351c0b2f7Stbbdev     CHECK(dst_queue2.empty());
135451c0b2f7Stbbdev     CHECK(dst_queue2.size() == 0);
135551c0b2f7Stbbdev }
135651c0b2f7Stbbdev 
135751c0b2f7Stbbdev void TestMoveConstruction() {
135851c0b2f7Stbbdev     TestMoveConstructors<ConcQWithSizeWrapper<Bar, allocator<Bar>>, Bar>();
1359*49e08aacStbbdev     TestMoveConstructors<oneapi::tbb::concurrent_bounded_queue<Bar, allocator<Bar>>, Bar>();
136051c0b2f7Stbbdev }
136151c0b2f7Stbbdev 
136251c0b2f7Stbbdev class NonTrivialConstructorType {
136351c0b2f7Stbbdev public:
136451c0b2f7Stbbdev     NonTrivialConstructorType( int a = 0 ) : m_a( a ), m_str( "" ) {}
136551c0b2f7Stbbdev     NonTrivialConstructorType( const std::string& str ) : m_a( 0 ), m_str( str ) {}
136651c0b2f7Stbbdev     NonTrivialConstructorType( int a, const std::string& str ) : m_a( a ), m_str( str ) {}
136751c0b2f7Stbbdev     int get_a() const { return m_a; }
136851c0b2f7Stbbdev     std::string get_str() const { return m_str; }
136951c0b2f7Stbbdev private:
137051c0b2f7Stbbdev     int m_a;
137151c0b2f7Stbbdev     std::string m_str;
137251c0b2f7Stbbdev };
137351c0b2f7Stbbdev 
137451c0b2f7Stbbdev enum emplace_t { emplace_op, try_emplace_op };
137551c0b2f7Stbbdev 
137651c0b2f7Stbbdev template<emplace_t emplace_op>
137751c0b2f7Stbbdev struct emplacer {
137851c0b2f7Stbbdev     template<typename CQ, typename... Args>
137951c0b2f7Stbbdev     static void emplace( CQ& queue, Args&&... val ) { queue.emplace( std::forward<Args>( val )... ); }
138051c0b2f7Stbbdev };
138151c0b2f7Stbbdev 
138251c0b2f7Stbbdev template<>
138351c0b2f7Stbbdev struct emplacer <try_emplace_op> {
138451c0b2f7Stbbdev     template<typename CQ, typename... Args>
138551c0b2f7Stbbdev     static void emplace( CQ& queue, Args&&... val ) {
138651c0b2f7Stbbdev         bool result = queue.try_emplace( std::forward<Args>( val )... );
138751c0b2f7Stbbdev         REQUIRE_MESSAGE(result, "try_emplace error\n");
138851c0b2f7Stbbdev     }
138951c0b2f7Stbbdev };
139051c0b2f7Stbbdev 
139151c0b2f7Stbbdev template<typename CQ, emplace_t emplace_op>
139251c0b2f7Stbbdev void TestEmplaceInQueue() {
139351c0b2f7Stbbdev     CQ cq;
139451c0b2f7Stbbdev     std::string test_str = "I'm being emplaced!";
139551c0b2f7Stbbdev     {
139651c0b2f7Stbbdev         emplacer<emplace_op>::emplace( cq, 5 );
139751c0b2f7Stbbdev         CHECK(cq.size() == 1);
139851c0b2f7Stbbdev         NonTrivialConstructorType popped( -1 );
139951c0b2f7Stbbdev         bool result = cq.try_pop( popped );
140051c0b2f7Stbbdev         CHECK(result);
140151c0b2f7Stbbdev         CHECK(popped.get_a() == 5);
140251c0b2f7Stbbdev         CHECK(popped.get_str() == std::string( "" ));
140351c0b2f7Stbbdev     }
140451c0b2f7Stbbdev 
140551c0b2f7Stbbdev     CHECK(cq.empty());
140651c0b2f7Stbbdev 
140751c0b2f7Stbbdev     {
140851c0b2f7Stbbdev         NonTrivialConstructorType popped( -1 );
140951c0b2f7Stbbdev         emplacer<emplace_op>::emplace( cq, std::string(test_str) );
141051c0b2f7Stbbdev         bool result = cq.try_pop( popped );
141151c0b2f7Stbbdev         CHECK(result);
141251c0b2f7Stbbdev         CHECK(popped.get_a() == 0);
141351c0b2f7Stbbdev         CHECK(popped.get_str() == test_str);
141451c0b2f7Stbbdev     }
141551c0b2f7Stbbdev 
141651c0b2f7Stbbdev     CHECK(cq.empty());
141751c0b2f7Stbbdev 
141851c0b2f7Stbbdev     {
141951c0b2f7Stbbdev         NonTrivialConstructorType popped( -1, "" );
142051c0b2f7Stbbdev         emplacer<emplace_op>::emplace( cq, 5, std::string(test_str) );
142151c0b2f7Stbbdev         bool result = cq.try_pop( popped );
142251c0b2f7Stbbdev         CHECK(result);
142351c0b2f7Stbbdev         CHECK(popped.get_a() == 5);
142451c0b2f7Stbbdev         CHECK(popped.get_str() == test_str);
142551c0b2f7Stbbdev     }
142651c0b2f7Stbbdev }
142751c0b2f7Stbbdev void TestEmplace() {
142851c0b2f7Stbbdev     TestEmplaceInQueue<ConcQWithSizeWrapper<NonTrivialConstructorType>, emplace_op>();
1429*49e08aacStbbdev     TestEmplaceInQueue<oneapi::tbb::concurrent_bounded_queue<NonTrivialConstructorType>, emplace_op>();
1430*49e08aacStbbdev     TestEmplaceInQueue<oneapi::tbb::concurrent_bounded_queue<NonTrivialConstructorType>, try_emplace_op>();
143151c0b2f7Stbbdev }
143251c0b2f7Stbbdev 
143351c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
143451c0b2f7Stbbdev template <template <typename...> typename TQueue>
143551c0b2f7Stbbdev void TestDeductionGuides() {
143651c0b2f7Stbbdev     using ComplexType = const std::string*;
143751c0b2f7Stbbdev     std::vector<ComplexType> v;
143851c0b2f7Stbbdev 
143951c0b2f7Stbbdev     // check TQueue(InputIterator, InputIterator)
144051c0b2f7Stbbdev     TQueue q1(v.begin(), v.end());
144151c0b2f7Stbbdev     static_assert(std::is_same<decltype(q1), TQueue<ComplexType>>::value);
144251c0b2f7Stbbdev 
144351c0b2f7Stbbdev     // check TQueue(InputIterator, InputIterator, Allocator)
144451c0b2f7Stbbdev     TQueue q2(v.begin(), v.end(), std::allocator<ComplexType>());
144551c0b2f7Stbbdev     static_assert(std::is_same<decltype(q2), TQueue<ComplexType, std::allocator<ComplexType>>>::value);
144651c0b2f7Stbbdev 
144751c0b2f7Stbbdev     // check TQueue(TQueue &)
144851c0b2f7Stbbdev     TQueue q3(q1);
144951c0b2f7Stbbdev     static_assert(std::is_same<decltype(q3), decltype(q1)>::value);
145051c0b2f7Stbbdev 
145151c0b2f7Stbbdev     // check TQueue(TQueue &, Allocator)
145251c0b2f7Stbbdev     TQueue q4(q2, std::allocator<ComplexType>());
145351c0b2f7Stbbdev     static_assert(std::is_same<decltype(q4), decltype(q2)>::value);
145451c0b2f7Stbbdev 
145551c0b2f7Stbbdev     // check TQueue(TQueue &&)
145651c0b2f7Stbbdev     TQueue q5(std::move(q1));
145751c0b2f7Stbbdev     static_assert(std::is_same<decltype(q5), decltype(q1)>::value);
145851c0b2f7Stbbdev 
145951c0b2f7Stbbdev     // check TQueue(TQueue &&, Allocator)
146051c0b2f7Stbbdev     TQueue q6(std::move(q4), std::allocator<ComplexType>());
146151c0b2f7Stbbdev     static_assert(std::is_same<decltype(q6), decltype(q4)>::value);
146251c0b2f7Stbbdev }
146351c0b2f7Stbbdev #endif
146451c0b2f7Stbbdev 
146551c0b2f7Stbbdev 
146651c0b2f7Stbbdev //! Test constructors
146751c0b2f7Stbbdev //! \brief \ref interface \ref requirement
146851c0b2f7Stbbdev TEST_CASE("testing constructors") {
146951c0b2f7Stbbdev     TestQueueConstructors();
147051c0b2f7Stbbdev }
147151c0b2f7Stbbdev 
147251c0b2f7Stbbdev //! Test work with empty queue
147351c0b2f7Stbbdev //! \brief \ref interface \ref requirement
147451c0b2f7Stbbdev TEST_CASE("testing work with empty queue") {
147551c0b2f7Stbbdev     TestEmptiness();
147651c0b2f7Stbbdev }
147751c0b2f7Stbbdev 
147851c0b2f7Stbbdev //! Test set capacity operation
147951c0b2f7Stbbdev //! \brief \ref interface \ref requirement
148051c0b2f7Stbbdev TEST_CASE("testing set capacity operation") {
148151c0b2f7Stbbdev     TestFullness();
148251c0b2f7Stbbdev }
148351c0b2f7Stbbdev 
148451c0b2f7Stbbdev //! Test clean operation
148551c0b2f7Stbbdev //! \brief \ref interface \ref requirement
148651c0b2f7Stbbdev TEST_CASE("testing clean operation") {
148751c0b2f7Stbbdev     TestClearWorks();
148851c0b2f7Stbbdev }
148951c0b2f7Stbbdev 
149051c0b2f7Stbbdev //! Test move constructors
149151c0b2f7Stbbdev //! \brief \ref interface \ref requirement
149251c0b2f7Stbbdev TEST_CASE("testing move constructor") {
149351c0b2f7Stbbdev     TestMoveConstruction();
149451c0b2f7Stbbdev }
149551c0b2f7Stbbdev 
149651c0b2f7Stbbdev //! Test move support in push and pop
149751c0b2f7Stbbdev //! \brief \ref requirement
149851c0b2f7Stbbdev TEST_CASE("testing move support in push and pop") {
149951c0b2f7Stbbdev     TestMoveSupportInPushPop();
150051c0b2f7Stbbdev }
150151c0b2f7Stbbdev 
150251c0b2f7Stbbdev //! Test emplace operation
150351c0b2f7Stbbdev //! \brief \ref interface \ref requirement
150451c0b2f7Stbbdev TEST_CASE("testing emplace") {
150551c0b2f7Stbbdev     TestEmplace();
150651c0b2f7Stbbdev }
150751c0b2f7Stbbdev 
150851c0b2f7Stbbdev //! Test concurrent_queues member types
150951c0b2f7Stbbdev //! \brief \ref interface \ref requirement
151051c0b2f7Stbbdev TEST_CASE("testing concurrent_queues member types"){
1511*49e08aacStbbdev     test_member_types<oneapi::tbb::concurrent_queue>();
1512*49e08aacStbbdev     test_member_types<oneapi::tbb::concurrent_bounded_queue>();
151351c0b2f7Stbbdev 
151451c0b2f7Stbbdev     // Test size_type
1515*49e08aacStbbdev     static_assert(std::is_unsigned<typename oneapi::tbb::concurrent_queue<int>::size_type>::value,
1516*49e08aacStbbdev                   "Incorrect oneapi::tbb::concurrent_queue::size_type member type");
1517*49e08aacStbbdev     static_assert(std::is_signed<typename oneapi::tbb::concurrent_bounded_queue<int>::size_type>::value,
1518*49e08aacStbbdev                   "Incorrect oneapi::tbb::concurrent_bounded_queue::size_type member type");
151951c0b2f7Stbbdev }
152051c0b2f7Stbbdev 
152151c0b2f7Stbbdev //! Test iterators
152251c0b2f7Stbbdev //! \brief \ref interface \ref requirement
152351c0b2f7Stbbdev TEST_CASE("testing iterators") {
152451c0b2f7Stbbdev     TestQueueIteratorWorks();
152551c0b2f7Stbbdev }
152651c0b2f7Stbbdev 
152751c0b2f7Stbbdev //! Test concurrent oprations support
152851c0b2f7Stbbdev //! \brief \ref requirement
152951c0b2f7Stbbdev TEST_CASE("testing concurrent oprations support") {
153051c0b2f7Stbbdev     TestConcurrentPushPop();
153151c0b2f7Stbbdev }
153251c0b2f7Stbbdev 
153351c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
153451c0b2f7Stbbdev //! Test exception safety
153551c0b2f7Stbbdev //! \brief \ref requirement
153651c0b2f7Stbbdev TEST_CASE("testing exception safety") {
153751c0b2f7Stbbdev     TestExceptions();
153851c0b2f7Stbbdev }
153951c0b2f7Stbbdev 
154051c0b2f7Stbbdev //! Test abort operation
154151c0b2f7Stbbdev //! \brief \ref interface \ref requirement
154251c0b2f7Stbbdev TEST_CASE("testing abort operation") {
154351c0b2f7Stbbdev     TestAbort();
154451c0b2f7Stbbdev }
154551c0b2f7Stbbdev #endif
154651c0b2f7Stbbdev 
154751c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
154851c0b2f7Stbbdev //! Test deduction guides
154951c0b2f7Stbbdev //! \brief \ref interface
155051c0b2f7Stbbdev TEST_CASE("testing deduction guides") {
1551*49e08aacStbbdev     TestDeductionGuides<oneapi::tbb::concurrent_queue>();
1552*49e08aacStbbdev     TestDeductionGuides<oneapi::tbb::concurrent_bounded_queue>();
155351c0b2f7Stbbdev }
155451c0b2f7Stbbdev #endif
1555