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/vector_types.h> 2051c0b2f7Stbbdev #include <common/custom_allocators.h> 2151c0b2f7Stbbdev 2251c0b2f7Stbbdev #include <tbb/concurrent_queue.h> 23*8155aaebSkboyarinov #include <unordered_set> 2451c0b2f7Stbbdev 2551c0b2f7Stbbdev //! \file test_concurrent_queue.cpp 2651c0b2f7Stbbdev //! \brief Test for [containers.concurrent_queue containers.concurrent_bounded_queue] specification 2751c0b2f7Stbbdev 2851c0b2f7Stbbdev static constexpr std::size_t MaxThread = 4; 2951c0b2f7Stbbdev 3051c0b2f7Stbbdev template<typename CQ, typename T> 3151c0b2f7Stbbdev struct TestQueueElements { 3251c0b2f7Stbbdev CQ& queue; 3351c0b2f7Stbbdev const std::size_t nthread; 3451c0b2f7Stbbdev TestQueueElements( CQ& q, std::size_t n ) : queue(q), nthread(n) {} 3551c0b2f7Stbbdev void operator()( std::size_t k ) const { 3651c0b2f7Stbbdev for (std::size_t i=0; i < 1000; ++i) { 3751c0b2f7Stbbdev if( (i&0x1)==0 ) { 3851c0b2f7Stbbdev CHECK(T(k) < T(nthread)); 3951c0b2f7Stbbdev queue.push(T(k)); 4051c0b2f7Stbbdev } else { 4151c0b2f7Stbbdev // Pop item from queue 4251c0b2f7Stbbdev T item = 0; 4351c0b2f7Stbbdev queue.try_pop(item); 4451c0b2f7Stbbdev CHECK(item <= T(nthread)); 4551c0b2f7Stbbdev } 4651c0b2f7Stbbdev } 4751c0b2f7Stbbdev } 4851c0b2f7Stbbdev }; 4951c0b2f7Stbbdev 5051c0b2f7Stbbdev //! Test concurrent queue with primitive data type 5151c0b2f7Stbbdev template<typename CQ, typename T> 5251c0b2f7Stbbdev void TestPrimitiveTypes(std::size_t nthread, T exemplar) { 5351c0b2f7Stbbdev CQ queue; 5451c0b2f7Stbbdev for (std::size_t i = 0; i < 100; ++i) { 5551c0b2f7Stbbdev queue.push(exemplar); 5651c0b2f7Stbbdev } 5751c0b2f7Stbbdev utils::NativeParallelFor(nthread, TestQueueElements<CQ, T>(queue, nthread)); 5851c0b2f7Stbbdev } 5951c0b2f7Stbbdev 6051c0b2f7Stbbdev void TestQueueWorksWithPrimitiveTypes() { 6151c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_queue<char>, char>(MaxThread, (char)1); 6251c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_queue<int>, int>(MaxThread, (int)-12); 6351c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_queue<float>, float>(MaxThread, (float)-1.2f); 6451c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_queue<double>, double>(MaxThread, (double)-4.3); 6551c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_bounded_queue<char>, char>(MaxThread, (char)1); 6651c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_bounded_queue<int>, int>(MaxThread, (int)-12); 6751c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_bounded_queue<float>, float>(MaxThread, (float)-1.2f); 6851c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_bounded_queue<double>, double>(MaxThread, (double)-4.3); 6951c0b2f7Stbbdev } 7051c0b2f7Stbbdev 7151c0b2f7Stbbdev #if HAVE_m128 || HAVE_m256 7251c0b2f7Stbbdev //! Test concurrent queue with vector types 7351c0b2f7Stbbdev /** Type Queue should be a queue of ClassWithSSE/ClassWithAVX. */ 7451c0b2f7Stbbdev template<typename ClassWithVectorType, typename Queue> 7551c0b2f7Stbbdev void TestVectorTypes() { 7651c0b2f7Stbbdev Queue q1; 7751c0b2f7Stbbdev for (int i = 0; i < 100; ++i) { 7851c0b2f7Stbbdev // VC8 does not properly align a temporary value; to work around, use explicit variable 7951c0b2f7Stbbdev ClassWithVectorType bar(i); 8051c0b2f7Stbbdev q1.push(bar); 8151c0b2f7Stbbdev } 8251c0b2f7Stbbdev 8351c0b2f7Stbbdev // Copy the queue 8451c0b2f7Stbbdev Queue q2 = q1; 8551c0b2f7Stbbdev // Check that elements of the copy are correct 8651c0b2f7Stbbdev typename Queue::const_iterator ci = q2.unsafe_begin(); 8751c0b2f7Stbbdev for (int i=0; i < 100; ++i ) { 8851c0b2f7Stbbdev CHECK((ci != q2.unsafe_end())); 8951c0b2f7Stbbdev ClassWithVectorType foo = *ci; 9051c0b2f7Stbbdev ClassWithVectorType bar(i); 9151c0b2f7Stbbdev CHECK((*ci == bar)); 9251c0b2f7Stbbdev ++ci; 9351c0b2f7Stbbdev } 9451c0b2f7Stbbdev 9551c0b2f7Stbbdev for (int i = 0; i < 101; ++i) { 9651c0b2f7Stbbdev ClassWithVectorType tmp; 9751c0b2f7Stbbdev bool b = q1.try_pop(tmp); 9851c0b2f7Stbbdev CHECK((b == (i < 100))); 9951c0b2f7Stbbdev ClassWithVectorType bar(i); 10051c0b2f7Stbbdev CHECK((!b || tmp==bar)); 10151c0b2f7Stbbdev } 10251c0b2f7Stbbdev } 10351c0b2f7Stbbdev #endif /* HAVE_m128 || HAVE_m256 */ 10451c0b2f7Stbbdev 10551c0b2f7Stbbdev void TestQueueWorksWithSSE() { 10651c0b2f7Stbbdev 10751c0b2f7Stbbdev #if HAVE_m128 10851c0b2f7Stbbdev TestVectorTypes<ClassWithSSE, tbb::concurrent_queue<ClassWithSSE> >(); 10951c0b2f7Stbbdev TestVectorTypes<ClassWithSSE, tbb::concurrent_bounded_queue<ClassWithSSE> >(); 11051c0b2f7Stbbdev #endif /* HAVE_m128 */ 11151c0b2f7Stbbdev #if HAVE_m256 11251c0b2f7Stbbdev if( have_AVX() ) { 11351c0b2f7Stbbdev TestVectorTypes<ClassWithAVX, tbb::concurrent_queue<ClassWithAVX> >(); 11451c0b2f7Stbbdev TestVectorTypes<ClassWithAVX, tbb::concurrent_bounded_queue<ClassWithAVX> >(); 11551c0b2f7Stbbdev } 11651c0b2f7Stbbdev #endif /* HAVE_m256 */ 11751c0b2f7Stbbdev } 11851c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 11951c0b2f7Stbbdev int rnd_elem = -1; 12051c0b2f7Stbbdev int global_counter = -1; 12151c0b2f7Stbbdev 12251c0b2f7Stbbdev struct throw_element { 12351c0b2f7Stbbdev throw_element() = default; 12451c0b2f7Stbbdev throw_element(const throw_element&) { 12551c0b2f7Stbbdev if (global_counter++ == rnd_elem) { 12651c0b2f7Stbbdev throw std::exception{}; 12751c0b2f7Stbbdev } 12851c0b2f7Stbbdev } 12951c0b2f7Stbbdev 13051c0b2f7Stbbdev throw_element& operator= (const throw_element&) = default; 13151c0b2f7Stbbdev }; 13251c0b2f7Stbbdev 13351c0b2f7Stbbdev template <typename Queue> 13451c0b2f7Stbbdev void CopyWithThrowElement() { 13551c0b2f7Stbbdev utils::FastRandom<> rnd(42); 13651c0b2f7Stbbdev 13751c0b2f7Stbbdev Queue source; 13851c0b2f7Stbbdev 13951c0b2f7Stbbdev constexpr size_t queue_size = 100000; 14051c0b2f7Stbbdev for (std::size_t i = 0; i < queue_size; ++i) { 14151c0b2f7Stbbdev source.emplace(); 14251c0b2f7Stbbdev } 14351c0b2f7Stbbdev 14451c0b2f7Stbbdev for (std::size_t i = 0; i < 100; ++i) { 14551c0b2f7Stbbdev global_counter = 0; 14651c0b2f7Stbbdev rnd_elem = rnd.get() % queue_size; 14751c0b2f7Stbbdev 14851c0b2f7Stbbdev REQUIRE_THROWS_AS( [&] { 14951c0b2f7Stbbdev Queue copy(source); 15051c0b2f7Stbbdev utils::suppress_unused_warning(copy); 15151c0b2f7Stbbdev }(), std::exception); 15251c0b2f7Stbbdev } 15351c0b2f7Stbbdev } 15451c0b2f7Stbbdev #endif // TBB_USE_EXCEPTIONS 15551c0b2f7Stbbdev 15651c0b2f7Stbbdev //! Test work with different fypes 15751c0b2f7Stbbdev //! \brief \ref error_guessing 15851c0b2f7Stbbdev TEST_CASE("testing work with different fypes") { 15951c0b2f7Stbbdev TestQueueWorksWithPrimitiveTypes(); 16051c0b2f7Stbbdev } 16151c0b2f7Stbbdev 16251c0b2f7Stbbdev //! Test work with vector types 16351c0b2f7Stbbdev //! \brief \ref error_guessing 16451c0b2f7Stbbdev TEST_CASE("testing vector types") { 16551c0b2f7Stbbdev TestQueueWorksWithSSE(); 16651c0b2f7Stbbdev } 16751c0b2f7Stbbdev 16851c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 16951c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing 17051c0b2f7Stbbdev TEST_CASE("Test exception in allocation") { 17151c0b2f7Stbbdev using allocator_type = StaticSharedCountingAllocator<std::allocator<int>>; 17251c0b2f7Stbbdev using queue_type = tbb::concurrent_queue<int, allocator_type>; 17351c0b2f7Stbbdev 17451c0b2f7Stbbdev queue_type src_queue; 17551c0b2f7Stbbdev for (int i = 0; i < 100000; ++i) { 17651c0b2f7Stbbdev src_queue.push(i); 17751c0b2f7Stbbdev } 17851c0b2f7Stbbdev 17951c0b2f7Stbbdev allocator_type::set_limits(1); 18051c0b2f7Stbbdev 18151c0b2f7Stbbdev REQUIRE_THROWS_AS( [] { 18251c0b2f7Stbbdev queue_type queue1; 18351c0b2f7Stbbdev queue1.push(1); 18451c0b2f7Stbbdev }(), const std::bad_alloc); 18551c0b2f7Stbbdev 18651c0b2f7Stbbdev for (std::size_t i = 1; i < 1000; ++i) { 18751c0b2f7Stbbdev allocator_type::init_counters(); 18851c0b2f7Stbbdev allocator_type::set_limits(1); 18951c0b2f7Stbbdev REQUIRE_THROWS_AS( [&] { 19051c0b2f7Stbbdev queue_type queue2(src_queue); 19151c0b2f7Stbbdev utils::suppress_unused_warning(queue2); 19251c0b2f7Stbbdev }(), const std::bad_alloc); 19351c0b2f7Stbbdev } 19451c0b2f7Stbbdev } 19551c0b2f7Stbbdev 19651c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing 19751c0b2f7Stbbdev TEST_CASE("Test exception in allocation") { 19851c0b2f7Stbbdev CopyWithThrowElement<tbb::concurrent_queue<throw_element>>(); 19951c0b2f7Stbbdev CopyWithThrowElement<tbb::concurrent_bounded_queue<throw_element>>(); 20051c0b2f7Stbbdev } 20151c0b2f7Stbbdev 20251c0b2f7Stbbdev #endif // TBB_USE_EXCEPTIONS 203*8155aaebSkboyarinov 204*8155aaebSkboyarinov struct TrackableItem { 205*8155aaebSkboyarinov static std::unordered_set<TrackableItem*> object_addresses; 206*8155aaebSkboyarinov #if TBB_USE_EXCEPTIONS 207*8155aaebSkboyarinov static std::size_t global_count_for_exceptions; 208*8155aaebSkboyarinov #endif 209*8155aaebSkboyarinov 210*8155aaebSkboyarinov TrackableItem() { 211*8155aaebSkboyarinov #if TBB_USE_EXCEPTIONS 212*8155aaebSkboyarinov if (global_count_for_exceptions++ % 3 == 0) throw 1; 213*8155aaebSkboyarinov #endif 214*8155aaebSkboyarinov bool res = object_addresses.emplace(this).second; 215*8155aaebSkboyarinov CHECK(res); 216*8155aaebSkboyarinov } 217*8155aaebSkboyarinov 218*8155aaebSkboyarinov ~TrackableItem() { 219*8155aaebSkboyarinov auto it = object_addresses.find(this); 220*8155aaebSkboyarinov CHECK(it != object_addresses.end()); 221*8155aaebSkboyarinov object_addresses.erase(it); 222*8155aaebSkboyarinov } 223*8155aaebSkboyarinov }; 224*8155aaebSkboyarinov 225*8155aaebSkboyarinov template <typename Container> 226*8155aaebSkboyarinov void fill_and_catch(Container& q, std::size_t elements_count) { 227*8155aaebSkboyarinov CHECK(TrackableItem::object_addresses.size() == 0); 228*8155aaebSkboyarinov for (std::size_t i = 0; i < elements_count; ++i) { 229*8155aaebSkboyarinov #if TBB_USE_EXCEPTIONS 230*8155aaebSkboyarinov try { 231*8155aaebSkboyarinov #endif 232*8155aaebSkboyarinov q.emplace(); 233*8155aaebSkboyarinov #if TBB_USE_EXCEPTIONS 234*8155aaebSkboyarinov } catch (int exception) { 235*8155aaebSkboyarinov CHECK(exception == 1); 236*8155aaebSkboyarinov } 237*8155aaebSkboyarinov #endif 238*8155aaebSkboyarinov } 239*8155aaebSkboyarinov #if TBB_USE_EXCEPTIONS 240*8155aaebSkboyarinov CHECK(TrackableItem::object_addresses.size() == 2 * elements_count / 3); 241*8155aaebSkboyarinov #else 242*8155aaebSkboyarinov CHECK(TrackableItem::object_addresses.size() == elements_count); 243*8155aaebSkboyarinov #endif 244*8155aaebSkboyarinov } 245*8155aaebSkboyarinov 246*8155aaebSkboyarinov std::unordered_set<TrackableItem*> TrackableItem::object_addresses; 247*8155aaebSkboyarinov #if TBB_USE_EXCEPTIONS 248*8155aaebSkboyarinov std::size_t TrackableItem::global_count_for_exceptions = 0; 249*8155aaebSkboyarinov #endif 250*8155aaebSkboyarinov 251*8155aaebSkboyarinov template <typename Container> 252*8155aaebSkboyarinov void test_tracking_dtors_on_clear() { 253*8155aaebSkboyarinov static_assert(std::is_same<typename Container::value_type, TrackableItem>::value, "Incorrect test setup"); 254*8155aaebSkboyarinov const std::size_t elements_count = 100000; 255*8155aaebSkboyarinov { 256*8155aaebSkboyarinov Container q; 257*8155aaebSkboyarinov fill_and_catch(q, elements_count); 258*8155aaebSkboyarinov 259*8155aaebSkboyarinov q.clear(); 260*8155aaebSkboyarinov 261*8155aaebSkboyarinov CHECK(q.empty()); 262*8155aaebSkboyarinov CHECK(TrackableItem::object_addresses.empty()); 263*8155aaebSkboyarinov #if TBB_USE_EXCEPTIONS 264*8155aaebSkboyarinov TrackableItem::global_count_for_exceptions = 0; 265*8155aaebSkboyarinov #endif 266*8155aaebSkboyarinov } 267*8155aaebSkboyarinov { 268*8155aaebSkboyarinov { 269*8155aaebSkboyarinov Container q; 270*8155aaebSkboyarinov fill_and_catch(q, elements_count); 271*8155aaebSkboyarinov } // Dtor of q would be called here 272*8155aaebSkboyarinov CHECK(TrackableItem::object_addresses.empty()); 273*8155aaebSkboyarinov #if TBB_USE_EXCEPTIONS 274*8155aaebSkboyarinov TrackableItem::global_count_for_exceptions = 0; 275*8155aaebSkboyarinov #endif 276*8155aaebSkboyarinov } 277*8155aaebSkboyarinov } 278*8155aaebSkboyarinov 279*8155aaebSkboyarinov //! \brief \ref regression \ref error_guessing 280*8155aaebSkboyarinov TEST_CASE("Test clear and dtor with TrackableItem") { 281*8155aaebSkboyarinov test_tracking_dtors_on_clear<oneapi::tbb::concurrent_queue<TrackableItem>>(); 282*8155aaebSkboyarinov test_tracking_dtors_on_clear<oneapi::tbb::concurrent_bounded_queue<TrackableItem>>(); 283*8155aaebSkboyarinov } 284