151c0b2f7Stbbdev /*
2155acce7SJhaShweta1 Copyright (c) 2005-2023 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>
push(CQ & q,ValueType v,CounterType i)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:
ConcQWithCapacity()5951c0b2f7Stbbdev ConcQWithCapacity() : my_capacity( std::size_t(-1) / (sizeof(void*) + sizeof(T)) ) {}
size() const6051c0b2f7Stbbdev std::size_t size() const {
6151c0b2f7Stbbdev return this->unsafe_size();
6251c0b2f7Stbbdev }
6351c0b2f7Stbbdev
capacity() const6451c0b2f7Stbbdev std::size_t capacity() const {
6551c0b2f7Stbbdev return my_capacity;
6651c0b2f7Stbbdev }
6751c0b2f7Stbbdev
set_capacity(const std::size_t n)6851c0b2f7Stbbdev void set_capacity( const std::size_t n ) {
6951c0b2f7Stbbdev my_capacity = n;
7051c0b2f7Stbbdev }
7151c0b2f7Stbbdev
try_push(const T & source)7251c0b2f7Stbbdev bool try_push( const T& source ) {
7351c0b2f7Stbbdev base_type::push( source);
7451c0b2f7Stbbdev return source.get_serial() < my_capacity;
7551c0b2f7Stbbdev }
7651c0b2f7Stbbdev
try_pop(T & dest)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>
TestEmptyQueue()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
TestEmptiness()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>
TestFullQueue()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
TestFullness()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>
TestClear()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
TestClearWorks()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>
TestIteratorAux(Iterator1 i,Iterator2 j,int size)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>
TestIteratorAssignment(Iterator2 j)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>
TestIteratorTraits()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>
TestIterator()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
TestQueueIteratorWorks()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:
ConcQWithSizeWrapper()26151c0b2f7Stbbdev ConcQWithSizeWrapper() {}
ConcQWithSizeWrapper(const ConcQWithSizeWrapper & q)26249e08aacStbbdev ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q ) : oneapi::tbb::concurrent_queue<T, A>(q) {}
ConcQWithSizeWrapper(const ConcQWithSizeWrapper & q,const A & a)26349e08aacStbbdev ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q, const A& a ) : oneapi::tbb::concurrent_queue<T, A>(q, a) {}
ConcQWithSizeWrapper(const A & a)26449e08aacStbbdev ConcQWithSizeWrapper( const A& a ) : oneapi::tbb::concurrent_queue<T, A>( a ) {}
26551c0b2f7Stbbdev
ConcQWithSizeWrapper(ConcQWithSizeWrapper && q)26649e08aacStbbdev ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q ) : oneapi::tbb::concurrent_queue<T>(std::move(q)) {}
ConcQWithSizeWrapper(ConcQWithSizeWrapper && q,const A & a)26751c0b2f7Stbbdev ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q, const A& a )
26849e08aacStbbdev : oneapi::tbb::concurrent_queue<T, A>(std::move(q), a) { }
26951c0b2f7Stbbdev
27051c0b2f7Stbbdev template<typename InputIterator>
ConcQWithSizeWrapper(InputIterator begin,InputIterator end,const A & a=A ())27151c0b2f7Stbbdev ConcQWithSizeWrapper( InputIterator begin, InputIterator end, const A& a = A() )
27249e08aacStbbdev : oneapi::tbb::concurrent_queue<T, A>(begin, end, a) {}
size() const27349e08aacStbbdev 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;
Bar()28651c0b2f7Stbbdev Bar() : state(LIVE), my_id(-1)
28751c0b2f7Stbbdev {}
28851c0b2f7Stbbdev
Bar(std::size_t _i)28951c0b2f7Stbbdev Bar( std::size_t _i ) : state(LIVE), my_id(_i) { construction_num++; }
29051c0b2f7Stbbdev
Bar(const Bar & a_bar)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
~Bar()29751c0b2f7Stbbdev ~Bar() {
298b15aabb3Stbbdev CHECK_FAST(state == LIVE);
29951c0b2f7Stbbdev state = DEAD;
30051c0b2f7Stbbdev my_id = DEAD;
30151c0b2f7Stbbdev destruction_num++;
30251c0b2f7Stbbdev }
30351c0b2f7Stbbdev
operator =(const Bar & a_bar)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
operator ==(const Bar & bar1,const Bar & bar2)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;
BarIterator(Bar * bp_)32351c0b2f7Stbbdev BarIterator(Bar* bp_) : bar_ptr(bp_) {}
32451c0b2f7Stbbdev public:
operator *() const32551c0b2f7Stbbdev Bar& operator*() const {
32651c0b2f7Stbbdev return *bar_ptr;
32751c0b2f7Stbbdev }
operator ++()32851c0b2f7Stbbdev BarIterator& operator++() {
32951c0b2f7Stbbdev ++bar_ptr;
33051c0b2f7Stbbdev return *this;
33151c0b2f7Stbbdev }
operator ++(int)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
operator ==(const BarIterator & bia,const BarIterator & bib)34351c0b2f7Stbbdev bool operator==(const BarIterator& bia, const BarIterator& bib) {
34451c0b2f7Stbbdev return bia.bar_ptr==bib.bar_ptr;
34551c0b2f7Stbbdev }
34651c0b2f7Stbbdev
operator !=(const BarIterator & bia,const BarIterator & bib)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:
what() const35451c0b2f7Stbbdev virtual const char *what() const noexcept override { return "making the entry invalid"; }
~Bar_exception()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
BarEx()37251c0b2f7Stbbdev BarEx() : state(LIVE), my_id(-1), my_tilda_id(-1)
37351c0b2f7Stbbdev {}
37451c0b2f7Stbbdev
BarEx(std::size_t _i)37551c0b2f7Stbbdev BarEx(std::size_t _i) : state(LIVE), my_id(_i), my_tilda_id(my_id^(-1))
37651c0b2f7Stbbdev {}
37751c0b2f7Stbbdev
BarEx(const BarEx & a_bar)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
~BarEx()38851c0b2f7Stbbdev ~BarEx() {
389b15aabb3Stbbdev CHECK_FAST(state == LIVE);
39051c0b2f7Stbbdev state = DEAD;
39151c0b2f7Stbbdev my_id = DEAD;
39251c0b2f7Stbbdev }
set_mode(mode_type m)39351c0b2f7Stbbdev static void set_mode( mode_type m ) { mode = m; }
39451c0b2f7Stbbdev
operator =(const BarEx & a_bar)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
operator ==(const BarEx & bar1,const BarEx & bar2)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>
TestConstructors()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
TestQueueConstructors()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;
TestNegativeQueueBodyTestNegativeQueueBody59449e08aacStbbdev TestNegativeQueueBody( oneapi::tbb::concurrent_bounded_queue<T>& q, std::size_t n ) : queue(q), nthread(n) {}
operator ()TestNegativeQueueBody59551c0b2f7Stbbdev 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>
TestNegativeQueue(std::size_t nthread)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:
ConcQPushPopWrapper()62851c0b2f7Stbbdev ConcQPushPopWrapper() : my_capacity(std::size_t(-1) / (sizeof(void*) + sizeof(T)))
62951c0b2f7Stbbdev {}
63051c0b2f7Stbbdev
size() const63151c0b2f7Stbbdev std::size_t size() const { return this->unsafe_size(); }
set_capacity(const ptrdiff_t n)63251c0b2f7Stbbdev void set_capacity( const ptrdiff_t n ) { my_capacity = n; }
try_push(const T & source)63351c0b2f7Stbbdev bool try_push( const T& source ) { return this->push( source); }
try_pop(T & dest)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;
BodyBody64251c0b2f7Stbbdev Body( std::size_t nthread_ ) : nthread(nthread_) {}
operator ()Body64351c0b2f7Stbbdev 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>
TestPushPop(typename CQ::size_type prefill,std::ptrdiff_t capacity,std::size_t nthread)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
TestConcurrentPushPop()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:
what() const78051c0b2f7Stbbdev virtual const char *what() const throw() override { return "out of Foo limit"; }
~Foo_exception()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;
FooEx()79551c0b2f7Stbbdev FooEx() : state(LIVE) {
79651c0b2f7Stbbdev ++FooExConstructed;
79751c0b2f7Stbbdev serial = serial_source++;
79851c0b2f7Stbbdev }
79951c0b2f7Stbbdev
FooEx(const FooEx & item)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
~FooEx()80951c0b2f7Stbbdev ~FooEx() {
81051c0b2f7Stbbdev CHECK(state==LIVE);
81151c0b2f7Stbbdev ++FooExDestroyed;
81251c0b2f7Stbbdev state=DEAD;
81351c0b2f7Stbbdev serial=DEAD;
81451c0b2f7Stbbdev }
81551c0b2f7Stbbdev
operator =(FooEx & item)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
operator =(FooEx && item)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>
TestExceptionBody()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
TestExceptions()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:
SimplePushBody(oneapi::tbb::concurrent_bounded_queue<int> * _q,std::size_t hi_thr)94049e08aacStbbdev SimplePushBody(oneapi::tbb::concurrent_bounded_queue<int>* _q, std::size_t hi_thr) : q(_q), max(hi_thr) {}
94151c0b2f7Stbbdev
operator ()(std::size_t thread_id) const94251c0b2f7Stbbdev 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:
SimplePopBody(oneapi::tbb::concurrent_bounded_queue<int> * _q,std::size_t hi_thr,std::size_t nitems)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
operator ()(std::size_t thread_id) const96751c0b2f7Stbbdev 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
TestAbort()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>
test_member_types()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>
pushpusher110951c0b2f7Stbbdev 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>
pushpusher111851c0b2f7Stbbdev 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>
poppopper112851c0b2f7Stbbdev 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>
poppopper113851c0b2f7Stbbdev 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
MoveOperationTrackerMoveOperationTracker114951c0b2f7Stbbdev MoveOperationTracker() {}
MoveOperationTrackerMoveOperationTracker115051c0b2f7Stbbdev MoveOperationTracker(const MoveOperationTracker&) {
115151c0b2f7Stbbdev ++copy_constructor_called_times;
115251c0b2f7Stbbdev }
MoveOperationTrackerMoveOperationTracker115351c0b2f7Stbbdev MoveOperationTracker(MoveOperationTracker&&) {
115451c0b2f7Stbbdev ++move_constructor_called_times;
115551c0b2f7Stbbdev }
operator =MoveOperationTracker115651c0b2f7Stbbdev MoveOperationTracker& operator=(MoveOperationTracker const&) {
115751c0b2f7Stbbdev ++copy_assignment_called_times;
115851c0b2f7Stbbdev return *this;
115951c0b2f7Stbbdev }
operator =MoveOperationTracker116051c0b2f7Stbbdev 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>
TestMoveSupport()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
TestMoveSupportInPushPop()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
allocator()121351c0b2f7Stbbdev allocator() : m_unique_id( 0 ) {}
allocator(size_t unique_id)121451c0b2f7Stbbdev allocator(size_t unique_id) { m_unique_id = unique_id; }
121551c0b2f7Stbbdev
~allocator()1216fbc48b39Svlserov ~allocator() {
1217fbc48b39Svlserov REQUIRE_MESSAGE(state == LIVE, "Destroyed allocator has been used.");
1218fbc48b39Svlserov state = DEAD;
1219fbc48b39Svlserov }
1220fbc48b39Svlserov
122151c0b2f7Stbbdev template<typename U>
allocator(const allocator<U> & a)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
operator ==(const allocator & lhs,const allocator & rhs)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>
AssertEquality(Queue & q,const std::vector<typename Queue::value_type> & vec)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>
AssertEmptiness(Queue & q)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>
FillTest(Queue & q,const std::vector<typename Queue::value_type> & vec)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>
EmptyTest(Queue & q,const std::vector<typename Queue::value_type> & vec)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>
bounded_queue_specific_test(oneapi::tbb::concurrent_queue<T,A> &,const std::vector<T> &)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>
bounded_queue_specific_test(oneapi::tbb::concurrent_bounded_queue<T,A> & q,const std::vector<T> & vec)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>
TestQueueOperabilityAfterDataMove(CQ & queue)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>
TestMoveConstructors()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);
13228155aaebSkboyarinov 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
TestMoveConstruction()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:
NonTrivialConstructorType(int a=0)136351c0b2f7Stbbdev NonTrivialConstructorType( int a = 0 ) : m_a( a ), m_str( "" ) {}
NonTrivialConstructorType(const std::string & str)136451c0b2f7Stbbdev NonTrivialConstructorType( const std::string& str ) : m_a( 0 ), m_str( str ) {}
NonTrivialConstructorType(int a,const std::string & str)136551c0b2f7Stbbdev NonTrivialConstructorType( int a, const std::string& str ) : m_a( a ), m_str( str ) {}
get_a() const136651c0b2f7Stbbdev int get_a() const { return m_a; }
get_str() const136751c0b2f7Stbbdev 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>
emplaceemplacer137851c0b2f7Stbbdev 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>
emplaceemplacer138451c0b2f7Stbbdev 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>
TestEmplaceInQueue()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 }
TestEmplace()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>
TestDeductionGuides()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>
TestQueueIteratorComparisonsBasic(QueueType & q)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>
TestQueueIteratorComparisons()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
1550*c4a799dfSJhaShweta1 //! Test concurrent operations support
155151c0b2f7Stbbdev //! \brief \ref requirement
1552*c4a799dfSJhaShweta1 TEST_CASE("testing concurrent operations 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 }
15888155aaebSkboyarinov
15898155aaebSkboyarinov class MinimalisticObject {
15908155aaebSkboyarinov public:
15918155aaebSkboyarinov struct flag {};
15928155aaebSkboyarinov
15938155aaebSkboyarinov MinimalisticObject() = delete;
MinimalisticObject(flag)15948155aaebSkboyarinov MinimalisticObject(flag) : underlying_obj(default_obj) {}
15958155aaebSkboyarinov
15968155aaebSkboyarinov MinimalisticObject(const MinimalisticObject&) = delete;
15978155aaebSkboyarinov MinimalisticObject& operator=(const MinimalisticObject&) = delete;
15988155aaebSkboyarinov
get_obj() const15998155aaebSkboyarinov std::size_t get_obj() const { return underlying_obj; }
get_default_obj() const16008155aaebSkboyarinov std::size_t get_default_obj() const { return default_obj; }
16018155aaebSkboyarinov
16028155aaebSkboyarinov protected:
16038155aaebSkboyarinov static constexpr std::size_t default_obj = 42;
16048155aaebSkboyarinov std::size_t underlying_obj;
16058155aaebSkboyarinov friend struct MoveAssignableMinimalisticObject;
16068155aaebSkboyarinov };
16078155aaebSkboyarinov
16088155aaebSkboyarinov struct MoveAssignableMinimalisticObject : MinimalisticObject {
16098155aaebSkboyarinov public:
16108155aaebSkboyarinov using MinimalisticObject::MinimalisticObject;
16118155aaebSkboyarinov
operator =MoveAssignableMinimalisticObject16128155aaebSkboyarinov MoveAssignableMinimalisticObject& operator=(MoveAssignableMinimalisticObject&& other) {
16138155aaebSkboyarinov if (this != &other) {
16148155aaebSkboyarinov underlying_obj = other.underlying_obj;
16158155aaebSkboyarinov other.underlying_obj = 0;
16168155aaebSkboyarinov }
16178155aaebSkboyarinov return *this;
16188155aaebSkboyarinov }
16198155aaebSkboyarinov };
16208155aaebSkboyarinov
16218155aaebSkboyarinov template <typename Container>
test_basics(Container & container,std::size_t desired_size)16228155aaebSkboyarinov void test_basics(Container& container, std::size_t desired_size) {
16238155aaebSkboyarinov CHECK(!container.empty());
16248155aaebSkboyarinov
16258155aaebSkboyarinov std::size_t counter = 0;
16268155aaebSkboyarinov for (auto it = container.unsafe_begin(); it != container.unsafe_end(); ++it) {
16278155aaebSkboyarinov CHECK(it->get_obj() == it->get_default_obj());
16288155aaebSkboyarinov ++counter;
16298155aaebSkboyarinov }
16308155aaebSkboyarinov CHECK(counter == desired_size);
16318155aaebSkboyarinov
16328155aaebSkboyarinov container.clear();
16338155aaebSkboyarinov CHECK(container.empty());
16348155aaebSkboyarinov }
16358155aaebSkboyarinov
16368155aaebSkboyarinov template <template <class...> class Container>
test_with_minimalistic_objects()16378155aaebSkboyarinov void test_with_minimalistic_objects() {
16388155aaebSkboyarinov // Test with MinimalisticObject and no pop operations
16398155aaebSkboyarinov const std::size_t elements_count = 100;
16408155aaebSkboyarinov {
16418155aaebSkboyarinov Container<MinimalisticObject> default_container;
16428155aaebSkboyarinov
16438155aaebSkboyarinov for (std::size_t i = 0; i < elements_count; ++i) {
16448155aaebSkboyarinov default_container.emplace(MinimalisticObject::flag{});
16458155aaebSkboyarinov }
16468155aaebSkboyarinov test_basics(default_container, elements_count);
16478155aaebSkboyarinov }
16488155aaebSkboyarinov // Test with MoveAssignableMinimalisticObject with pop operation
16498155aaebSkboyarinov {
16508155aaebSkboyarinov Container<MoveAssignableMinimalisticObject> default_container;
16518155aaebSkboyarinov
16528155aaebSkboyarinov for (std::size_t i = 0; i < elements_count; ++i) {
16538155aaebSkboyarinov default_container.emplace(MinimalisticObject::flag{});
16548155aaebSkboyarinov }
16558155aaebSkboyarinov test_basics(default_container, elements_count);
16568155aaebSkboyarinov
16578155aaebSkboyarinov // Refill again
16588155aaebSkboyarinov for (std::size_t i = 0; i < elements_count; ++i) {
16598155aaebSkboyarinov default_container.emplace(MinimalisticObject::flag{});
16608155aaebSkboyarinov }
16618155aaebSkboyarinov
16628155aaebSkboyarinov MoveAssignableMinimalisticObject result(MinimalisticObject::flag{});
16638155aaebSkboyarinov
16648155aaebSkboyarinov std::size_t element_counter = 0;
16658155aaebSkboyarinov while (!default_container.empty()) {
16668155aaebSkboyarinov CHECK(default_container.try_pop(result));
16678155aaebSkboyarinov ++element_counter;
16688155aaebSkboyarinov }
16698155aaebSkboyarinov
16708155aaebSkboyarinov CHECK(element_counter == elements_count);
16718155aaebSkboyarinov CHECK(default_container.empty());
16728155aaebSkboyarinov }
16738155aaebSkboyarinov }
16748155aaebSkboyarinov
16758155aaebSkboyarinov //! \brief \ref requirement
16768155aaebSkboyarinov TEST_CASE("Test with minimalistic object type") {
16778155aaebSkboyarinov test_with_minimalistic_objects<oneapi::tbb::concurrent_queue>();
16788155aaebSkboyarinov test_with_minimalistic_objects<oneapi::tbb::concurrent_bounded_queue>();
16798155aaebSkboyarinov }
1680155acce7SJhaShweta1
1681155acce7SJhaShweta1 //TODO: Once support for std::allocator_traits::propagate_on_container_* is implemented,
1682155acce7SJhaShweta1 // most of the 4 test cases below can be replaced with move_support_tests::test_*.
1683155acce7SJhaShweta1
1684155acce7SJhaShweta1 template<typename CQ>
test_queue_helper()1685155acce7SJhaShweta1 void test_queue_helper() {
1686155acce7SJhaShweta1 int size = 5;
1687155acce7SJhaShweta1 typename CQ::value_type vec_1(size, 0), vec_2(size, 0), vec_3(size, 0), vec_4(size, 0);
1688155acce7SJhaShweta1 srand(static_cast<unsigned>(time(0)));
1689155acce7SJhaShweta1 generate(vec_1.begin(), vec_1.end(), rand);
1690155acce7SJhaShweta1 generate(vec_2.begin(), vec_2.end(), rand);
1691155acce7SJhaShweta1 generate(vec_3.begin(), vec_3.end(), rand);
1692155acce7SJhaShweta1 generate(vec_4.begin(), vec_4.end(), rand);
1693155acce7SJhaShweta1
1694155acce7SJhaShweta1 CQ q1, q2, q3;
1695155acce7SJhaShweta1 q3 = {vec_4, vec_2, vec_3};
1696155acce7SJhaShweta1 CQ q4({vec_1, vec_2, vec_3});
1697155acce7SJhaShweta1
1698155acce7SJhaShweta1 q1 = q3;
1699155acce7SJhaShweta1 q2 = std::move(q3);
1700155acce7SJhaShweta1 CHECK(q3.empty());
1701155acce7SJhaShweta1
1702155acce7SJhaShweta1 CHECK(q1 != q4);
1703155acce7SJhaShweta1 q1.swap(q4);
1704155acce7SJhaShweta1 CHECK(q2 == q4);
1705155acce7SJhaShweta1
1706155acce7SJhaShweta1 swap(q2, q3);
1707155acce7SJhaShweta1 CHECK(q2.empty());
1708155acce7SJhaShweta1 CHECK(q3 == q4);
1709155acce7SJhaShweta1 }
1710155acce7SJhaShweta1
1711155acce7SJhaShweta1 //! Test assignment (copy/move/initializer_list) and swapping
1712155acce7SJhaShweta1 //! \brief \ref interface \ref requirement
1713155acce7SJhaShweta1 TEST_CASE("testing assignment and swapping") {
1714155acce7SJhaShweta1 test_queue_helper<tbb::concurrent_queue<std::vector<int>>>();
1715155acce7SJhaShweta1 test_queue_helper<tbb::concurrent_bounded_queue<std::vector<int>>>();
1716155acce7SJhaShweta1 }
1717155acce7SJhaShweta1
1718155acce7SJhaShweta1 template <typename QueueType>
TestMoveQueue()1719155acce7SJhaShweta1 void TestMoveQueue() {
1720155acce7SJhaShweta1 using allocator_type = typename QueueType::allocator_type;
1721155acce7SJhaShweta1
1722155acce7SJhaShweta1 QueueType q1, q2;
1723155acce7SJhaShweta1 move_support_tests::Foo obj;
1724155acce7SJhaShweta1 size_t n1(15), n2(7);
1725155acce7SJhaShweta1
1726155acce7SJhaShweta1 allocator_type::init_counters();
1727155acce7SJhaShweta1 for(size_t i =0; i < n1; i++)
1728155acce7SJhaShweta1 q1.push(obj);
1729155acce7SJhaShweta1 size_t q1_items_constructed = allocator_type::items_constructed;
1730155acce7SJhaShweta1 size_t q1_items_allocated = allocator_type::items_allocated;
1731155acce7SJhaShweta1
1732155acce7SJhaShweta1 allocator_type::init_counters();
1733155acce7SJhaShweta1 for(size_t i =0; i < n2; i++)
1734155acce7SJhaShweta1 q2.push(obj);
1735155acce7SJhaShweta1 size_t q2_items_allocated = allocator_type::items_allocated;
1736155acce7SJhaShweta1
1737155acce7SJhaShweta1 allocator_type::init_counters();
1738155acce7SJhaShweta1 q1 = std::move(q2);
1739155acce7SJhaShweta1
1740155acce7SJhaShweta1 CHECK(q1_items_allocated == allocator_type::items_freed);
1741155acce7SJhaShweta1 CHECK(q1_items_constructed == allocator_type::items_destroyed);
1742155acce7SJhaShweta1 CHECK(q2_items_allocated >= allocator_type::items_allocated);
1743155acce7SJhaShweta1 }
1744155acce7SJhaShweta1
1745155acce7SJhaShweta1 //! move assignment test for equal counting allocator
1746155acce7SJhaShweta1 //! \brief \ref interface \ref requirement
1747155acce7SJhaShweta1 TEST_CASE("testing move assignment with equal counting allocators") {
1748155acce7SJhaShweta1 using allocator_type = StaticSharedCountingAllocator<std::allocator<move_support_tests::Foo>>;
1749155acce7SJhaShweta1 TestMoveQueue<tbb::concurrent_queue<move_support_tests::Foo, allocator_type>>();
1750155acce7SJhaShweta1 TestMoveQueue<tbb::concurrent_bounded_queue<move_support_tests::Foo, allocator_type>>();
1751155acce7SJhaShweta1 }
1752155acce7SJhaShweta1
1753155acce7SJhaShweta1 template<class T>
1754155acce7SJhaShweta1 struct stateful_allocator {
1755155acce7SJhaShweta1 typedef T value_type;
1756155acce7SJhaShweta1 stateful_allocator() = default;
1757155acce7SJhaShweta1 int state = 0;
1758155acce7SJhaShweta1 template<class U>
stateful_allocatorstateful_allocator1759155acce7SJhaShweta1 constexpr stateful_allocator(const stateful_allocator<U>& src) noexcept : state(src.state) {}
1760155acce7SJhaShweta1
allocatestateful_allocator1761155acce7SJhaShweta1 T* allocate(std::size_t n) {
1762155acce7SJhaShweta1 return static_cast<T*>(::operator new(n * sizeof(T)));
1763155acce7SJhaShweta1 }
1764155acce7SJhaShweta1
deallocatestateful_allocator1765155acce7SJhaShweta1 void deallocate(T* p, std::size_t) noexcept {
1766155acce7SJhaShweta1 ::operator delete(p);
1767155acce7SJhaShweta1 }
1768155acce7SJhaShweta1 };
1769155acce7SJhaShweta1
1770155acce7SJhaShweta1 template<class T, class U>
operator ==(const stateful_allocator<T> & lhs,const stateful_allocator<U> & rhs)1771155acce7SJhaShweta1 bool operator==(const stateful_allocator<T>& lhs, const stateful_allocator<U>& rhs) { return lhs.state == rhs.state; }
1772155acce7SJhaShweta1
1773155acce7SJhaShweta1 template<class T, class U>
operator !=(const stateful_allocator<T> & lhs,const stateful_allocator<U> & rhs)1774155acce7SJhaShweta1 bool operator!=(const stateful_allocator<T>& lhs, const stateful_allocator<U>& rhs) { return lhs.state != rhs.state; }
1775155acce7SJhaShweta1
1776155acce7SJhaShweta1 template <typename QueueType>
TestMoveQueueUnequal()1777155acce7SJhaShweta1 void TestMoveQueueUnequal() {
1778155acce7SJhaShweta1 using allocator_type = typename QueueType::allocator_type;
1779155acce7SJhaShweta1 allocator_type alloc1, alloc2;
1780155acce7SJhaShweta1 alloc1.state = 0;
1781155acce7SJhaShweta1 alloc2.state = 1;
1782155acce7SJhaShweta1
1783155acce7SJhaShweta1 QueueType q1(alloc1), q2(alloc2);
1784155acce7SJhaShweta1 move_support_tests::Foo obj;
1785155acce7SJhaShweta1 size_t n1(15), n2(7);
1786155acce7SJhaShweta1
1787155acce7SJhaShweta1 allocator_type::init_counters();
1788155acce7SJhaShweta1 for(size_t i =0; i < n1; i++)
1789155acce7SJhaShweta1 q1.push(obj);
1790155acce7SJhaShweta1
1791155acce7SJhaShweta1 allocator_type::init_counters();
1792155acce7SJhaShweta1 for(size_t i =0; i < n2; i++)
1793155acce7SJhaShweta1 q2.push(obj);
1794155acce7SJhaShweta1 size_t q2_items_allocated = allocator_type::items_allocated;
1795155acce7SJhaShweta1
1796155acce7SJhaShweta1 allocator_type::init_counters();
1797155acce7SJhaShweta1 q1 = std::move(q2);
1798155acce7SJhaShweta1
1799155acce7SJhaShweta1 REQUIRE_MESSAGE(allocator_type::items_allocated == q2_items_allocated, "More than expected memory allocated?");
1800155acce7SJhaShweta1 REQUIRE_MESSAGE(std::all_of(q1.unsafe_begin(), q1.unsafe_end(), is_state_predicate<move_support_tests::Foo::MoveInitialized>()),
1801155acce7SJhaShweta1 "Container did not move construct some elements");
1802155acce7SJhaShweta1 REQUIRE_MESSAGE(std::all_of(q2.unsafe_begin(), q2.unsafe_end(), is_state_predicate<move_support_tests::Foo::MovedFrom>()),
1803155acce7SJhaShweta1 "Container did not move all the elements");
1804155acce7SJhaShweta1 }
1805155acce7SJhaShweta1
1806155acce7SJhaShweta1 //! move assignment test for unequal counting allocator
1807155acce7SJhaShweta1 //! \brief \ref interface \ref requirement
1808155acce7SJhaShweta1 TEST_CASE("testing move assignment with unequal counting allocators") {
1809155acce7SJhaShweta1 using allocator_type = StaticSharedCountingAllocator<stateful_allocator<move_support_tests::Foo>>;
1810155acce7SJhaShweta1 TestMoveQueueUnequal<tbb::concurrent_queue<move_support_tests::Foo, allocator_type>>();
1811155acce7SJhaShweta1 TestMoveQueueUnequal<tbb::concurrent_bounded_queue<move_support_tests::Foo, allocator_type>>();
1812155acce7SJhaShweta1 }
1813155acce7SJhaShweta1
1814155acce7SJhaShweta1 template<typename Container>
test_check_move_allocator(Container & src,Container & dst,Container & cpy)1815155acce7SJhaShweta1 void test_check_move_allocator(Container& src, Container& dst, Container& cpy) {
1816155acce7SJhaShweta1 REQUIRE_MESSAGE(src.empty(), "Source didn't clear");
1817155acce7SJhaShweta1 REQUIRE_MESSAGE(std::equal(dst.unsafe_begin(), dst.unsafe_end(), cpy.unsafe_begin()), "Elements are not equal");
1818155acce7SJhaShweta1 }
1819155acce7SJhaShweta1
test_move_assignment_test_equal()1820155acce7SJhaShweta1 void test_move_assignment_test_equal() {
1821155acce7SJhaShweta1 int n = 5;
1822155acce7SJhaShweta1 std::vector<int> vect1(n, 10), vect2(n,20), vect3(n, 30);
1823155acce7SJhaShweta1
1824155acce7SJhaShweta1 tbb::concurrent_queue<std::vector<int>> src({vect1, vect2, vect3});
1825155acce7SJhaShweta1 tbb::concurrent_queue<std::vector<int>> dst(src.get_allocator());
1826155acce7SJhaShweta1 tbb::concurrent_queue<std::vector<int>> cpy(src.get_allocator());
1827155acce7SJhaShweta1 REQUIRE_MESSAGE(src.get_allocator() == dst.get_allocator(), "Incorrect test setup: allocators should be equal");
1828155acce7SJhaShweta1 cpy = src;
1829155acce7SJhaShweta1 dst = std::move(src);
1830155acce7SJhaShweta1
1831155acce7SJhaShweta1 tbb::concurrent_bounded_queue<std::vector<int>> src_bnd({vect1, vect2, vect3});
1832155acce7SJhaShweta1 tbb::concurrent_bounded_queue<std::vector<int>> dst_bnd(src_bnd.get_allocator());
1833155acce7SJhaShweta1 tbb::concurrent_bounded_queue<std::vector<int>> cpy_bnd(src_bnd.get_allocator());
1834155acce7SJhaShweta1 REQUIRE_MESSAGE(src_bnd.get_allocator() == dst_bnd.get_allocator(), "Incorrect test setup: allocators should be equal");
1835155acce7SJhaShweta1 cpy_bnd = src_bnd;
1836155acce7SJhaShweta1 dst_bnd = std::move(src_bnd);
1837155acce7SJhaShweta1
1838155acce7SJhaShweta1 test_check_move_allocator<tbb::concurrent_queue<std::vector<int>>>(src, dst, cpy);
1839155acce7SJhaShweta1 REQUIRE_MESSAGE(cpy.unsafe_size() == dst.unsafe_size(), "Queues are not equal");
1840155acce7SJhaShweta1
1841155acce7SJhaShweta1 test_check_move_allocator<tbb::concurrent_bounded_queue<std::vector<int>>>(src_bnd, dst_bnd, cpy_bnd);
1842155acce7SJhaShweta1 REQUIRE_MESSAGE(cpy_bnd.size() == dst_bnd.size(), "Queues are not equal");
1843155acce7SJhaShweta1 }
1844155acce7SJhaShweta1
test_move_assignment_test_unequal()1845155acce7SJhaShweta1 void test_move_assignment_test_unequal() {
1846155acce7SJhaShweta1 stateful_allocator<int> src_alloc;
1847155acce7SJhaShweta1 src_alloc.state = 0;
1848155acce7SJhaShweta1 std::vector<int, stateful_allocator<int>> v(8, 0, src_alloc);
1849155acce7SJhaShweta1 tbb::concurrent_queue<std::vector<int, stateful_allocator<int>>, stateful_allocator<int>> src(src_alloc);
1850155acce7SJhaShweta1
1851155acce7SJhaShweta1 v.push_back(42);
1852155acce7SJhaShweta1 v.push_back(82);
1853155acce7SJhaShweta1 src.push(v);
1854155acce7SJhaShweta1 src.push(v);
1855155acce7SJhaShweta1
1856155acce7SJhaShweta1 stateful_allocator<int> dst_alloc;
1857155acce7SJhaShweta1 dst_alloc.state = 1;
1858155acce7SJhaShweta1 tbb::concurrent_queue<std::vector<int, stateful_allocator<int>>, stateful_allocator<int>> dst(dst_alloc);
1859155acce7SJhaShweta1 tbb::concurrent_queue<std::vector<int, stateful_allocator<int>>, stateful_allocator<int>> cpy(src_alloc);
1860155acce7SJhaShweta1 REQUIRE_MESSAGE(src.get_allocator() != dst.get_allocator(), "Incorrect test setup: allocators should be unequal");
1861155acce7SJhaShweta1 cpy = src;
1862155acce7SJhaShweta1 dst = std::move(src);
1863155acce7SJhaShweta1
1864155acce7SJhaShweta1 tbb::concurrent_bounded_queue<std::vector<int, stateful_allocator<int>>, stateful_allocator<int>> src_bnd(src_alloc);
1865155acce7SJhaShweta1 tbb::concurrent_bounded_queue<std::vector<int, stateful_allocator<int>>, stateful_allocator<int>> dst_bnd(dst_alloc);
1866155acce7SJhaShweta1 tbb::concurrent_bounded_queue<std::vector<int, stateful_allocator<int>>, stateful_allocator<int>> cpy_bnd(src_alloc);
1867155acce7SJhaShweta1 REQUIRE_MESSAGE(src_bnd.get_allocator() != dst_bnd.get_allocator(), "Incorrect test setup: allocators should be unequal");
1868155acce7SJhaShweta1 src_bnd.push(v);
1869155acce7SJhaShweta1 src_bnd.push(v);
1870155acce7SJhaShweta1 cpy_bnd = src_bnd;
1871155acce7SJhaShweta1 dst_bnd = std::move(src_bnd);
1872155acce7SJhaShweta1
1873155acce7SJhaShweta1 test_check_move_allocator<tbb::concurrent_queue<std::vector<int, stateful_allocator<int>>, stateful_allocator<int>>>(src, dst, cpy);
1874155acce7SJhaShweta1 REQUIRE_MESSAGE(dst.unsafe_size() == cpy.unsafe_size(), "Queues are not equal");
1875155acce7SJhaShweta1
1876155acce7SJhaShweta1 test_check_move_allocator<tbb::concurrent_bounded_queue<std::vector<int, stateful_allocator<int>>, stateful_allocator<int>>>(src_bnd, dst_bnd, cpy_bnd);
1877155acce7SJhaShweta1 REQUIRE_MESSAGE(dst_bnd.size() == cpy_bnd.size(), "Queues are not equal");
1878155acce7SJhaShweta1 }
1879155acce7SJhaShweta1
1880155acce7SJhaShweta1 //! move assignment test for equal and unequal allocator
1881155acce7SJhaShweta1 //! \brief \ref interface \ref requirement
1882155acce7SJhaShweta1 TEST_CASE("testing move assignment with equal and unequal allocators") {
1883155acce7SJhaShweta1 test_move_assignment_test_equal();
1884155acce7SJhaShweta1 test_move_assignment_test_unequal();
1885155acce7SJhaShweta1 }
1886