xref: /oneTBB/test/tbb/test_concurrent_queue.cpp (revision 88f73bbb)
151c0b2f7Stbbdev /*
2*88f73bbbSDmitri Mokhov     Copyright (c) 2005-2023 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #include <common/test.h>
1851c0b2f7Stbbdev #include <common/utils.h>
1951c0b2f7Stbbdev #include <common/vector_types.h>
2051c0b2f7Stbbdev #include <common/custom_allocators.h>
2151c0b2f7Stbbdev 
2251c0b2f7Stbbdev #include <tbb/concurrent_queue.h>
238155aaebSkboyarinov #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;
TestQueueElementsTestQueueElements3451c0b2f7Stbbdev     TestQueueElements( CQ& q, std::size_t n ) : queue(q), nthread(n) {}
operator ()TestQueueElements3551c0b2f7Stbbdev     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>
TestPrimitiveTypes(std::size_t nthread,T exemplar)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 
TestQueueWorksWithPrimitiveTypes()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>
TestVectorTypes()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 
TestQueueWorksWithSSE()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;
throw_elementthrow_element12451c0b2f7Stbbdev     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>
CopyWithThrowElement()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 
__anon525151d10202null18151c0b2f7Stbbdev     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);
__anon525151d10302null18951c0b2f7Stbbdev         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
2038155aaebSkboyarinov 
2048155aaebSkboyarinov struct TrackableItem {
2058155aaebSkboyarinov     static std::unordered_set<TrackableItem*> object_addresses;
2068155aaebSkboyarinov #if TBB_USE_EXCEPTIONS
2078155aaebSkboyarinov     static std::size_t global_count_for_exceptions;
2088155aaebSkboyarinov #endif
2098155aaebSkboyarinov 
TrackableItemTrackableItem2108155aaebSkboyarinov     TrackableItem() {
2118155aaebSkboyarinov #if TBB_USE_EXCEPTIONS
2128155aaebSkboyarinov         if (global_count_for_exceptions++ % 3 == 0) throw 1;
2138155aaebSkboyarinov #endif
2148155aaebSkboyarinov         bool res = object_addresses.emplace(this).second;
2158155aaebSkboyarinov         CHECK(res);
2168155aaebSkboyarinov     }
2178155aaebSkboyarinov 
~TrackableItemTrackableItem2188155aaebSkboyarinov     ~TrackableItem() {
2198155aaebSkboyarinov         auto it = object_addresses.find(this);
2208155aaebSkboyarinov         CHECK(it != object_addresses.end());
2218155aaebSkboyarinov         object_addresses.erase(it);
222*88f73bbbSDmitri Mokhov         CHECK(object_addresses.count(this) == 0);
2238155aaebSkboyarinov     }
2248155aaebSkboyarinov };
2258155aaebSkboyarinov 
2268155aaebSkboyarinov template <typename Container>
fill_and_catch(Container & q,std::size_t elements_count)2278155aaebSkboyarinov void fill_and_catch(Container& q, std::size_t elements_count) {
2288155aaebSkboyarinov     CHECK(TrackableItem::object_addresses.size() == 0);
2298155aaebSkboyarinov     for (std::size_t i = 0; i < elements_count; ++i) {
2308155aaebSkboyarinov #if TBB_USE_EXCEPTIONS
2318155aaebSkboyarinov         try {
2328155aaebSkboyarinov #endif
2338155aaebSkboyarinov             q.emplace();
2348155aaebSkboyarinov #if TBB_USE_EXCEPTIONS
2358155aaebSkboyarinov         } catch (int exception) {
2368155aaebSkboyarinov             CHECK(exception == 1);
2378155aaebSkboyarinov         }
2388155aaebSkboyarinov #endif
2398155aaebSkboyarinov     }
2408155aaebSkboyarinov #if TBB_USE_EXCEPTIONS
2418155aaebSkboyarinov     CHECK(TrackableItem::object_addresses.size() == 2 * elements_count / 3);
2428155aaebSkboyarinov #else
2438155aaebSkboyarinov     CHECK(TrackableItem::object_addresses.size() == elements_count);
2448155aaebSkboyarinov #endif
2458155aaebSkboyarinov }
2468155aaebSkboyarinov 
2478155aaebSkboyarinov std::unordered_set<TrackableItem*> TrackableItem::object_addresses;
2488155aaebSkboyarinov #if TBB_USE_EXCEPTIONS
2498155aaebSkboyarinov std::size_t TrackableItem::global_count_for_exceptions = 0;
2508155aaebSkboyarinov #endif
2518155aaebSkboyarinov 
2528155aaebSkboyarinov template <typename Container>
test_tracking_dtors_on_clear()2538155aaebSkboyarinov void test_tracking_dtors_on_clear() {
2548155aaebSkboyarinov     static_assert(std::is_same<typename Container::value_type, TrackableItem>::value, "Incorrect test setup");
2558155aaebSkboyarinov     const std::size_t elements_count = 100000;
2568155aaebSkboyarinov     {
2578155aaebSkboyarinov         Container q;
2588155aaebSkboyarinov         fill_and_catch(q, elements_count);
2598155aaebSkboyarinov 
2608155aaebSkboyarinov         q.clear();
2618155aaebSkboyarinov 
2628155aaebSkboyarinov         CHECK(q.empty());
2638155aaebSkboyarinov         CHECK(TrackableItem::object_addresses.empty());
2648155aaebSkboyarinov #if TBB_USE_EXCEPTIONS
2658155aaebSkboyarinov         TrackableItem::global_count_for_exceptions = 0;
2668155aaebSkboyarinov #endif
2678155aaebSkboyarinov     }
2688155aaebSkboyarinov     {
2698155aaebSkboyarinov         {
2708155aaebSkboyarinov             Container q;
2718155aaebSkboyarinov             fill_and_catch(q, elements_count);
2728155aaebSkboyarinov         } // Dtor of q would be called here
2738155aaebSkboyarinov         CHECK(TrackableItem::object_addresses.empty());
2748155aaebSkboyarinov #if TBB_USE_EXCEPTIONS
2758155aaebSkboyarinov         TrackableItem::global_count_for_exceptions = 0;
2768155aaebSkboyarinov #endif
2778155aaebSkboyarinov     }
2788155aaebSkboyarinov }
2798155aaebSkboyarinov 
2808155aaebSkboyarinov //! \brief \ref regression \ref error_guessing
2818155aaebSkboyarinov TEST_CASE("Test clear and dtor with TrackableItem") {
2828155aaebSkboyarinov     test_tracking_dtors_on_clear<oneapi::tbb::concurrent_queue<TrackableItem>>();
2838155aaebSkboyarinov     test_tracking_dtors_on_clear<oneapi::tbb::concurrent_bounded_queue<TrackableItem>>();
2848155aaebSkboyarinov }
285