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