xref: /oneTBB/test/tbb/test_concurrent_queue.cpp (revision 8155aaeb)
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