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