151c0b2f7Stbbdev /* 2*b15aabb3Stbbdev Copyright (c) 2005-2021 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> 22*b15aabb3Stbbdev #include <common/test_comparisons.h> 2351c0b2f7Stbbdev 2449e08aacStbbdev #include "oneapi/tbb/concurrent_queue.h" 2549e08aacStbbdev #include "oneapi/tbb/cache_aligned_allocator.h" 2651c0b2f7Stbbdev #include <type_traits> 2751c0b2f7Stbbdev #include <atomic> 2851c0b2f7Stbbdev 2951c0b2f7Stbbdev //! \file conformance_concurrent_queue.cpp 3051c0b2f7Stbbdev //! \brief Test for [containers.concurrent_queue containers.concurrent_bounded_queue] specification 3151c0b2f7Stbbdev 3251c0b2f7Stbbdev template <typename T> 3349e08aacStbbdev using test_allocator = StaticSharedCountingAllocator<oneapi::tbb::cache_aligned_allocator<T>>; 3451c0b2f7Stbbdev 3551c0b2f7Stbbdev static constexpr std::size_t MinThread = 1; 3651c0b2f7Stbbdev static constexpr std::size_t MaxThread = 4; 3751c0b2f7Stbbdev 3851c0b2f7Stbbdev static constexpr std::size_t MAXTHREAD = 256; 3951c0b2f7Stbbdev 4051c0b2f7Stbbdev static constexpr std::size_t M = 10000; 4151c0b2f7Stbbdev static std::atomic<long> PopKind[3]; 4251c0b2f7Stbbdev 4351c0b2f7Stbbdev static int Sum[MAXTHREAD]; 4451c0b2f7Stbbdev 4551c0b2f7Stbbdev template<typename CQ, typename ValueType, typename CounterType> 4651c0b2f7Stbbdev void push(CQ& q, ValueType v, CounterType i) { 4751c0b2f7Stbbdev switch (i % 3) { 4851c0b2f7Stbbdev case 0: q.push( v); break; 4951c0b2f7Stbbdev case 1: q.push( std::move(v)); break; 5051c0b2f7Stbbdev case 2: q.emplace( v); break; 5151c0b2f7Stbbdev default: CHECK(false); break; 5251c0b2f7Stbbdev } 5351c0b2f7Stbbdev } 5451c0b2f7Stbbdev 5551c0b2f7Stbbdev template<typename T> 5649e08aacStbbdev class ConcQWithCapacity : public oneapi::tbb::concurrent_queue<T, test_allocator<T>> { 5749e08aacStbbdev using base_type = oneapi::tbb::concurrent_queue<T, test_allocator<T>>; 5851c0b2f7Stbbdev public: 5951c0b2f7Stbbdev ConcQWithCapacity() : my_capacity( std::size_t(-1) / (sizeof(void*) + sizeof(T)) ) {} 6051c0b2f7Stbbdev std::size_t size() const { 6151c0b2f7Stbbdev return this->unsafe_size(); 6251c0b2f7Stbbdev } 6351c0b2f7Stbbdev 6451c0b2f7Stbbdev std::size_t capacity() const { 6551c0b2f7Stbbdev return my_capacity; 6651c0b2f7Stbbdev } 6751c0b2f7Stbbdev 6851c0b2f7Stbbdev void set_capacity( const std::size_t n ) { 6951c0b2f7Stbbdev my_capacity = n; 7051c0b2f7Stbbdev } 7151c0b2f7Stbbdev 7251c0b2f7Stbbdev bool try_push( const T& source ) { 7351c0b2f7Stbbdev base_type::push( source); 7451c0b2f7Stbbdev return source.get_serial() < my_capacity; 7551c0b2f7Stbbdev } 7651c0b2f7Stbbdev 7751c0b2f7Stbbdev bool try_pop( T& dest ) { 7851c0b2f7Stbbdev base_type::try_pop( dest); 7951c0b2f7Stbbdev return dest.get_serial() < my_capacity; 8051c0b2f7Stbbdev } 8151c0b2f7Stbbdev 8251c0b2f7Stbbdev private: 8351c0b2f7Stbbdev std::size_t my_capacity; 8451c0b2f7Stbbdev }; 8551c0b2f7Stbbdev 8651c0b2f7Stbbdev template<typename CQ, typename T> 8751c0b2f7Stbbdev void TestEmptyQueue() { 8851c0b2f7Stbbdev const CQ queue; 8951c0b2f7Stbbdev CHECK(queue.size() == 0); 9051c0b2f7Stbbdev CHECK(queue.capacity()> 0); 9151c0b2f7Stbbdev CHECK(size_t(queue.capacity())>= std::size_t(-1)/(sizeof(void*)+sizeof(T))); 9251c0b2f7Stbbdev } 9351c0b2f7Stbbdev 9451c0b2f7Stbbdev void TestEmptiness() { 9551c0b2f7Stbbdev TestEmptyQueue<ConcQWithCapacity<char>, char>(); 9651c0b2f7Stbbdev TestEmptyQueue<ConcQWithCapacity<move_support_tests::Foo>, move_support_tests::Foo>(); 9749e08aacStbbdev TestEmptyQueue<oneapi::tbb::concurrent_bounded_queue<char, test_allocator<char>>, char>(); 9849e08aacStbbdev TestEmptyQueue<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, 9951c0b2f7Stbbdev test_allocator<move_support_tests::Foo>>, move_support_tests::Foo>(); 10051c0b2f7Stbbdev } 10151c0b2f7Stbbdev 10251c0b2f7Stbbdev template<typename CQ, typename T> 10351c0b2f7Stbbdev void TestFullQueue() { 10451c0b2f7Stbbdev using allocator_type = decltype(std::declval<CQ>().get_allocator()); 10551c0b2f7Stbbdev 10651c0b2f7Stbbdev for (std::size_t n = 0; n < 100; ++n) { 10751c0b2f7Stbbdev allocator_type::init_counters(); 10851c0b2f7Stbbdev { 10951c0b2f7Stbbdev CQ queue; 11051c0b2f7Stbbdev queue.set_capacity(n); 11151c0b2f7Stbbdev for (std::size_t i = 0; i <= n; ++i) { 11251c0b2f7Stbbdev T f; 11351c0b2f7Stbbdev f.set_serial(i); 11451c0b2f7Stbbdev bool result = queue.try_push( f); 11551c0b2f7Stbbdev CHECK((result == (i < n))); 11651c0b2f7Stbbdev } 11751c0b2f7Stbbdev 11851c0b2f7Stbbdev for (std::size_t i = 0; i <= n; ++i) { 11951c0b2f7Stbbdev T f; 12051c0b2f7Stbbdev bool result = queue.try_pop(f); 12151c0b2f7Stbbdev CHECK((result == (i < n))); 12251c0b2f7Stbbdev CHECK((result == 0 || f.get_serial() == i)); 12351c0b2f7Stbbdev } 12451c0b2f7Stbbdev } 12551c0b2f7Stbbdev CHECK(allocator_type::items_allocated == allocator_type::items_freed); 12651c0b2f7Stbbdev CHECK(allocator_type::allocations == allocator_type::frees); 12751c0b2f7Stbbdev } 12851c0b2f7Stbbdev } 12951c0b2f7Stbbdev 13051c0b2f7Stbbdev void TestFullness() { 13151c0b2f7Stbbdev TestFullQueue<ConcQWithCapacity<move_support_tests::Foo>, move_support_tests::Foo>(); 13249e08aacStbbdev TestFullQueue<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, move_support_tests::Foo>(); 13351c0b2f7Stbbdev } 13451c0b2f7Stbbdev 13551c0b2f7Stbbdev template<typename CQ> 13651c0b2f7Stbbdev void TestClear() { 13751c0b2f7Stbbdev using allocator_type = decltype(std::declval<CQ>().get_allocator()); 13851c0b2f7Stbbdev allocator_type::init_counters(); 13951c0b2f7Stbbdev const std::size_t n = 5; 14051c0b2f7Stbbdev 14151c0b2f7Stbbdev CQ queue; 14251c0b2f7Stbbdev const std::size_t q_capacity = 10; 14351c0b2f7Stbbdev queue.set_capacity(q_capacity); 14451c0b2f7Stbbdev 14551c0b2f7Stbbdev for (std::size_t i = 0; i < n; ++i) { 14651c0b2f7Stbbdev move_support_tests::Foo f; 14751c0b2f7Stbbdev f.set_serial(i); 14851c0b2f7Stbbdev queue.push(f); 14951c0b2f7Stbbdev } 15051c0b2f7Stbbdev 15151c0b2f7Stbbdev CHECK(queue.size() == n); 15251c0b2f7Stbbdev 15351c0b2f7Stbbdev queue.clear(); 15451c0b2f7Stbbdev CHECK(queue.size()==0); 15551c0b2f7Stbbdev for (std::size_t i = 0; i < n; ++i) { 15651c0b2f7Stbbdev move_support_tests::Foo f; 15751c0b2f7Stbbdev f.set_serial(i); 15851c0b2f7Stbbdev queue.push( f); 15951c0b2f7Stbbdev } 16051c0b2f7Stbbdev 16151c0b2f7Stbbdev CHECK(queue.size() == n); 16251c0b2f7Stbbdev queue.clear(); 16351c0b2f7Stbbdev CHECK(queue.size() == 0); 16451c0b2f7Stbbdev 16551c0b2f7Stbbdev for (std::size_t i = 0; i < n; ++i) { 16651c0b2f7Stbbdev move_support_tests::Foo f; 16751c0b2f7Stbbdev f.set_serial(i); 16851c0b2f7Stbbdev queue.push(f); 16951c0b2f7Stbbdev } 17051c0b2f7Stbbdev 17151c0b2f7Stbbdev CHECK(queue.size()==n); 17251c0b2f7Stbbdev } 17351c0b2f7Stbbdev 17451c0b2f7Stbbdev void TestClearWorks() { 17551c0b2f7Stbbdev TestClear<ConcQWithCapacity<move_support_tests::Foo>>(); 17649e08aacStbbdev TestClear<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>(); 17751c0b2f7Stbbdev } 17851c0b2f7Stbbdev 17951c0b2f7Stbbdev template<typename Iterator1, typename Iterator2> 18051c0b2f7Stbbdev void TestIteratorAux( Iterator1 i, Iterator2 j, int size ) { 18151c0b2f7Stbbdev Iterator1 old_i; // assigned at first iteration below 18251c0b2f7Stbbdev for (std::size_t k = 0; k < (std::size_t)size; ++k) { 183*b15aabb3Stbbdev CHECK_FAST(i != j); 184*b15aabb3Stbbdev CHECK_FAST(!(i == j)); 18551c0b2f7Stbbdev // Test "->" 186*b15aabb3Stbbdev CHECK_FAST((k+1 == i->get_serial())); 18751c0b2f7Stbbdev if (k & 1) { 18851c0b2f7Stbbdev // Test post-increment 18951c0b2f7Stbbdev move_support_tests::Foo f = *old_i++; 190*b15aabb3Stbbdev 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; 197*b15aabb3Stbbdev CHECK_FAST((k + 2 == f.get_serial())); 19851c0b2f7Stbbdev } else ++i; 19951c0b2f7Stbbdev // Test assignment 20051c0b2f7Stbbdev old_i = i; 20151c0b2f7Stbbdev } 20251c0b2f7Stbbdev } 203*b15aabb3Stbbdev CHECK_FAST(!(i != j)); 204*b15aabb3Stbbdev CHECK_FAST(i == j); 20551c0b2f7Stbbdev } 20651c0b2f7Stbbdev 20751c0b2f7Stbbdev template<typename Iterator1, typename Iterator2> 20851c0b2f7Stbbdev void TestIteratorAssignment( Iterator2 j ) { 20951c0b2f7Stbbdev Iterator1 i(j); 21051c0b2f7Stbbdev CHECK(i == j); 21151c0b2f7Stbbdev CHECK(!(i != j)); 21251c0b2f7Stbbdev 21351c0b2f7Stbbdev Iterator1 k; 21451c0b2f7Stbbdev k = j; 21551c0b2f7Stbbdev CHECK(k == j); 21651c0b2f7Stbbdev CHECK(!(k != j)); 21751c0b2f7Stbbdev } 21851c0b2f7Stbbdev 21951c0b2f7Stbbdev template<typename Iterator, typename T> 22051c0b2f7Stbbdev void TestIteratorTraits() { 22151c0b2f7Stbbdev static_assert( std::is_same<typename Iterator::iterator_category, std::forward_iterator_tag>::value, "wrong iterator category"); 22251c0b2f7Stbbdev 22351c0b2f7Stbbdev T x; 22451c0b2f7Stbbdev 22551c0b2f7Stbbdev typename Iterator::reference xr = x; 22651c0b2f7Stbbdev typename Iterator::pointer xp = &x; 22751c0b2f7Stbbdev CHECK((&xr == xp)); 22851c0b2f7Stbbdev } 22951c0b2f7Stbbdev 23051c0b2f7Stbbdev // Test the iterators for concurrent_queue 23151c0b2f7Stbbdev template <typename CQ> 23251c0b2f7Stbbdev void TestIterator() { 23351c0b2f7Stbbdev CQ queue; 23451c0b2f7Stbbdev const CQ& const_queue = queue; 23551c0b2f7Stbbdev for (int j=0; j < 500; ++j) { 23651c0b2f7Stbbdev TestIteratorAux( queue.unsafe_begin() , queue.unsafe_end() , j); 23751c0b2f7Stbbdev TestIteratorAux( queue.unsafe_cbegin() , queue.unsafe_cend() , j); 23851c0b2f7Stbbdev TestIteratorAux( const_queue.unsafe_begin(), const_queue.unsafe_end(), j); 23951c0b2f7Stbbdev TestIteratorAux( const_queue.unsafe_begin(), queue.unsafe_end() , j); 24051c0b2f7Stbbdev TestIteratorAux( queue.unsafe_begin() , const_queue.unsafe_end(), j); 24151c0b2f7Stbbdev move_support_tests::Foo f; 24251c0b2f7Stbbdev f.set_serial(j+1); 24351c0b2f7Stbbdev queue.push(f); 24451c0b2f7Stbbdev } 24551c0b2f7Stbbdev TestIteratorAssignment<typename CQ::const_iterator>( const_queue.unsafe_begin()); 24651c0b2f7Stbbdev TestIteratorAssignment<typename CQ::const_iterator>( queue.unsafe_begin()); 24751c0b2f7Stbbdev TestIteratorAssignment<typename CQ::iterator>( queue.unsafe_begin()); 24851c0b2f7Stbbdev TestIteratorTraits<typename CQ::const_iterator, const move_support_tests::Foo>(); 24951c0b2f7Stbbdev TestIteratorTraits<typename CQ::iterator, move_support_tests::Foo>(); 25051c0b2f7Stbbdev } 25151c0b2f7Stbbdev 25251c0b2f7Stbbdev void TestQueueIteratorWorks() { 25349e08aacStbbdev TestIterator<oneapi::tbb::concurrent_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>(); 25449e08aacStbbdev TestIterator<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>>(); 25551c0b2f7Stbbdev } 25651c0b2f7Stbbdev 25749e08aacStbbdev // Define wrapper classes to test oneapi::tbb::concurrent_queue<T> 25849e08aacStbbdev template<typename T, typename A = oneapi::tbb::cache_aligned_allocator<T>> 25949e08aacStbbdev class ConcQWithSizeWrapper : public oneapi::tbb::concurrent_queue<T, A> { 26051c0b2f7Stbbdev public: 26151c0b2f7Stbbdev ConcQWithSizeWrapper() {} 26249e08aacStbbdev ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q ) : oneapi::tbb::concurrent_queue<T, A>(q) {} 26349e08aacStbbdev ConcQWithSizeWrapper( const ConcQWithSizeWrapper& q, const A& a ) : oneapi::tbb::concurrent_queue<T, A>(q, a) {} 26449e08aacStbbdev ConcQWithSizeWrapper( const A& a ) : oneapi::tbb::concurrent_queue<T, A>( a ) {} 26551c0b2f7Stbbdev 26649e08aacStbbdev ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q ) : oneapi::tbb::concurrent_queue<T>(std::move(q)) {} 26751c0b2f7Stbbdev ConcQWithSizeWrapper( ConcQWithSizeWrapper&& q, const A& a ) 26849e08aacStbbdev : oneapi::tbb::concurrent_queue<T, A>(std::move(q), a) { } 26951c0b2f7Stbbdev 27051c0b2f7Stbbdev template<typename InputIterator> 27151c0b2f7Stbbdev ConcQWithSizeWrapper( InputIterator begin, InputIterator end, const A& a = A() ) 27249e08aacStbbdev : oneapi::tbb::concurrent_queue<T, A>(begin, end, a) {} 27349e08aacStbbdev typename oneapi::tbb::concurrent_queue<T, A>::size_type size() const { return this->unsafe_size(); } 27451c0b2f7Stbbdev }; 27551c0b2f7Stbbdev 27651c0b2f7Stbbdev enum state_type { 27751c0b2f7Stbbdev LIVE = 0x1234, 27851c0b2f7Stbbdev DEAD = 0xDEAD 27951c0b2f7Stbbdev }; 28051c0b2f7Stbbdev 28151c0b2f7Stbbdev class Bar { 28251c0b2f7Stbbdev state_type state; 28351c0b2f7Stbbdev public: 28451c0b2f7Stbbdev static std::size_t construction_num, destruction_num; 28551c0b2f7Stbbdev std::ptrdiff_t my_id; 28651c0b2f7Stbbdev Bar() : state(LIVE), my_id(-1) 28751c0b2f7Stbbdev {} 28851c0b2f7Stbbdev 28951c0b2f7Stbbdev Bar( std::size_t _i ) : state(LIVE), my_id(_i) { construction_num++; } 29051c0b2f7Stbbdev 29151c0b2f7Stbbdev Bar( const Bar& a_bar ) : state(LIVE) { 292*b15aabb3Stbbdev CHECK_FAST(a_bar.state == LIVE); 29351c0b2f7Stbbdev my_id = a_bar.my_id; 29451c0b2f7Stbbdev construction_num++; 29551c0b2f7Stbbdev } 29651c0b2f7Stbbdev 29751c0b2f7Stbbdev ~Bar() { 298*b15aabb3Stbbdev CHECK_FAST(state == LIVE); 29951c0b2f7Stbbdev state = DEAD; 30051c0b2f7Stbbdev my_id = DEAD; 30151c0b2f7Stbbdev destruction_num++; 30251c0b2f7Stbbdev } 30351c0b2f7Stbbdev 30451c0b2f7Stbbdev void operator=( const Bar& a_bar ) { 305*b15aabb3Stbbdev CHECK_FAST(a_bar.state == LIVE); 306*b15aabb3Stbbdev CHECK_FAST(state == LIVE); 30751c0b2f7Stbbdev my_id = a_bar.my_id; 30851c0b2f7Stbbdev } 30951c0b2f7Stbbdev friend bool operator==( const Bar& bar1, const Bar& bar2 ) ; 31051c0b2f7Stbbdev }; 31151c0b2f7Stbbdev 31251c0b2f7Stbbdev std::size_t Bar::construction_num = 0; 31351c0b2f7Stbbdev std::size_t Bar::destruction_num = 0; 31451c0b2f7Stbbdev 31551c0b2f7Stbbdev bool operator==( const Bar& bar1, const Bar& bar2 ) { 316*b15aabb3Stbbdev CHECK_FAST(bar1.state == LIVE); 317*b15aabb3Stbbdev CHECK_FAST(bar2.state == LIVE); 31851c0b2f7Stbbdev return bar1.my_id == bar2.my_id; 31951c0b2f7Stbbdev } 32051c0b2f7Stbbdev 32151c0b2f7Stbbdev class BarIterator { 32251c0b2f7Stbbdev Bar* bar_ptr; 32351c0b2f7Stbbdev BarIterator(Bar* bp_) : bar_ptr(bp_) {} 32451c0b2f7Stbbdev public: 32551c0b2f7Stbbdev Bar& operator*() const { 32651c0b2f7Stbbdev return *bar_ptr; 32751c0b2f7Stbbdev } 32851c0b2f7Stbbdev BarIterator& operator++() { 32951c0b2f7Stbbdev ++bar_ptr; 33051c0b2f7Stbbdev return *this; 33151c0b2f7Stbbdev } 33251c0b2f7Stbbdev Bar* operator++(int) { 33351c0b2f7Stbbdev Bar* result = &operator*(); 33451c0b2f7Stbbdev operator++(); 33551c0b2f7Stbbdev return result; 33651c0b2f7Stbbdev } 33751c0b2f7Stbbdev friend bool operator==(const BarIterator& bia, const BarIterator& bib) ; 33851c0b2f7Stbbdev friend bool operator!=(const BarIterator& bia, const BarIterator& bib) ; 33951c0b2f7Stbbdev template<typename CQ, typename T, typename TIter, typename CQ_EX, typename T_EX> 34051c0b2f7Stbbdev friend void TestConstructors (); 34151c0b2f7Stbbdev } ; 34251c0b2f7Stbbdev 34351c0b2f7Stbbdev bool operator==(const BarIterator& bia, const BarIterator& bib) { 34451c0b2f7Stbbdev return bia.bar_ptr==bib.bar_ptr; 34551c0b2f7Stbbdev } 34651c0b2f7Stbbdev 34751c0b2f7Stbbdev bool operator!=(const BarIterator& bia, const BarIterator& bib) { 34851c0b2f7Stbbdev return bia.bar_ptr!=bib.bar_ptr; 34951c0b2f7Stbbdev } 35051c0b2f7Stbbdev 35151c0b2f7Stbbdev 35251c0b2f7Stbbdev class Bar_exception : public std::bad_alloc { 35351c0b2f7Stbbdev public: 35451c0b2f7Stbbdev virtual const char *what() const noexcept override { return "making the entry invalid"; } 35551c0b2f7Stbbdev virtual ~Bar_exception() noexcept {} 35651c0b2f7Stbbdev }; 35751c0b2f7Stbbdev 35851c0b2f7Stbbdev class BarEx { 35951c0b2f7Stbbdev static int count; 36051c0b2f7Stbbdev public: 36151c0b2f7Stbbdev state_type state; 36251c0b2f7Stbbdev typedef enum { 36351c0b2f7Stbbdev PREPARATION, 36451c0b2f7Stbbdev COPY_CONSTRUCT 36551c0b2f7Stbbdev } mode_type; 36651c0b2f7Stbbdev static mode_type mode; 36751c0b2f7Stbbdev std::ptrdiff_t my_id; 36851c0b2f7Stbbdev std::ptrdiff_t my_tilda_id; 36951c0b2f7Stbbdev 37051c0b2f7Stbbdev static int button; 37151c0b2f7Stbbdev 37251c0b2f7Stbbdev BarEx() : state(LIVE), my_id(-1), my_tilda_id(-1) 37351c0b2f7Stbbdev {} 37451c0b2f7Stbbdev 37551c0b2f7Stbbdev BarEx(std::size_t _i) : state(LIVE), my_id(_i), my_tilda_id(my_id^(-1)) 37651c0b2f7Stbbdev {} 37751c0b2f7Stbbdev 37851c0b2f7Stbbdev BarEx( const BarEx& a_bar ) : state(LIVE) { 379*b15aabb3Stbbdev CHECK_FAST(a_bar.state == LIVE); 38051c0b2f7Stbbdev my_id = a_bar.my_id; 38151c0b2f7Stbbdev if (mode == PREPARATION) 38251c0b2f7Stbbdev if (!(++count % 100)) { 38351c0b2f7Stbbdev TBB_TEST_THROW(Bar_exception()); 38451c0b2f7Stbbdev } 38551c0b2f7Stbbdev my_tilda_id = a_bar.my_tilda_id; 38651c0b2f7Stbbdev } 38751c0b2f7Stbbdev 38851c0b2f7Stbbdev ~BarEx() { 389*b15aabb3Stbbdev CHECK_FAST(state == LIVE); 39051c0b2f7Stbbdev state = DEAD; 39151c0b2f7Stbbdev my_id = DEAD; 39251c0b2f7Stbbdev } 39351c0b2f7Stbbdev static void set_mode( mode_type m ) { mode = m; } 39451c0b2f7Stbbdev 39551c0b2f7Stbbdev void operator=( const BarEx& a_bar ) { 396*b15aabb3Stbbdev CHECK_FAST(a_bar.state == LIVE); 397*b15aabb3Stbbdev CHECK_FAST(state == LIVE); 39851c0b2f7Stbbdev my_id = a_bar.my_id; 39951c0b2f7Stbbdev my_tilda_id = a_bar.my_tilda_id; 40051c0b2f7Stbbdev } 40151c0b2f7Stbbdev 40251c0b2f7Stbbdev friend bool operator==(const BarEx& bar1, const BarEx& bar2 ) ; 40351c0b2f7Stbbdev }; 40451c0b2f7Stbbdev 40551c0b2f7Stbbdev int BarEx::count = 0; 40651c0b2f7Stbbdev BarEx::mode_type BarEx::mode = BarEx::PREPARATION; 40751c0b2f7Stbbdev 40851c0b2f7Stbbdev bool operator==(const BarEx& bar1, const BarEx& bar2) { 409*b15aabb3Stbbdev CHECK_FAST(bar1.state == LIVE); 410*b15aabb3Stbbdev CHECK_FAST(bar2.state == LIVE); 411*b15aabb3Stbbdev CHECK_FAST((bar1.my_id ^ bar1.my_tilda_id) == -1); 412*b15aabb3Stbbdev CHECK_FAST((bar2.my_id ^ bar2.my_tilda_id) == -1); 41351c0b2f7Stbbdev return bar1.my_id == bar2.my_id && bar1.my_tilda_id == bar2.my_tilda_id; 41451c0b2f7Stbbdev } 41551c0b2f7Stbbdev 41651c0b2f7Stbbdev template<typename CQ, typename T, typename TIter, typename CQ_EX, typename T_EX> 41751c0b2f7Stbbdev void TestConstructors () { 41851c0b2f7Stbbdev CQ src_queue; 41951c0b2f7Stbbdev typename CQ::const_iterator dqb; 42051c0b2f7Stbbdev typename CQ::const_iterator dqe; 42151c0b2f7Stbbdev typename CQ::const_iterator iter; 422*b15aabb3Stbbdev using size_type = typename CQ::size_type; 42351c0b2f7Stbbdev 424*b15aabb3Stbbdev for (size_type size = 0; size < 1001; ++size) { 425*b15aabb3Stbbdev 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 433*b15aabb3Stbbdev CHECK_FAST_MESSAGE(src_queue.size() == dst_queue.size(), "different size"); 434*b15aabb3Stbbdev CHECK_FAST_MESSAGE(src_queue.size() == copy_with_alloc.size(), "different size"); 43551c0b2f7Stbbdev 43651c0b2f7Stbbdev src_queue.clear(); 43751c0b2f7Stbbdev } 43851c0b2f7Stbbdev 43951c0b2f7Stbbdev T bar_array[1001]; 440*b15aabb3Stbbdev for (size_type size=0; size < 1001; ++size) { 441*b15aabb3Stbbdev 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 450*b15aabb3Stbbdev CHECK_FAST(size == dst_queue2.size()); 451*b15aabb3Stbbdev CHECK_FAST(sab == TIter(bar_array+0)); 452*b15aabb3Stbbdev CHECK_FAST(sae == TIter(bar_array+size)); 45351c0b2f7Stbbdev 45451c0b2f7Stbbdev dqb = dst_queue2.unsafe_begin(); 45551c0b2f7Stbbdev dqe = dst_queue2.unsafe_end(); 456*b15aabb3Stbbdev auto res = std::mismatch(dqb, dqe, bar_array); 457*b15aabb3Stbbdev CHECK_FAST_MESSAGE(res.first == dqe, "unexpected element"); 458*b15aabb3Stbbdev 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; 468*b15aabb3Stbbdev 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 476*b15aabb3Stbbdev 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(); 481*b15aabb3Stbbdev auto res = std::mismatch(dqb, dqe, iter); 482*b15aabb3Stbbdev CHECK_FAST_MESSAGE(res.first == dqe, "unexpected element"); 483*b15aabb3Stbbdev 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(); 492*b15aabb3Stbbdev REQUIRE_MESSAGE(std::equal(dqb, dqe, iter), "unexpected element"); 49351c0b2f7Stbbdev 494*b15aabb3Stbbdev 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(); 509*b15aabb3Stbbdev auto res = std::mismatch(dqb, dqe, iter); 510*b15aabb3Stbbdev REQUIRE_MESSAGE(res.first == dqe, "unexpected element"); 511*b15aabb3Stbbdev 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; 517*b15aabb3Stbbdev 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); 535*b15aabb3Stbbdev 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 540*b15aabb3Stbbdev 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 546*b15aabb3Stbbdev auto res2 = std::mismatch(dqb_ex, dqe_ex, iter_ex); 547*b15aabb3Stbbdev CHECK_FAST_MESSAGE(res2.first == dqe_ex, "unexpected element"); 548*b15aabb3Stbbdev CHECK_FAST_MESSAGE(res2.second == src_queue_ex.unsafe_end(), "different size?"); 54951c0b2f7Stbbdev } 55051c0b2f7Stbbdev #endif 55151c0b2f7Stbbdev src_queue.clear(); 55251c0b2f7Stbbdev 553*b15aabb3Stbbdev for (size_type size = 0; size < 1001; ++size) { 554*b15aabb3Stbbdev 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(); 559*b15aabb3Stbbdev for (size_type i = 0; i < size; ++i, ++qit) { 56051c0b2f7Stbbdev locations[i] = &(*qit); 56151c0b2f7Stbbdev } 56251c0b2f7Stbbdev 563*b15aabb3Stbbdev size_type size_of_queue = src_queue.size(); 56451c0b2f7Stbbdev CQ dst_queue(std::move(src_queue)); 56551c0b2f7Stbbdev 566*b15aabb3Stbbdev CHECK_FAST_MESSAGE((src_queue.empty() && src_queue.size() == 0), "not working move constructor?"); 567*b15aabb3Stbbdev CHECK_FAST_MESSAGE((size == size_of_queue && size_of_queue == dst_queue.size()), "not working move constructor?"); 56851c0b2f7Stbbdev 569*b15aabb3Stbbdev CHECK_FAST_MESSAGE( 570*b15aabb3Stbbdev std::equal(locations.begin(), locations.end(), dst_queue.unsafe_begin(), [](const T* t1, const T& r2) { return t1 == &r2; }), 571*b15aabb3Stbbdev "there was data movement during move constructor" 572*b15aabb3Stbbdev ); 57351c0b2f7Stbbdev 574*b15aabb3Stbbdev 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 579*b15aabb3Stbbdev CHECK_FAST(pop_result); 580*b15aabb3Stbbdev CHECK_FAST(test == popped); 58151c0b2f7Stbbdev } 58251c0b2f7Stbbdev } 58351c0b2f7Stbbdev } 58451c0b2f7Stbbdev 58551c0b2f7Stbbdev void TestQueueConstructors() { 58651c0b2f7Stbbdev TestConstructors<ConcQWithSizeWrapper<Bar>, Bar, BarIterator, ConcQWithSizeWrapper<BarEx>, BarEx>(); 58749e08aacStbbdev TestConstructors<oneapi::tbb::concurrent_bounded_queue<Bar>, Bar, BarIterator, oneapi::tbb::concurrent_bounded_queue<BarEx>, BarEx>(); 58851c0b2f7Stbbdev } 58951c0b2f7Stbbdev 59051c0b2f7Stbbdev template<typename T> 59151c0b2f7Stbbdev struct TestNegativeQueueBody { 59249e08aacStbbdev oneapi::tbb::concurrent_bounded_queue<T>& queue; 59351c0b2f7Stbbdev const std::size_t nthread; 59449e08aacStbbdev TestNegativeQueueBody( oneapi::tbb::concurrent_bounded_queue<T>& q, std::size_t n ) : queue(q), nthread(n) {} 59551c0b2f7Stbbdev void operator()( std::size_t k ) const { 59651c0b2f7Stbbdev if (k == 0) { 59751c0b2f7Stbbdev int number_of_pops = int(nthread) - 1; 59851c0b2f7Stbbdev // Wait for all pops to pend. 59951c0b2f7Stbbdev while (int(queue.size())> -number_of_pops) { 600*b15aabb3Stbbdev utils::yield(); 60151c0b2f7Stbbdev } 60251c0b2f7Stbbdev 60351c0b2f7Stbbdev for (int i = 0; ; ++i) { 60451c0b2f7Stbbdev CHECK(queue.size() == std::size_t(i - number_of_pops)); 60551c0b2f7Stbbdev CHECK((queue.empty() == (queue.size() <= 0))); 60651c0b2f7Stbbdev if (i == number_of_pops) break; 60751c0b2f7Stbbdev // Satisfy another pop 60851c0b2f7Stbbdev queue.push(T()); 60951c0b2f7Stbbdev } 61051c0b2f7Stbbdev } else { 61151c0b2f7Stbbdev // Pop item from queue 61251c0b2f7Stbbdev T item; 61351c0b2f7Stbbdev queue.pop(item); 61451c0b2f7Stbbdev } 61551c0b2f7Stbbdev } 61651c0b2f7Stbbdev }; 61751c0b2f7Stbbdev 61851c0b2f7Stbbdev //! Test a queue with a negative size. 61951c0b2f7Stbbdev template<typename T> 62051c0b2f7Stbbdev void TestNegativeQueue( std::size_t nthread ) { 62149e08aacStbbdev oneapi::tbb::concurrent_bounded_queue<T> queue; 62251c0b2f7Stbbdev utils::NativeParallelFor( nthread, TestNegativeQueueBody<T>(queue,nthread)); 62351c0b2f7Stbbdev } 62451c0b2f7Stbbdev 62551c0b2f7Stbbdev template<typename T> 62649e08aacStbbdev class ConcQPushPopWrapper : public oneapi::tbb::concurrent_queue<T, test_allocator<T>> { 62751c0b2f7Stbbdev public: 62851c0b2f7Stbbdev ConcQPushPopWrapper() : my_capacity(std::size_t(-1) / (sizeof(void*) + sizeof(T))) 62951c0b2f7Stbbdev {} 63051c0b2f7Stbbdev 63151c0b2f7Stbbdev std::size_t size() const { return this->unsafe_size(); } 63251c0b2f7Stbbdev void set_capacity( const ptrdiff_t n ) { my_capacity = n; } 63351c0b2f7Stbbdev bool try_push( const T& source ) { return this->push( source); } 63449e08aacStbbdev bool try_pop( T& dest ) { return this->oneapi::tbb::concurrent_queue<T, test_allocator<T>>::try_pop(dest); } 63551c0b2f7Stbbdev std::size_t my_capacity; 63651c0b2f7Stbbdev }; 63751c0b2f7Stbbdev 63851c0b2f7Stbbdev template<typename CQ, typename T> 63951c0b2f7Stbbdev struct Body { 64051c0b2f7Stbbdev CQ* queue; 64151c0b2f7Stbbdev const std::size_t nthread; 64251c0b2f7Stbbdev Body( std::size_t nthread_ ) : nthread(nthread_) {} 64351c0b2f7Stbbdev void operator()( std::size_t thread_id ) const { 64451c0b2f7Stbbdev long pop_kind[3] = {0, 0, 0}; 64551c0b2f7Stbbdev std::size_t serial[MAXTHREAD + 1]; 64651c0b2f7Stbbdev memset(serial, 0, nthread * sizeof(std::size_t)); 64751c0b2f7Stbbdev CHECK(thread_id < nthread); 64851c0b2f7Stbbdev 64951c0b2f7Stbbdev long sum = 0; 65051c0b2f7Stbbdev for (std::size_t j = 0; j < M; ++j) { 65151c0b2f7Stbbdev T f; 65251c0b2f7Stbbdev f.set_thread_id(move_support_tests::serial_dead_state); 65351c0b2f7Stbbdev f.set_serial(move_support_tests::serial_dead_state); 65451c0b2f7Stbbdev bool prepopped = false; 65551c0b2f7Stbbdev if (j & 1) { 65651c0b2f7Stbbdev prepopped = queue->try_pop(f); 65751c0b2f7Stbbdev ++pop_kind[prepopped]; 65851c0b2f7Stbbdev } 65951c0b2f7Stbbdev T g; 66051c0b2f7Stbbdev g.set_thread_id(thread_id); 66151c0b2f7Stbbdev g.set_serial(j + 1); 66251c0b2f7Stbbdev push(*queue, g, j); 66351c0b2f7Stbbdev if (!prepopped) { 664*b15aabb3Stbbdev while(!(queue)->try_pop(f)) utils::yield(); 66551c0b2f7Stbbdev ++pop_kind[2]; 66651c0b2f7Stbbdev } 667*b15aabb3Stbbdev CHECK_FAST(f.get_thread_id() <= nthread); 668*b15aabb3Stbbdev 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> 679*b15aabb3Stbbdev 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; 699*b15aabb3Stbbdev 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); 704*b15aabb3Stbbdev CHECK_FAST(queue.size() == i + 1); 705*b15aabb3Stbbdev 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;) { 717*b15aabb3Stbbdev CHECK_FAST(!queue.empty()); 71851c0b2f7Stbbdev T f; 71951c0b2f7Stbbdev bool result = queue.try_pop(f); 720*b15aabb3Stbbdev CHECK_FAST(result); 721*b15aabb3Stbbdev CHECK_FAST(int(queue.size()) == i); 72251c0b2f7Stbbdev sum += int(f.get_serial()) - 1; 72351c0b2f7Stbbdev } 72451c0b2f7Stbbdev REQUIRE_MESSAGE(queue.empty(), "The queue should be empty"); 72551c0b2f7Stbbdev REQUIRE_MESSAGE(queue.size() == 0, "The queue should have zero size"); 72651c0b2f7Stbbdev if (sum != expected) { 72751c0b2f7Stbbdev REPORT("sum=%d expected=%d\n",sum,expected); 72851c0b2f7Stbbdev } 72951c0b2f7Stbbdev 73051c0b2f7Stbbdev success = true; 73151c0b2f7Stbbdev if (nthread> 1 && prefill == 0) { 73251c0b2f7Stbbdev // Check that pop_if_present got sufficient exercise 73351c0b2f7Stbbdev for (std::size_t k = 0; k < 2; ++k) { 73451c0b2f7Stbbdev const int min_requirement = 100; 73551c0b2f7Stbbdev const int max_trial = 20; 73651c0b2f7Stbbdev 73751c0b2f7Stbbdev if (PopKind[k] < min_requirement) { 73851c0b2f7Stbbdev if (trial>= max_trial) { 73951c0b2f7Stbbdev REPORT("Warning: %d threads had only %ld pop_if_present operations %s after %d trials (expected at least %d). " 74051c0b2f7Stbbdev "This problem may merely be unlucky scheduling. " 74151c0b2f7Stbbdev "Investigate only if it happens repeatedly.\n", 74251c0b2f7Stbbdev nthread, long(PopKind[k]), k==0?"failed":"succeeded", max_trial, min_requirement); 74351c0b2f7Stbbdev } else { 74451c0b2f7Stbbdev success = false; 74551c0b2f7Stbbdev } 74651c0b2f7Stbbdev } 74751c0b2f7Stbbdev } 74851c0b2f7Stbbdev } 74951c0b2f7Stbbdev } 75051c0b2f7Stbbdev } 75151c0b2f7Stbbdev 75251c0b2f7Stbbdev void TestConcurrentPushPop() { 75351c0b2f7Stbbdev for (std::size_t nthread = MinThread; nthread <= MaxThread; ++nthread) { 75451c0b2f7Stbbdev INFO(" Testing with "<< nthread << " thread(s)"); 75551c0b2f7Stbbdev TestNegativeQueue<move_support_tests::Foo>(nthread); 75651c0b2f7Stbbdev for (std::size_t prefill=0; prefill < 64; prefill += (1 + prefill / 3)) { 75751c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(-1), nthread); 75851c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(1), nthread); 75951c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(2), nthread); 76051c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(10), nthread); 76151c0b2f7Stbbdev TestPushPop<ConcQPushPopWrapper<move_support_tests::Foo>, move_support_tests::Foo>(prefill, std::ptrdiff_t(100), nthread); 76251c0b2f7Stbbdev } 76351c0b2f7Stbbdev for (std::size_t prefill = 0; prefill < 64; prefill += (1 + prefill / 3) ) { 76449e08aacStbbdev TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 76551c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(-1), nthread); 76649e08aacStbbdev TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 76751c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(1), nthread); 76849e08aacStbbdev TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 76951c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(2), nthread); 77049e08aacStbbdev TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 77151c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(10), nthread); 77249e08aacStbbdev TestPushPop<oneapi::tbb::concurrent_bounded_queue<move_support_tests::Foo, test_allocator<move_support_tests::Foo>>, 77351c0b2f7Stbbdev move_support_tests::Foo>(prefill, std::ptrdiff_t(100), nthread); 77451c0b2f7Stbbdev } 77551c0b2f7Stbbdev } 77651c0b2f7Stbbdev } 77751c0b2f7Stbbdev 77851c0b2f7Stbbdev class Foo_exception : public std::bad_alloc { 77951c0b2f7Stbbdev public: 78051c0b2f7Stbbdev virtual const char *what() const throw() override { return "out of Foo limit"; } 78151c0b2f7Stbbdev virtual ~Foo_exception() throw() {} 78251c0b2f7Stbbdev }; 78351c0b2f7Stbbdev 78451c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 78551c0b2f7Stbbdev static std::atomic<long> FooExConstructed; 78651c0b2f7Stbbdev static std::atomic<long> FooExDestroyed; 78751c0b2f7Stbbdev static std::atomic<long> serial_source; 78851c0b2f7Stbbdev static long MaxFooCount = 0; 78951c0b2f7Stbbdev static const long Threshold = 400; 79051c0b2f7Stbbdev 79151c0b2f7Stbbdev class FooEx { 79251c0b2f7Stbbdev state_type state; 79351c0b2f7Stbbdev public: 79451c0b2f7Stbbdev int serial; 79551c0b2f7Stbbdev FooEx() : state(LIVE) { 79651c0b2f7Stbbdev ++FooExConstructed; 79751c0b2f7Stbbdev serial = serial_source++; 79851c0b2f7Stbbdev } 79951c0b2f7Stbbdev 80051c0b2f7Stbbdev FooEx( const FooEx& item ) : state(LIVE) { 80151c0b2f7Stbbdev CHECK(item.state == LIVE); 80251c0b2f7Stbbdev ++FooExConstructed; 80351c0b2f7Stbbdev if (MaxFooCount && (FooExConstructed - FooExDestroyed) >= MaxFooCount) { // in push() 80451c0b2f7Stbbdev throw Foo_exception(); 80551c0b2f7Stbbdev } 80651c0b2f7Stbbdev serial = item.serial; 80751c0b2f7Stbbdev } 80851c0b2f7Stbbdev 80951c0b2f7Stbbdev ~FooEx() { 81051c0b2f7Stbbdev CHECK(state==LIVE); 81151c0b2f7Stbbdev ++FooExDestroyed; 81251c0b2f7Stbbdev state=DEAD; 81351c0b2f7Stbbdev serial=DEAD; 81451c0b2f7Stbbdev } 81551c0b2f7Stbbdev 81651c0b2f7Stbbdev void operator=( FooEx& item ) { 81751c0b2f7Stbbdev CHECK(item.state==LIVE); 81851c0b2f7Stbbdev CHECK(state==LIVE); 81951c0b2f7Stbbdev serial = item.serial; 82051c0b2f7Stbbdev if( MaxFooCount==2*Threshold && (FooExConstructed-FooExDestroyed) <= MaxFooCount/4 ) // in pop() 82151c0b2f7Stbbdev throw Foo_exception(); 82251c0b2f7Stbbdev } 82351c0b2f7Stbbdev 82451c0b2f7Stbbdev void operator=( FooEx&& item ) { 82551c0b2f7Stbbdev operator=( item ); 82651c0b2f7Stbbdev item.serial = 0; 82751c0b2f7Stbbdev } 82851c0b2f7Stbbdev 82951c0b2f7Stbbdev }; 83051c0b2f7Stbbdev 83151c0b2f7Stbbdev template <template <typename, typename> class CQ, typename A1, typename A2, typename T> 83251c0b2f7Stbbdev void TestExceptionBody() { 83351c0b2f7Stbbdev enum methods { 83451c0b2f7Stbbdev m_push = 0, 83551c0b2f7Stbbdev m_pop 83651c0b2f7Stbbdev }; 83751c0b2f7Stbbdev 83851c0b2f7Stbbdev const int N = 1000; // # of bytes 83951c0b2f7Stbbdev 84051c0b2f7Stbbdev MaxFooCount = 5; 84151c0b2f7Stbbdev 84251c0b2f7Stbbdev try { 84351c0b2f7Stbbdev int n_pushed=0, n_popped=0; 84451c0b2f7Stbbdev for(int t = 0; t <= 1; t++)// exception type -- 0 : from allocator(), 1 : from Foo's constructor 84551c0b2f7Stbbdev { 84651c0b2f7Stbbdev CQ<T,A1> queue_test; 84751c0b2f7Stbbdev for( int m=m_push; m<=m_pop; m++ ) { 84851c0b2f7Stbbdev // concurrent_queue internally rebinds the allocator to the one for 'char' 84951c0b2f7Stbbdev A2::init_counters(); 85051c0b2f7Stbbdev 85151c0b2f7Stbbdev if(t) MaxFooCount = MaxFooCount + 400; 85251c0b2f7Stbbdev else A2::set_limits(N/2); 85351c0b2f7Stbbdev 85451c0b2f7Stbbdev try { 85551c0b2f7Stbbdev switch(m) { 85651c0b2f7Stbbdev case m_push: 85751c0b2f7Stbbdev for( int k=0; k<N; k++ ) { 85851c0b2f7Stbbdev push( queue_test, T(), k); 85951c0b2f7Stbbdev n_pushed++; 86051c0b2f7Stbbdev } 86151c0b2f7Stbbdev break; 86251c0b2f7Stbbdev case m_pop: 86351c0b2f7Stbbdev n_popped=0; 86451c0b2f7Stbbdev for( int k=0; k<n_pushed; k++ ) { 86551c0b2f7Stbbdev T elt; 86651c0b2f7Stbbdev queue_test.try_pop( elt); 86751c0b2f7Stbbdev n_popped++; 86851c0b2f7Stbbdev } 86951c0b2f7Stbbdev n_pushed = 0; 87051c0b2f7Stbbdev A2::set_limits(); 87151c0b2f7Stbbdev break; 87251c0b2f7Stbbdev } 87351c0b2f7Stbbdev if( !t && m==m_push ) REQUIRE_MESSAGE(false, "should throw an exception"); 87451c0b2f7Stbbdev } catch ( Foo_exception & ) { 87551c0b2f7Stbbdev long tc = MaxFooCount; 87651c0b2f7Stbbdev MaxFooCount = 0; // disable exception 87751c0b2f7Stbbdev switch(m) { 87851c0b2f7Stbbdev case m_push: 87951c0b2f7Stbbdev REQUIRE_MESSAGE(ptrdiff_t(queue_test.size())==n_pushed, "incorrect queue size"); 88051c0b2f7Stbbdev for( int k=0; k<(int)tc; k++ ) { 88151c0b2f7Stbbdev push( queue_test, T(), k); 88251c0b2f7Stbbdev n_pushed++; 88351c0b2f7Stbbdev } 88451c0b2f7Stbbdev break; 88551c0b2f7Stbbdev case m_pop: 88651c0b2f7Stbbdev n_pushed -= (n_popped+1); // including one that threw the exception 88751c0b2f7Stbbdev REQUIRE_MESSAGE(n_pushed>=0, "n_pushed cannot be less than 0"); 88851c0b2f7Stbbdev for( int k=0; k<1000; k++ ) { 88951c0b2f7Stbbdev push( queue_test, T(), k); 89051c0b2f7Stbbdev n_pushed++; 89151c0b2f7Stbbdev } 89251c0b2f7Stbbdev REQUIRE_MESSAGE(!queue_test.empty(), "queue must not be empty"); 89351c0b2f7Stbbdev REQUIRE_MESSAGE(ptrdiff_t(queue_test.size())==n_pushed, "queue size must be equal to n pushed"); 89451c0b2f7Stbbdev for( int k=0; k<n_pushed; k++ ) { 89551c0b2f7Stbbdev T elt; 89651c0b2f7Stbbdev queue_test.try_pop( elt); 89751c0b2f7Stbbdev } 89851c0b2f7Stbbdev REQUIRE_MESSAGE(queue_test.empty(), "queue must be empty"); 89951c0b2f7Stbbdev REQUIRE_MESSAGE(queue_test.size()==0, "queue must be empty"); 90051c0b2f7Stbbdev break; 90151c0b2f7Stbbdev } 90251c0b2f7Stbbdev MaxFooCount = tc; 90351c0b2f7Stbbdev } catch ( std::bad_alloc & ) { 90451c0b2f7Stbbdev A2::set_limits(); // disable exception from allocator 90551c0b2f7Stbbdev std::size_t size = queue_test.size(); 90651c0b2f7Stbbdev switch(m) { 90751c0b2f7Stbbdev case m_push: 90851c0b2f7Stbbdev REQUIRE_MESSAGE(size>0, "incorrect queue size"); 90951c0b2f7Stbbdev break; 91051c0b2f7Stbbdev case m_pop: 91151c0b2f7Stbbdev if( !t ) REQUIRE_MESSAGE(false, "should not throw an exception"); 91251c0b2f7Stbbdev break; 91351c0b2f7Stbbdev } 91451c0b2f7Stbbdev } 91551c0b2f7Stbbdev INFO("for t= " << t << "and m= " << m << " exception test passed"); 91651c0b2f7Stbbdev } 91751c0b2f7Stbbdev } 91851c0b2f7Stbbdev } catch(...) { 91951c0b2f7Stbbdev REQUIRE_MESSAGE(false, "unexpected exception"); 92051c0b2f7Stbbdev } 92151c0b2f7Stbbdev } 92251c0b2f7Stbbdev 92351c0b2f7Stbbdev void TestExceptions() { 92449e08aacStbbdev using allocator_t = StaticSharedCountingAllocator<oneapi::tbb::cache_aligned_allocator<std::size_t>>; 92549e08aacStbbdev using allocator_char_t = StaticSharedCountingAllocator<oneapi::tbb::cache_aligned_allocator<char>>; 92651c0b2f7Stbbdev TestExceptionBody<ConcQWithSizeWrapper, allocator_t, allocator_char_t, FooEx>(); 92749e08aacStbbdev TestExceptionBody<oneapi::tbb::concurrent_bounded_queue, allocator_t, allocator_char_t, FooEx>(); 92851c0b2f7Stbbdev 92951c0b2f7Stbbdev } 93051c0b2f7Stbbdev 93151c0b2f7Stbbdev std::atomic<std::size_t> num_pushed; 93251c0b2f7Stbbdev std::atomic<std::size_t> num_popped; 93351c0b2f7Stbbdev std::atomic<std::size_t> failed_pushes; 93451c0b2f7Stbbdev std::atomic<std::size_t> failed_pops; 93551c0b2f7Stbbdev 93651c0b2f7Stbbdev class SimplePushBody { 93749e08aacStbbdev oneapi::tbb::concurrent_bounded_queue<int>* q; 93851c0b2f7Stbbdev std::size_t max; 93951c0b2f7Stbbdev public: 94049e08aacStbbdev SimplePushBody(oneapi::tbb::concurrent_bounded_queue<int>* _q, std::size_t hi_thr) : q(_q), max(hi_thr) {} 94151c0b2f7Stbbdev 94251c0b2f7Stbbdev void operator()(std::size_t thread_id) const { 94351c0b2f7Stbbdev if (thread_id == max) { 94451c0b2f7Stbbdev while ( q->size() < std::ptrdiff_t(max) ) { 945*b15aabb3Stbbdev utils::yield(); 94651c0b2f7Stbbdev } 94751c0b2f7Stbbdev q->abort(); 94851c0b2f7Stbbdev return; 94951c0b2f7Stbbdev } 95051c0b2f7Stbbdev try { 95151c0b2f7Stbbdev q->push(42); 95251c0b2f7Stbbdev ++num_pushed; 95351c0b2f7Stbbdev } catch (...) { 95451c0b2f7Stbbdev ++failed_pushes; 95551c0b2f7Stbbdev } 95651c0b2f7Stbbdev } 95751c0b2f7Stbbdev }; 95851c0b2f7Stbbdev 95951c0b2f7Stbbdev class SimplePopBody { 96049e08aacStbbdev oneapi::tbb::concurrent_bounded_queue<int>* q; 96151c0b2f7Stbbdev std::ptrdiff_t max; 96251c0b2f7Stbbdev std::ptrdiff_t prefill; 96351c0b2f7Stbbdev public: 96449e08aacStbbdev SimplePopBody(oneapi::tbb::concurrent_bounded_queue<int>* _q, std::size_t hi_thr, std::size_t nitems) 96551c0b2f7Stbbdev : q(_q), max(hi_thr), prefill(nitems) {} 96651c0b2f7Stbbdev 96751c0b2f7Stbbdev void operator()(std::size_t thread_id) const { 96851c0b2f7Stbbdev int e; 96951c0b2f7Stbbdev if (thread_id == std::size_t(max)) { 97051c0b2f7Stbbdev while (q->size()> prefill - max) { 971*b15aabb3Stbbdev utils::yield(); 97251c0b2f7Stbbdev } 97351c0b2f7Stbbdev 97451c0b2f7Stbbdev q->abort(); 97551c0b2f7Stbbdev return; 97651c0b2f7Stbbdev } 97751c0b2f7Stbbdev try { 97851c0b2f7Stbbdev q->pop(e); 97951c0b2f7Stbbdev ++num_popped; 98051c0b2f7Stbbdev } catch ( ... ) { 98151c0b2f7Stbbdev ++failed_pops; 98251c0b2f7Stbbdev } 98351c0b2f7Stbbdev } 98451c0b2f7Stbbdev }; 98551c0b2f7Stbbdev 98651c0b2f7Stbbdev void TestAbort() { 98751c0b2f7Stbbdev for (std::size_t nthreads = MinThread; nthreads <= MaxThread; ++nthreads) { 98849e08aacStbbdev oneapi::tbb::concurrent_bounded_queue<int> iq1; 98951c0b2f7Stbbdev iq1.set_capacity(0); 99051c0b2f7Stbbdev for (std::size_t i = 0; i < 10; ++i) { 99151c0b2f7Stbbdev num_pushed.store(0, std::memory_order_relaxed); 99251c0b2f7Stbbdev num_popped.store(0, std::memory_order_relaxed); 99351c0b2f7Stbbdev failed_pushes.store(0, std::memory_order_relaxed); 99451c0b2f7Stbbdev failed_pops.store(0, std::memory_order_relaxed); 99551c0b2f7Stbbdev SimplePushBody my_push_body1(&iq1, nthreads); 99651c0b2f7Stbbdev utils::NativeParallelFor(nthreads + 1, my_push_body1); 99751c0b2f7Stbbdev REQUIRE_MESSAGE(num_pushed == 0, "no elements should have been pushed to zero-sized queue"); 99851c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pushes == nthreads, "All threads should have failed to push an element to zero-sized queue"); 99951c0b2f7Stbbdev // Do not test popping each time in order to test queue destruction with no previous pops 100051c0b2f7Stbbdev if (nthreads < (MaxThread + MinThread) / 2) { 100151c0b2f7Stbbdev int e; 100251c0b2f7Stbbdev bool queue_empty = !iq1.try_pop(e); 100351c0b2f7Stbbdev REQUIRE_MESSAGE(queue_empty, "no elements should have been popped from zero-sized queue"); 100451c0b2f7Stbbdev } 100551c0b2f7Stbbdev } 100651c0b2f7Stbbdev 100749e08aacStbbdev oneapi::tbb::concurrent_bounded_queue<int> iq2; 100851c0b2f7Stbbdev iq2.set_capacity(2); 100951c0b2f7Stbbdev for (std::size_t i=0; i < 10; ++i) { 101051c0b2f7Stbbdev num_pushed.store(0, std::memory_order_relaxed); 101151c0b2f7Stbbdev num_popped.store(0, std::memory_order_relaxed); 101251c0b2f7Stbbdev failed_pushes.store(0, std::memory_order_relaxed); 101351c0b2f7Stbbdev failed_pops.store(0, std::memory_order_relaxed); 101451c0b2f7Stbbdev SimplePushBody my_push_body2(&iq2, nthreads); 101551c0b2f7Stbbdev utils::NativeParallelFor(nthreads + 1, my_push_body2); 101651c0b2f7Stbbdev REQUIRE_MESSAGE(num_pushed <= 2, "at most 2 elements should have been pushed to queue of size 2"); 101751c0b2f7Stbbdev if (nthreads>= 2) 101851c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pushes == nthreads - 2, "nthreads-2 threads should have failed to push an element to queue of size 2"); 101951c0b2f7Stbbdev int e; 102051c0b2f7Stbbdev while (iq2.try_pop(e)) ; 102151c0b2f7Stbbdev } 102251c0b2f7Stbbdev 102349e08aacStbbdev oneapi::tbb::concurrent_bounded_queue<int> iq3; 102451c0b2f7Stbbdev iq3.set_capacity(2); 102551c0b2f7Stbbdev for (std::size_t i = 0; i < 10; ++i) { 102651c0b2f7Stbbdev num_pushed.store(0, std::memory_order_relaxed); 102751c0b2f7Stbbdev num_popped.store(0, std::memory_order_relaxed); 102851c0b2f7Stbbdev failed_pushes.store(0, std::memory_order_relaxed); 102951c0b2f7Stbbdev failed_pops.store(0, std::memory_order_relaxed); 103051c0b2f7Stbbdev iq3.push(42); 103151c0b2f7Stbbdev iq3.push(42); 103251c0b2f7Stbbdev SimplePopBody my_pop_body(&iq3, nthreads, 2); 103351c0b2f7Stbbdev utils::NativeParallelFor( nthreads+1, my_pop_body ); 103451c0b2f7Stbbdev REQUIRE_MESSAGE(num_popped <= 2, "at most 2 elements should have been popped from queue of size 2"); 103551c0b2f7Stbbdev if (nthreads>= 2) 103651c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pops == nthreads - 2, "nthreads-2 threads should have failed to pop an element from queue of size 2"); 103751c0b2f7Stbbdev else { 103851c0b2f7Stbbdev int e; 103951c0b2f7Stbbdev iq3.pop(e); 104051c0b2f7Stbbdev } 104151c0b2f7Stbbdev } 104251c0b2f7Stbbdev 104349e08aacStbbdev oneapi::tbb::concurrent_bounded_queue<int> iq4; 104451c0b2f7Stbbdev std::size_t cap = nthreads / 2; 104551c0b2f7Stbbdev if (!cap) cap = 1; 104651c0b2f7Stbbdev iq4.set_capacity(cap); 104751c0b2f7Stbbdev for (int i=0; i<10; ++i) { 104851c0b2f7Stbbdev num_pushed.store(0, std::memory_order_relaxed); 104951c0b2f7Stbbdev num_popped.store(0, std::memory_order_relaxed); 105051c0b2f7Stbbdev failed_pushes.store(0, std::memory_order_relaxed); 105151c0b2f7Stbbdev failed_pops.store(0, std::memory_order_relaxed); 105251c0b2f7Stbbdev SimplePushBody my_push_body2(&iq4, nthreads); 105351c0b2f7Stbbdev utils::NativeParallelFor(nthreads + 1, my_push_body2); 105451c0b2f7Stbbdev REQUIRE_MESSAGE(num_pushed <= cap, "at most cap elements should have been pushed to queue of size cap"); 105551c0b2f7Stbbdev if (nthreads>= cap) 105651c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pushes == nthreads-cap, "nthreads-cap threads should have failed to push an element to queue of size cap"); 105751c0b2f7Stbbdev SimplePopBody my_pop_body(&iq4, nthreads, num_pushed); 105851c0b2f7Stbbdev utils::NativeParallelFor( nthreads+1, my_pop_body ); 105951c0b2f7Stbbdev REQUIRE_MESSAGE((int)num_popped <= cap, "at most cap elements should have been popped from queue of size cap"); 106051c0b2f7Stbbdev if (nthreads>= cap) 106151c0b2f7Stbbdev REQUIRE_MESSAGE(failed_pops == nthreads-cap, "nthreads-cap threads should have failed to pop an element from queue of size cap"); 106251c0b2f7Stbbdev else { 106351c0b2f7Stbbdev int e; 106451c0b2f7Stbbdev while (iq4.try_pop(e)) ; 106551c0b2f7Stbbdev } 106651c0b2f7Stbbdev } 106751c0b2f7Stbbdev } 106851c0b2f7Stbbdev } 106951c0b2f7Stbbdev #endif 107051c0b2f7Stbbdev 107151c0b2f7Stbbdev template <template <typename...> class ContainerType> 107251c0b2f7Stbbdev void test_member_types() { 107351c0b2f7Stbbdev using container_type = ContainerType<int>; 107449e08aacStbbdev static_assert(std::is_same<typename container_type::allocator_type, oneapi::tbb::cache_aligned_allocator<int>>::value, 107551c0b2f7Stbbdev "Incorrect default template allocator"); 107651c0b2f7Stbbdev 107751c0b2f7Stbbdev static_assert(std::is_same<typename container_type::value_type, int>::value, 107851c0b2f7Stbbdev "Incorrect container value_type member type"); 107951c0b2f7Stbbdev 108051c0b2f7Stbbdev static_assert(std::is_signed<typename container_type::difference_type>::value, 108151c0b2f7Stbbdev "Incorrect container difference_type member type"); 108251c0b2f7Stbbdev 108351c0b2f7Stbbdev using value_type = typename container_type::value_type; 108451c0b2f7Stbbdev static_assert(std::is_same<typename container_type::reference, value_type&>::value, 108551c0b2f7Stbbdev "Incorrect container reference member type"); 108651c0b2f7Stbbdev static_assert(std::is_same<typename container_type::const_reference, const value_type&>::value, 108751c0b2f7Stbbdev "Incorrect container const_reference member type"); 108851c0b2f7Stbbdev using allocator_type = typename container_type::allocator_type; 108951c0b2f7Stbbdev static_assert(std::is_same<typename container_type::pointer, typename std::allocator_traits<allocator_type>::pointer>::value, 109051c0b2f7Stbbdev "Incorrect container pointer member type"); 109151c0b2f7Stbbdev static_assert(std::is_same<typename container_type::const_pointer, typename std::allocator_traits<allocator_type>::const_pointer>::value, 109251c0b2f7Stbbdev "Incorrect container const_pointer member type"); 109351c0b2f7Stbbdev 109451c0b2f7Stbbdev static_assert(utils::is_forward_iterator<typename container_type::iterator>::value, 109551c0b2f7Stbbdev "Incorrect container iterator member type"); 109651c0b2f7Stbbdev static_assert(!std::is_const<typename container_type::iterator::value_type>::value, 109751c0b2f7Stbbdev "Incorrect container iterator member type"); 109851c0b2f7Stbbdev static_assert(utils::is_forward_iterator<typename container_type::const_iterator>::value, 109951c0b2f7Stbbdev "Incorrect container const_iterator member type"); 110051c0b2f7Stbbdev static_assert(std::is_const<typename container_type::const_iterator::value_type>::value, 110151c0b2f7Stbbdev "Incorrect container iterator member type"); 110251c0b2f7Stbbdev } 110351c0b2f7Stbbdev 110451c0b2f7Stbbdev enum push_t { push_op, try_push_op }; 110551c0b2f7Stbbdev 110651c0b2f7Stbbdev template<push_t push_op> 110751c0b2f7Stbbdev struct pusher { 110851c0b2f7Stbbdev template<typename CQ, typename VType> 110951c0b2f7Stbbdev static bool push( CQ& queue, VType&& val ) { 111051c0b2f7Stbbdev queue.push( std::forward<VType>( val ) ); 111151c0b2f7Stbbdev return true; 111251c0b2f7Stbbdev } 111351c0b2f7Stbbdev }; 111451c0b2f7Stbbdev 111551c0b2f7Stbbdev template<> 111651c0b2f7Stbbdev struct pusher< try_push_op> { 111751c0b2f7Stbbdev template<typename CQ, typename VType> 111851c0b2f7Stbbdev static bool push( CQ& queue, VType&& val ) { 111951c0b2f7Stbbdev return queue.try_push( std::forward<VType>( val ) ); 112051c0b2f7Stbbdev } 112151c0b2f7Stbbdev }; 112251c0b2f7Stbbdev 112351c0b2f7Stbbdev enum pop_t { pop_op, try_pop_op }; 112451c0b2f7Stbbdev 112551c0b2f7Stbbdev template<pop_t pop_op> 112651c0b2f7Stbbdev struct popper { 112751c0b2f7Stbbdev template<typename CQ, typename VType> 112851c0b2f7Stbbdev static bool pop( CQ& queue, VType&& val ) { 112951c0b2f7Stbbdev if( queue.empty() ) return false; 113051c0b2f7Stbbdev queue.pop( std::forward<VType>( val ) ); 113151c0b2f7Stbbdev return true; 113251c0b2f7Stbbdev } 113351c0b2f7Stbbdev }; 113451c0b2f7Stbbdev 113551c0b2f7Stbbdev template<> 113651c0b2f7Stbbdev struct popper<try_pop_op> { 113751c0b2f7Stbbdev template<typename CQ, typename VType> 113851c0b2f7Stbbdev static bool pop( CQ& queue, VType&& val ) { 113951c0b2f7Stbbdev return queue.try_pop( std::forward<VType>( val ) ); 114051c0b2f7Stbbdev } 114151c0b2f7Stbbdev }; 114251c0b2f7Stbbdev 114351c0b2f7Stbbdev struct MoveOperationTracker { 114451c0b2f7Stbbdev static std::size_t copy_constructor_called_times; 114551c0b2f7Stbbdev static std::size_t move_constructor_called_times; 114651c0b2f7Stbbdev static std::size_t copy_assignment_called_times; 114751c0b2f7Stbbdev static std::size_t move_assignment_called_times; 114851c0b2f7Stbbdev 114951c0b2f7Stbbdev MoveOperationTracker() {} 115051c0b2f7Stbbdev MoveOperationTracker(const MoveOperationTracker&) { 115151c0b2f7Stbbdev ++copy_constructor_called_times; 115251c0b2f7Stbbdev } 115351c0b2f7Stbbdev MoveOperationTracker(MoveOperationTracker&&) { 115451c0b2f7Stbbdev ++move_constructor_called_times; 115551c0b2f7Stbbdev } 115651c0b2f7Stbbdev MoveOperationTracker& operator=(MoveOperationTracker const&) { 115751c0b2f7Stbbdev ++copy_assignment_called_times; 115851c0b2f7Stbbdev return *this; 115951c0b2f7Stbbdev } 116051c0b2f7Stbbdev MoveOperationTracker& operator=(MoveOperationTracker&&) { 116151c0b2f7Stbbdev ++move_assignment_called_times; 116251c0b2f7Stbbdev return *this; 116351c0b2f7Stbbdev } 116451c0b2f7Stbbdev }; 116551c0b2f7Stbbdev 116651c0b2f7Stbbdev size_t MoveOperationTracker::copy_constructor_called_times = 0; 116751c0b2f7Stbbdev size_t MoveOperationTracker::move_constructor_called_times = 0; 116851c0b2f7Stbbdev size_t MoveOperationTracker::copy_assignment_called_times = 0; 116951c0b2f7Stbbdev size_t MoveOperationTracker::move_assignment_called_times = 0; 117051c0b2f7Stbbdev 117151c0b2f7Stbbdev template <class CQ, push_t push_op, pop_t pop_op> 117251c0b2f7Stbbdev void TestMoveSupport() { 117351c0b2f7Stbbdev std::size_t &mcct = MoveOperationTracker::move_constructor_called_times; 117451c0b2f7Stbbdev std::size_t &ccct = MoveOperationTracker::copy_constructor_called_times; 117551c0b2f7Stbbdev std::size_t &cact = MoveOperationTracker::copy_assignment_called_times; 117651c0b2f7Stbbdev std::size_t &mact = MoveOperationTracker::move_assignment_called_times; 117751c0b2f7Stbbdev mcct = ccct = cact = mact = 0; 117851c0b2f7Stbbdev 117951c0b2f7Stbbdev CQ q; 118051c0b2f7Stbbdev 118151c0b2f7Stbbdev REQUIRE_MESSAGE(mcct == 0, "Value must be zero-initialized"); 118251c0b2f7Stbbdev REQUIRE_MESSAGE(ccct == 0, "Value must be zero-initialized"); 118351c0b2f7Stbbdev CHECK(pusher<push_op>::push( q, MoveOperationTracker() )); 118451c0b2f7Stbbdev REQUIRE_MESSAGE(mcct == 1, "Not working push(T&&) or try_push(T&&)?"); 118551c0b2f7Stbbdev REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&) or try_push(T&&)"); 118651c0b2f7Stbbdev 118751c0b2f7Stbbdev MoveOperationTracker ob; 118851c0b2f7Stbbdev CHECK(pusher<push_op>::push( q, std::move(ob) )); 118951c0b2f7Stbbdev REQUIRE_MESSAGE(mcct == 2, "Not working push(T&&) or try_push(T&&)?"); 119051c0b2f7Stbbdev REQUIRE_MESSAGE(ccct == 0, "Copying of arg occurred during push(T&&) or try_push(T&&)"); 119151c0b2f7Stbbdev 119251c0b2f7Stbbdev REQUIRE_MESSAGE(cact == 0, "Copy assignment called during push(T&&) or try_push(T&&)"); 119351c0b2f7Stbbdev REQUIRE_MESSAGE(mact == 0, "Move assignment called during push(T&&) or try_push(T&&)"); 119451c0b2f7Stbbdev 119551c0b2f7Stbbdev bool result = popper<pop_op>::pop( q, ob ); 119651c0b2f7Stbbdev CHECK(result); 119751c0b2f7Stbbdev REQUIRE_MESSAGE(cact == 0, "Copy assignment called during try_pop(T&&)"); 119851c0b2f7Stbbdev REQUIRE_MESSAGE(mact == 1, "Move assignment was not called during try_pop(T&&)"); 119951c0b2f7Stbbdev } 120051c0b2f7Stbbdev 120151c0b2f7Stbbdev void TestMoveSupportInPushPop() { 120249e08aacStbbdev TestMoveSupport<oneapi::tbb::concurrent_queue<MoveOperationTracker>, push_op, try_pop_op>(); 120349e08aacStbbdev TestMoveSupport<oneapi::tbb::concurrent_bounded_queue<MoveOperationTracker>, push_op, pop_op>(); 120449e08aacStbbdev TestMoveSupport<oneapi::tbb::concurrent_bounded_queue<MoveOperationTracker>, try_push_op, try_pop_op>(); 120551c0b2f7Stbbdev } 120651c0b2f7Stbbdev 120751c0b2f7Stbbdev template<class T> 120849e08aacStbbdev class allocator: public oneapi::tbb::cache_aligned_allocator<T> { 120951c0b2f7Stbbdev public: 121051c0b2f7Stbbdev std::size_t m_unique_id; 121151c0b2f7Stbbdev 121251c0b2f7Stbbdev allocator() : m_unique_id( 0 ) {} 121351c0b2f7Stbbdev 121451c0b2f7Stbbdev allocator(size_t unique_id) { m_unique_id = unique_id; } 121551c0b2f7Stbbdev 121651c0b2f7Stbbdev template<typename U> 121751c0b2f7Stbbdev allocator(const allocator<U>& a) noexcept { m_unique_id = a.m_unique_id; } 121851c0b2f7Stbbdev 121951c0b2f7Stbbdev template<typename U> 122051c0b2f7Stbbdev struct rebind { typedef allocator<U> other; }; 122151c0b2f7Stbbdev 122251c0b2f7Stbbdev friend bool operator==(const allocator& lhs, const allocator& rhs) { 122351c0b2f7Stbbdev return lhs.m_unique_id == rhs.m_unique_id; 122451c0b2f7Stbbdev } 122551c0b2f7Stbbdev }; 122651c0b2f7Stbbdev 122751c0b2f7Stbbdev template <typename Queue> 122851c0b2f7Stbbdev void AssertEquality(Queue &q, const std::vector<typename Queue::value_type> &vec) { 122951c0b2f7Stbbdev CHECK(q.size() == typename Queue::size_type(vec.size())); 123051c0b2f7Stbbdev CHECK(std::equal(q.unsafe_begin(), q.unsafe_end(), vec.begin())); 123151c0b2f7Stbbdev } 123251c0b2f7Stbbdev 123351c0b2f7Stbbdev template <typename Queue> 123451c0b2f7Stbbdev void AssertEmptiness(Queue &q) { 123551c0b2f7Stbbdev CHECK(q.empty()); 123651c0b2f7Stbbdev CHECK(!q.size()); 123751c0b2f7Stbbdev typename Queue::value_type elem; 123851c0b2f7Stbbdev CHECK(!q.try_pop(elem)); 123951c0b2f7Stbbdev } 124051c0b2f7Stbbdev 124151c0b2f7Stbbdev template <push_t push_op, typename Queue> 124251c0b2f7Stbbdev void FillTest(Queue &q, const std::vector<typename Queue::value_type> &vec) { 124351c0b2f7Stbbdev for (typename std::vector<typename Queue::value_type>::const_iterator it = vec.begin(); it != vec.end(); ++it) 124451c0b2f7Stbbdev CHECK(pusher<push_op>::push(q, *it)); 124551c0b2f7Stbbdev AssertEquality(q, vec); 124651c0b2f7Stbbdev } 124751c0b2f7Stbbdev 124851c0b2f7Stbbdev template <pop_t pop_op, typename Queue> 124951c0b2f7Stbbdev void EmptyTest(Queue &q, const std::vector<typename Queue::value_type> &vec) { 125051c0b2f7Stbbdev typedef typename Queue::value_type value_type; 125151c0b2f7Stbbdev 125251c0b2f7Stbbdev value_type elem; 125351c0b2f7Stbbdev typename std::vector<value_type>::const_iterator it = vec.begin(); 125451c0b2f7Stbbdev while (popper<pop_op>::pop(q, elem)) { 125551c0b2f7Stbbdev CHECK(elem == *it); 125651c0b2f7Stbbdev ++it; 125751c0b2f7Stbbdev } 125851c0b2f7Stbbdev CHECK(it == vec.end()); 125951c0b2f7Stbbdev AssertEmptiness(q); 126051c0b2f7Stbbdev } 126151c0b2f7Stbbdev 126251c0b2f7Stbbdev template <typename T, typename A> 126349e08aacStbbdev void bounded_queue_specific_test(oneapi::tbb::concurrent_queue<T, A> &, const std::vector<T> &) { /* do nothing */ } 126451c0b2f7Stbbdev 126551c0b2f7Stbbdev template <typename T, typename A> 126649e08aacStbbdev void bounded_queue_specific_test(oneapi::tbb::concurrent_bounded_queue<T, A> &q, const std::vector<T> &vec) { 126749e08aacStbbdev typedef typename oneapi::tbb::concurrent_bounded_queue<T, A>::size_type size_type; 126851c0b2f7Stbbdev 126951c0b2f7Stbbdev FillTest<try_push_op>(q, vec); 127049e08aacStbbdev oneapi::tbb::concurrent_bounded_queue<T, A> q2 = q; 127151c0b2f7Stbbdev EmptyTest<pop_op>(q, vec); 127251c0b2f7Stbbdev 127351c0b2f7Stbbdev // capacity 127451c0b2f7Stbbdev q2.set_capacity(size_type(vec.size())); 127551c0b2f7Stbbdev CHECK(q2.capacity() == size_type(vec.size())); 127651c0b2f7Stbbdev CHECK(q2.size() == size_type(vec.size())); 127751c0b2f7Stbbdev CHECK(!q2.try_push(vec[0])); 127851c0b2f7Stbbdev q.abort(); 127951c0b2f7Stbbdev } 128051c0b2f7Stbbdev 128151c0b2f7Stbbdev // Checks operability of the queue the data was moved from 128251c0b2f7Stbbdev template<typename T, typename CQ> 128351c0b2f7Stbbdev void TestQueueOperabilityAfterDataMove( CQ& queue ) { 128451c0b2f7Stbbdev const std::size_t size = 10; 128551c0b2f7Stbbdev std::vector<T> v(size); 128651c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i ) v[i] = T( i * i + i ); 128751c0b2f7Stbbdev 128851c0b2f7Stbbdev FillTest<push_op>(queue, v); 128951c0b2f7Stbbdev EmptyTest<try_pop_op>(queue, v); 129051c0b2f7Stbbdev bounded_queue_specific_test(queue, v); 129151c0b2f7Stbbdev } 129251c0b2f7Stbbdev 129351c0b2f7Stbbdev template<class CQ, class T> 129451c0b2f7Stbbdev void TestMoveConstructors() { 129551c0b2f7Stbbdev T::construction_num = T::destruction_num = 0; 129651c0b2f7Stbbdev CQ src_queue( allocator<T>(0) ); 129751c0b2f7Stbbdev const std::size_t size = 10; 129851c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i ) 129951c0b2f7Stbbdev src_queue.push( T(i + (i ^ size)) ); 130051c0b2f7Stbbdev CHECK(T::construction_num == 2 * size); 130151c0b2f7Stbbdev CHECK(T::destruction_num == size); 130251c0b2f7Stbbdev 130351c0b2f7Stbbdev const T* locations[size]; 130451c0b2f7Stbbdev typename CQ::const_iterator qit = src_queue.unsafe_begin(); 130551c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i, ++qit ) 130651c0b2f7Stbbdev locations[i] = &(*qit); 130751c0b2f7Stbbdev 130851c0b2f7Stbbdev // Ensuring allocation operation takes place during move when allocators are different 130951c0b2f7Stbbdev T::construction_num = T::destruction_num = 0; 131051c0b2f7Stbbdev CQ dst_queue( std::move(src_queue), allocator<T>(1) ); 131151c0b2f7Stbbdev CHECK(T::construction_num == size); 131251c0b2f7Stbbdev CHECK(T::destruction_num == size * 2); // One item is used by the queue destructor 131351c0b2f7Stbbdev 131451c0b2f7Stbbdev TestQueueOperabilityAfterDataMove<T>( src_queue ); 131551c0b2f7Stbbdev 131651c0b2f7Stbbdev qit = dst_queue.unsafe_begin(); 131751c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i, ++qit ) { 131851c0b2f7Stbbdev REQUIRE_MESSAGE(locations[i] != &(*qit), "an item should have been copied but was not" ); 131951c0b2f7Stbbdev locations[i] = &(*qit); 132051c0b2f7Stbbdev } 132151c0b2f7Stbbdev 132251c0b2f7Stbbdev T::construction_num = T::destruction_num = 0; 132351c0b2f7Stbbdev // Ensuring there is no allocation operation during move with equal allocators 132451c0b2f7Stbbdev CQ dst_queue2( std::move(dst_queue), allocator<T>(1) ); 132551c0b2f7Stbbdev CHECK(T::construction_num == 0); 132651c0b2f7Stbbdev CHECK(T::destruction_num == 0); 132751c0b2f7Stbbdev 132851c0b2f7Stbbdev TestQueueOperabilityAfterDataMove<T>( dst_queue ); 132951c0b2f7Stbbdev 133051c0b2f7Stbbdev qit = dst_queue2.unsafe_begin(); 133151c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i, ++qit ) { 133251c0b2f7Stbbdev REQUIRE_MESSAGE(locations[i] == &(*qit), "an item should have been moved but was not" ); 133351c0b2f7Stbbdev } 133451c0b2f7Stbbdev 133551c0b2f7Stbbdev for( std::size_t i = 0; i < size; ++i) { 133651c0b2f7Stbbdev T test(i + (i ^ size)); 133751c0b2f7Stbbdev T popped; 133851c0b2f7Stbbdev bool pop_result = dst_queue2.try_pop( popped ); 133951c0b2f7Stbbdev CHECK(pop_result); 134051c0b2f7Stbbdev CHECK(test == popped); 134151c0b2f7Stbbdev } 134251c0b2f7Stbbdev CHECK(dst_queue2.empty()); 134351c0b2f7Stbbdev CHECK(dst_queue2.size() == 0); 134451c0b2f7Stbbdev } 134551c0b2f7Stbbdev 134651c0b2f7Stbbdev void TestMoveConstruction() { 134751c0b2f7Stbbdev TestMoveConstructors<ConcQWithSizeWrapper<Bar, allocator<Bar>>, Bar>(); 134849e08aacStbbdev TestMoveConstructors<oneapi::tbb::concurrent_bounded_queue<Bar, allocator<Bar>>, Bar>(); 134951c0b2f7Stbbdev } 135051c0b2f7Stbbdev 135151c0b2f7Stbbdev class NonTrivialConstructorType { 135251c0b2f7Stbbdev public: 135351c0b2f7Stbbdev NonTrivialConstructorType( int a = 0 ) : m_a( a ), m_str( "" ) {} 135451c0b2f7Stbbdev NonTrivialConstructorType( const std::string& str ) : m_a( 0 ), m_str( str ) {} 135551c0b2f7Stbbdev NonTrivialConstructorType( int a, const std::string& str ) : m_a( a ), m_str( str ) {} 135651c0b2f7Stbbdev int get_a() const { return m_a; } 135751c0b2f7Stbbdev std::string get_str() const { return m_str; } 135851c0b2f7Stbbdev private: 135951c0b2f7Stbbdev int m_a; 136051c0b2f7Stbbdev std::string m_str; 136151c0b2f7Stbbdev }; 136251c0b2f7Stbbdev 136351c0b2f7Stbbdev enum emplace_t { emplace_op, try_emplace_op }; 136451c0b2f7Stbbdev 136551c0b2f7Stbbdev template<emplace_t emplace_op> 136651c0b2f7Stbbdev struct emplacer { 136751c0b2f7Stbbdev template<typename CQ, typename... Args> 136851c0b2f7Stbbdev static void emplace( CQ& queue, Args&&... val ) { queue.emplace( std::forward<Args>( val )... ); } 136951c0b2f7Stbbdev }; 137051c0b2f7Stbbdev 137151c0b2f7Stbbdev template<> 137251c0b2f7Stbbdev struct emplacer <try_emplace_op> { 137351c0b2f7Stbbdev template<typename CQ, typename... Args> 137451c0b2f7Stbbdev static void emplace( CQ& queue, Args&&... val ) { 137551c0b2f7Stbbdev bool result = queue.try_emplace( std::forward<Args>( val )... ); 137651c0b2f7Stbbdev REQUIRE_MESSAGE(result, "try_emplace error\n"); 137751c0b2f7Stbbdev } 137851c0b2f7Stbbdev }; 137951c0b2f7Stbbdev 138051c0b2f7Stbbdev template<typename CQ, emplace_t emplace_op> 138151c0b2f7Stbbdev void TestEmplaceInQueue() { 138251c0b2f7Stbbdev CQ cq; 138351c0b2f7Stbbdev std::string test_str = "I'm being emplaced!"; 138451c0b2f7Stbbdev { 138551c0b2f7Stbbdev emplacer<emplace_op>::emplace( cq, 5 ); 138651c0b2f7Stbbdev CHECK(cq.size() == 1); 138751c0b2f7Stbbdev NonTrivialConstructorType popped( -1 ); 138851c0b2f7Stbbdev bool result = cq.try_pop( popped ); 138951c0b2f7Stbbdev CHECK(result); 139051c0b2f7Stbbdev CHECK(popped.get_a() == 5); 139151c0b2f7Stbbdev CHECK(popped.get_str() == std::string( "" )); 139251c0b2f7Stbbdev } 139351c0b2f7Stbbdev 139451c0b2f7Stbbdev CHECK(cq.empty()); 139551c0b2f7Stbbdev 139651c0b2f7Stbbdev { 139751c0b2f7Stbbdev NonTrivialConstructorType popped( -1 ); 139851c0b2f7Stbbdev emplacer<emplace_op>::emplace( cq, std::string(test_str) ); 139951c0b2f7Stbbdev bool result = cq.try_pop( popped ); 140051c0b2f7Stbbdev CHECK(result); 140151c0b2f7Stbbdev CHECK(popped.get_a() == 0); 140251c0b2f7Stbbdev CHECK(popped.get_str() == test_str); 140351c0b2f7Stbbdev } 140451c0b2f7Stbbdev 140551c0b2f7Stbbdev CHECK(cq.empty()); 140651c0b2f7Stbbdev 140751c0b2f7Stbbdev { 140851c0b2f7Stbbdev NonTrivialConstructorType popped( -1, "" ); 140951c0b2f7Stbbdev emplacer<emplace_op>::emplace( cq, 5, std::string(test_str) ); 141051c0b2f7Stbbdev bool result = cq.try_pop( popped ); 141151c0b2f7Stbbdev CHECK(result); 141251c0b2f7Stbbdev CHECK(popped.get_a() == 5); 141351c0b2f7Stbbdev CHECK(popped.get_str() == test_str); 141451c0b2f7Stbbdev } 141551c0b2f7Stbbdev } 141651c0b2f7Stbbdev void TestEmplace() { 141751c0b2f7Stbbdev TestEmplaceInQueue<ConcQWithSizeWrapper<NonTrivialConstructorType>, emplace_op>(); 141849e08aacStbbdev TestEmplaceInQueue<oneapi::tbb::concurrent_bounded_queue<NonTrivialConstructorType>, emplace_op>(); 141949e08aacStbbdev TestEmplaceInQueue<oneapi::tbb::concurrent_bounded_queue<NonTrivialConstructorType>, try_emplace_op>(); 142051c0b2f7Stbbdev } 142151c0b2f7Stbbdev 142251c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 142351c0b2f7Stbbdev template <template <typename...> typename TQueue> 142451c0b2f7Stbbdev void TestDeductionGuides() { 142551c0b2f7Stbbdev using ComplexType = const std::string*; 142651c0b2f7Stbbdev std::vector<ComplexType> v; 142751c0b2f7Stbbdev 142851c0b2f7Stbbdev // check TQueue(InputIterator, InputIterator) 142951c0b2f7Stbbdev TQueue q1(v.begin(), v.end()); 143051c0b2f7Stbbdev static_assert(std::is_same<decltype(q1), TQueue<ComplexType>>::value); 143151c0b2f7Stbbdev 143251c0b2f7Stbbdev // check TQueue(InputIterator, InputIterator, Allocator) 143351c0b2f7Stbbdev TQueue q2(v.begin(), v.end(), std::allocator<ComplexType>()); 143451c0b2f7Stbbdev static_assert(std::is_same<decltype(q2), TQueue<ComplexType, std::allocator<ComplexType>>>::value); 143551c0b2f7Stbbdev 143651c0b2f7Stbbdev // check TQueue(TQueue &) 143751c0b2f7Stbbdev TQueue q3(q1); 143851c0b2f7Stbbdev static_assert(std::is_same<decltype(q3), decltype(q1)>::value); 143951c0b2f7Stbbdev 144051c0b2f7Stbbdev // check TQueue(TQueue &, Allocator) 144151c0b2f7Stbbdev TQueue q4(q2, std::allocator<ComplexType>()); 144251c0b2f7Stbbdev static_assert(std::is_same<decltype(q4), decltype(q2)>::value); 144351c0b2f7Stbbdev 144451c0b2f7Stbbdev // check TQueue(TQueue &&) 144551c0b2f7Stbbdev TQueue q5(std::move(q1)); 144651c0b2f7Stbbdev static_assert(std::is_same<decltype(q5), decltype(q1)>::value); 144751c0b2f7Stbbdev 144851c0b2f7Stbbdev // check TQueue(TQueue &&, Allocator) 144951c0b2f7Stbbdev TQueue q6(std::move(q4), std::allocator<ComplexType>()); 145051c0b2f7Stbbdev static_assert(std::is_same<decltype(q6), decltype(q4)>::value); 145151c0b2f7Stbbdev } 145251c0b2f7Stbbdev #endif 145351c0b2f7Stbbdev 1454*b15aabb3Stbbdev template <typename Iterator, typename QueueType> 1455*b15aabb3Stbbdev void TestQueueIteratorComparisonsBasic( QueueType& q ) { 1456*b15aabb3Stbbdev REQUIRE_MESSAGE(!q.empty(), "Incorrect test setup"); 1457*b15aabb3Stbbdev using namespace comparisons_testing; 1458*b15aabb3Stbbdev Iterator it1, it2; 1459*b15aabb3Stbbdev testEqualityComparisons</*ExpectEqual = */true>(it1, it2); 1460*b15aabb3Stbbdev it1 = q.unsafe_begin(); 1461*b15aabb3Stbbdev testEqualityComparisons</*ExpectEqual = */false>(it1, it2); 1462*b15aabb3Stbbdev it2 = q.unsafe_begin(); 1463*b15aabb3Stbbdev testEqualityComparisons</*ExpectEqual = */true>(it1, it2); 1464*b15aabb3Stbbdev it2 = q.unsafe_end(); 1465*b15aabb3Stbbdev testEqualityComparisons</*ExpectEqual = */false>(it1, it2); 1466*b15aabb3Stbbdev } 1467*b15aabb3Stbbdev 1468*b15aabb3Stbbdev template <typename QueueType> 1469*b15aabb3Stbbdev void TestQueueIteratorComparisons() { 1470*b15aabb3Stbbdev QueueType q; 1471*b15aabb3Stbbdev q.emplace(1); 1472*b15aabb3Stbbdev q.emplace(2); 1473*b15aabb3Stbbdev q.emplace(3); 1474*b15aabb3Stbbdev TestQueueIteratorComparisonsBasic<typename QueueType::iterator>(q); 1475*b15aabb3Stbbdev const QueueType& cq = q; 1476*b15aabb3Stbbdev TestQueueIteratorComparisonsBasic<typename QueueType::const_iterator>(cq); 1477*b15aabb3Stbbdev } 147851c0b2f7Stbbdev 147951c0b2f7Stbbdev //! Test constructors 148051c0b2f7Stbbdev //! \brief \ref interface \ref requirement 148151c0b2f7Stbbdev TEST_CASE("testing constructors") { 148251c0b2f7Stbbdev TestQueueConstructors(); 148351c0b2f7Stbbdev } 148451c0b2f7Stbbdev 148551c0b2f7Stbbdev //! Test work with empty queue 148651c0b2f7Stbbdev //! \brief \ref interface \ref requirement 148751c0b2f7Stbbdev TEST_CASE("testing work with empty queue") { 148851c0b2f7Stbbdev TestEmptiness(); 148951c0b2f7Stbbdev } 149051c0b2f7Stbbdev 149151c0b2f7Stbbdev //! Test set capacity operation 149251c0b2f7Stbbdev //! \brief \ref interface \ref requirement 149351c0b2f7Stbbdev TEST_CASE("testing set capacity operation") { 149451c0b2f7Stbbdev TestFullness(); 149551c0b2f7Stbbdev } 149651c0b2f7Stbbdev 149751c0b2f7Stbbdev //! Test clean operation 149851c0b2f7Stbbdev //! \brief \ref interface \ref requirement 149951c0b2f7Stbbdev TEST_CASE("testing clean operation") { 150051c0b2f7Stbbdev TestClearWorks(); 150151c0b2f7Stbbdev } 150251c0b2f7Stbbdev 150351c0b2f7Stbbdev //! Test move constructors 150451c0b2f7Stbbdev //! \brief \ref interface \ref requirement 150551c0b2f7Stbbdev TEST_CASE("testing move constructor") { 150651c0b2f7Stbbdev TestMoveConstruction(); 150751c0b2f7Stbbdev } 150851c0b2f7Stbbdev 150951c0b2f7Stbbdev //! Test move support in push and pop 151051c0b2f7Stbbdev //! \brief \ref requirement 151151c0b2f7Stbbdev TEST_CASE("testing move support in push and pop") { 151251c0b2f7Stbbdev TestMoveSupportInPushPop(); 151351c0b2f7Stbbdev } 151451c0b2f7Stbbdev 151551c0b2f7Stbbdev //! Test emplace operation 151651c0b2f7Stbbdev //! \brief \ref interface \ref requirement 151751c0b2f7Stbbdev TEST_CASE("testing emplace") { 151851c0b2f7Stbbdev TestEmplace(); 151951c0b2f7Stbbdev } 152051c0b2f7Stbbdev 152151c0b2f7Stbbdev //! Test concurrent_queues member types 152251c0b2f7Stbbdev //! \brief \ref interface \ref requirement 152351c0b2f7Stbbdev TEST_CASE("testing concurrent_queues member types"){ 152449e08aacStbbdev test_member_types<oneapi::tbb::concurrent_queue>(); 152549e08aacStbbdev test_member_types<oneapi::tbb::concurrent_bounded_queue>(); 152651c0b2f7Stbbdev 152751c0b2f7Stbbdev // Test size_type 152849e08aacStbbdev static_assert(std::is_unsigned<typename oneapi::tbb::concurrent_queue<int>::size_type>::value, 152949e08aacStbbdev "Incorrect oneapi::tbb::concurrent_queue::size_type member type"); 153049e08aacStbbdev static_assert(std::is_signed<typename oneapi::tbb::concurrent_bounded_queue<int>::size_type>::value, 153149e08aacStbbdev "Incorrect oneapi::tbb::concurrent_bounded_queue::size_type member type"); 153251c0b2f7Stbbdev } 153351c0b2f7Stbbdev 153451c0b2f7Stbbdev //! Test iterators 153551c0b2f7Stbbdev //! \brief \ref interface \ref requirement 153651c0b2f7Stbbdev TEST_CASE("testing iterators") { 153751c0b2f7Stbbdev TestQueueIteratorWorks(); 153851c0b2f7Stbbdev } 153951c0b2f7Stbbdev 154051c0b2f7Stbbdev //! Test concurrent oprations support 154151c0b2f7Stbbdev //! \brief \ref requirement 154251c0b2f7Stbbdev TEST_CASE("testing concurrent oprations support") { 154351c0b2f7Stbbdev TestConcurrentPushPop(); 154451c0b2f7Stbbdev } 154551c0b2f7Stbbdev 154651c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 154751c0b2f7Stbbdev //! Test exception safety 154851c0b2f7Stbbdev //! \brief \ref requirement 154951c0b2f7Stbbdev TEST_CASE("testing exception safety") { 155051c0b2f7Stbbdev TestExceptions(); 155151c0b2f7Stbbdev } 155251c0b2f7Stbbdev 155351c0b2f7Stbbdev //! Test abort operation 155451c0b2f7Stbbdev //! \brief \ref interface \ref requirement 155551c0b2f7Stbbdev TEST_CASE("testing abort operation") { 155651c0b2f7Stbbdev TestAbort(); 155751c0b2f7Stbbdev } 155851c0b2f7Stbbdev #endif 155951c0b2f7Stbbdev 156051c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 156151c0b2f7Stbbdev //! Test deduction guides 156251c0b2f7Stbbdev //! \brief \ref interface 156351c0b2f7Stbbdev TEST_CASE("testing deduction guides") { 156449e08aacStbbdev TestDeductionGuides<oneapi::tbb::concurrent_queue>(); 156549e08aacStbbdev TestDeductionGuides<oneapi::tbb::concurrent_bounded_queue>(); 156651c0b2f7Stbbdev } 156751c0b2f7Stbbdev #endif 1568*b15aabb3Stbbdev 1569*b15aabb3Stbbdev //! \brief \ref interface \ref requirement 1570*b15aabb3Stbbdev TEST_CASE("concurrent_queue iterator comparisons") { 1571*b15aabb3Stbbdev TestQueueIteratorComparisons<oneapi::tbb::concurrent_queue<int>>(); 1572*b15aabb3Stbbdev } 1573*b15aabb3Stbbdev 1574*b15aabb3Stbbdev //! \brief \ref interface \ref requirement 1575*b15aabb3Stbbdev TEST_CASE("concurrent_bounded_queue iterator comparisons") { 1576*b15aabb3Stbbdev TestQueueIteratorComparisons<oneapi::tbb::concurrent_bounded_queue<int>>(); 1577*b15aabb3Stbbdev } 1578