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