1*51c0b2f7Stbbdev /* 2*51c0b2f7Stbbdev Copyright (c) 2005-2020 Intel Corporation 3*51c0b2f7Stbbdev 4*51c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License"); 5*51c0b2f7Stbbdev you may not use this file except in compliance with the License. 6*51c0b2f7Stbbdev You may obtain a copy of the License at 7*51c0b2f7Stbbdev 8*51c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0 9*51c0b2f7Stbbdev 10*51c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software 11*51c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS, 12*51c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*51c0b2f7Stbbdev See the License for the specific language governing permissions and 14*51c0b2f7Stbbdev limitations under the License. 15*51c0b2f7Stbbdev */ 16*51c0b2f7Stbbdev 17*51c0b2f7Stbbdev #include <common/test.h> 18*51c0b2f7Stbbdev #include <common/utils.h> 19*51c0b2f7Stbbdev #include <common/vector_types.h> 20*51c0b2f7Stbbdev #include <common/custom_allocators.h> 21*51c0b2f7Stbbdev 22*51c0b2f7Stbbdev #include <tbb/concurrent_queue.h> 23*51c0b2f7Stbbdev 24*51c0b2f7Stbbdev //! \file test_concurrent_queue.cpp 25*51c0b2f7Stbbdev //! \brief Test for [containers.concurrent_queue containers.concurrent_bounded_queue] specification 26*51c0b2f7Stbbdev 27*51c0b2f7Stbbdev static constexpr std::size_t MaxThread = 4; 28*51c0b2f7Stbbdev 29*51c0b2f7Stbbdev template<typename CQ, typename T> 30*51c0b2f7Stbbdev struct TestQueueElements { 31*51c0b2f7Stbbdev CQ& queue; 32*51c0b2f7Stbbdev const std::size_t nthread; 33*51c0b2f7Stbbdev TestQueueElements( CQ& q, std::size_t n ) : queue(q), nthread(n) {} 34*51c0b2f7Stbbdev void operator()( std::size_t k ) const { 35*51c0b2f7Stbbdev for (std::size_t i=0; i < 1000; ++i) { 36*51c0b2f7Stbbdev if( (i&0x1)==0 ) { 37*51c0b2f7Stbbdev CHECK(T(k) < T(nthread)); 38*51c0b2f7Stbbdev queue.push(T(k)); 39*51c0b2f7Stbbdev } else { 40*51c0b2f7Stbbdev // Pop item from queue 41*51c0b2f7Stbbdev T item = 0; 42*51c0b2f7Stbbdev queue.try_pop(item); 43*51c0b2f7Stbbdev CHECK(item <= T(nthread)); 44*51c0b2f7Stbbdev } 45*51c0b2f7Stbbdev } 46*51c0b2f7Stbbdev } 47*51c0b2f7Stbbdev }; 48*51c0b2f7Stbbdev 49*51c0b2f7Stbbdev //! Test concurrent queue with primitive data type 50*51c0b2f7Stbbdev template<typename CQ, typename T> 51*51c0b2f7Stbbdev void TestPrimitiveTypes(std::size_t nthread, T exemplar) { 52*51c0b2f7Stbbdev CQ queue; 53*51c0b2f7Stbbdev for (std::size_t i = 0; i < 100; ++i) { 54*51c0b2f7Stbbdev queue.push(exemplar); 55*51c0b2f7Stbbdev } 56*51c0b2f7Stbbdev utils::NativeParallelFor(nthread, TestQueueElements<CQ, T>(queue, nthread)); 57*51c0b2f7Stbbdev } 58*51c0b2f7Stbbdev 59*51c0b2f7Stbbdev void TestQueueWorksWithPrimitiveTypes() { 60*51c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_queue<char>, char>(MaxThread, (char)1); 61*51c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_queue<int>, int>(MaxThread, (int)-12); 62*51c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_queue<float>, float>(MaxThread, (float)-1.2f); 63*51c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_queue<double>, double>(MaxThread, (double)-4.3); 64*51c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_bounded_queue<char>, char>(MaxThread, (char)1); 65*51c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_bounded_queue<int>, int>(MaxThread, (int)-12); 66*51c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_bounded_queue<float>, float>(MaxThread, (float)-1.2f); 67*51c0b2f7Stbbdev TestPrimitiveTypes<tbb::concurrent_bounded_queue<double>, double>(MaxThread, (double)-4.3); 68*51c0b2f7Stbbdev } 69*51c0b2f7Stbbdev 70*51c0b2f7Stbbdev #if HAVE_m128 || HAVE_m256 71*51c0b2f7Stbbdev //! Test concurrent queue with vector types 72*51c0b2f7Stbbdev /** Type Queue should be a queue of ClassWithSSE/ClassWithAVX. */ 73*51c0b2f7Stbbdev template<typename ClassWithVectorType, typename Queue> 74*51c0b2f7Stbbdev void TestVectorTypes() { 75*51c0b2f7Stbbdev Queue q1; 76*51c0b2f7Stbbdev for (int i = 0; i < 100; ++i) { 77*51c0b2f7Stbbdev // VC8 does not properly align a temporary value; to work around, use explicit variable 78*51c0b2f7Stbbdev ClassWithVectorType bar(i); 79*51c0b2f7Stbbdev q1.push(bar); 80*51c0b2f7Stbbdev } 81*51c0b2f7Stbbdev 82*51c0b2f7Stbbdev // Copy the queue 83*51c0b2f7Stbbdev Queue q2 = q1; 84*51c0b2f7Stbbdev // Check that elements of the copy are correct 85*51c0b2f7Stbbdev typename Queue::const_iterator ci = q2.unsafe_begin(); 86*51c0b2f7Stbbdev for (int i=0; i < 100; ++i ) { 87*51c0b2f7Stbbdev CHECK((ci != q2.unsafe_end())); 88*51c0b2f7Stbbdev ClassWithVectorType foo = *ci; 89*51c0b2f7Stbbdev ClassWithVectorType bar(i); 90*51c0b2f7Stbbdev CHECK((*ci == bar)); 91*51c0b2f7Stbbdev ++ci; 92*51c0b2f7Stbbdev } 93*51c0b2f7Stbbdev 94*51c0b2f7Stbbdev for (int i = 0; i < 101; ++i) { 95*51c0b2f7Stbbdev ClassWithVectorType tmp; 96*51c0b2f7Stbbdev bool b = q1.try_pop(tmp); 97*51c0b2f7Stbbdev CHECK((b == (i < 100))); 98*51c0b2f7Stbbdev ClassWithVectorType bar(i); 99*51c0b2f7Stbbdev CHECK((!b || tmp==bar)); 100*51c0b2f7Stbbdev } 101*51c0b2f7Stbbdev } 102*51c0b2f7Stbbdev #endif /* HAVE_m128 || HAVE_m256 */ 103*51c0b2f7Stbbdev 104*51c0b2f7Stbbdev void TestQueueWorksWithSSE() { 105*51c0b2f7Stbbdev 106*51c0b2f7Stbbdev #if HAVE_m128 107*51c0b2f7Stbbdev TestVectorTypes<ClassWithSSE, tbb::concurrent_queue<ClassWithSSE> >(); 108*51c0b2f7Stbbdev TestVectorTypes<ClassWithSSE, tbb::concurrent_bounded_queue<ClassWithSSE> >(); 109*51c0b2f7Stbbdev #endif /* HAVE_m128 */ 110*51c0b2f7Stbbdev #if HAVE_m256 111*51c0b2f7Stbbdev if( have_AVX() ) { 112*51c0b2f7Stbbdev TestVectorTypes<ClassWithAVX, tbb::concurrent_queue<ClassWithAVX> >(); 113*51c0b2f7Stbbdev TestVectorTypes<ClassWithAVX, tbb::concurrent_bounded_queue<ClassWithAVX> >(); 114*51c0b2f7Stbbdev } 115*51c0b2f7Stbbdev #endif /* HAVE_m256 */ 116*51c0b2f7Stbbdev } 117*51c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 118*51c0b2f7Stbbdev int rnd_elem = -1; 119*51c0b2f7Stbbdev int global_counter = -1; 120*51c0b2f7Stbbdev 121*51c0b2f7Stbbdev struct throw_element { 122*51c0b2f7Stbbdev throw_element() = default; 123*51c0b2f7Stbbdev throw_element(const throw_element&) { 124*51c0b2f7Stbbdev if (global_counter++ == rnd_elem) { 125*51c0b2f7Stbbdev throw std::exception{}; 126*51c0b2f7Stbbdev } 127*51c0b2f7Stbbdev } 128*51c0b2f7Stbbdev 129*51c0b2f7Stbbdev throw_element& operator= (const throw_element&) = default; 130*51c0b2f7Stbbdev }; 131*51c0b2f7Stbbdev 132*51c0b2f7Stbbdev template <typename Queue> 133*51c0b2f7Stbbdev void CopyWithThrowElement() { 134*51c0b2f7Stbbdev utils::FastRandom<> rnd(42); 135*51c0b2f7Stbbdev 136*51c0b2f7Stbbdev Queue source; 137*51c0b2f7Stbbdev 138*51c0b2f7Stbbdev constexpr size_t queue_size = 100000; 139*51c0b2f7Stbbdev for (std::size_t i = 0; i < queue_size; ++i) { 140*51c0b2f7Stbbdev source.emplace(); 141*51c0b2f7Stbbdev } 142*51c0b2f7Stbbdev 143*51c0b2f7Stbbdev for (std::size_t i = 0; i < 100; ++i) { 144*51c0b2f7Stbbdev global_counter = 0; 145*51c0b2f7Stbbdev rnd_elem = rnd.get() % queue_size; 146*51c0b2f7Stbbdev 147*51c0b2f7Stbbdev REQUIRE_THROWS_AS( [&] { 148*51c0b2f7Stbbdev Queue copy(source); 149*51c0b2f7Stbbdev utils::suppress_unused_warning(copy); 150*51c0b2f7Stbbdev }(), std::exception); 151*51c0b2f7Stbbdev } 152*51c0b2f7Stbbdev } 153*51c0b2f7Stbbdev #endif // TBB_USE_EXCEPTIONS 154*51c0b2f7Stbbdev 155*51c0b2f7Stbbdev //! Test work with different fypes 156*51c0b2f7Stbbdev //! \brief \ref error_guessing 157*51c0b2f7Stbbdev TEST_CASE("testing work with different fypes") { 158*51c0b2f7Stbbdev TestQueueWorksWithPrimitiveTypes(); 159*51c0b2f7Stbbdev } 160*51c0b2f7Stbbdev 161*51c0b2f7Stbbdev //! Test work with vector types 162*51c0b2f7Stbbdev //! \brief \ref error_guessing 163*51c0b2f7Stbbdev TEST_CASE("testing vector types") { 164*51c0b2f7Stbbdev TestQueueWorksWithSSE(); 165*51c0b2f7Stbbdev } 166*51c0b2f7Stbbdev 167*51c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS 168*51c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing 169*51c0b2f7Stbbdev TEST_CASE("Test exception in allocation") { 170*51c0b2f7Stbbdev using allocator_type = StaticSharedCountingAllocator<std::allocator<int>>; 171*51c0b2f7Stbbdev using queue_type = tbb::concurrent_queue<int, allocator_type>; 172*51c0b2f7Stbbdev 173*51c0b2f7Stbbdev queue_type src_queue; 174*51c0b2f7Stbbdev for (int i = 0; i < 100000; ++i) { 175*51c0b2f7Stbbdev src_queue.push(i); 176*51c0b2f7Stbbdev } 177*51c0b2f7Stbbdev 178*51c0b2f7Stbbdev allocator_type::set_limits(1); 179*51c0b2f7Stbbdev 180*51c0b2f7Stbbdev REQUIRE_THROWS_AS( [] { 181*51c0b2f7Stbbdev queue_type queue1; 182*51c0b2f7Stbbdev queue1.push(1); 183*51c0b2f7Stbbdev }(), const std::bad_alloc); 184*51c0b2f7Stbbdev 185*51c0b2f7Stbbdev for (std::size_t i = 1; i < 1000; ++i) { 186*51c0b2f7Stbbdev allocator_type::init_counters(); 187*51c0b2f7Stbbdev allocator_type::set_limits(1); 188*51c0b2f7Stbbdev REQUIRE_THROWS_AS( [&] { 189*51c0b2f7Stbbdev queue_type queue2(src_queue); 190*51c0b2f7Stbbdev utils::suppress_unused_warning(queue2); 191*51c0b2f7Stbbdev }(), const std::bad_alloc); 192*51c0b2f7Stbbdev } 193*51c0b2f7Stbbdev } 194*51c0b2f7Stbbdev 195*51c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing 196*51c0b2f7Stbbdev TEST_CASE("Test exception in allocation") { 197*51c0b2f7Stbbdev CopyWithThrowElement<tbb::concurrent_queue<throw_element>>(); 198*51c0b2f7Stbbdev CopyWithThrowElement<tbb::concurrent_bounded_queue<throw_element>>(); 199*51c0b2f7Stbbdev } 200*51c0b2f7Stbbdev 201*51c0b2f7Stbbdev #endif // TBB_USE_EXCEPTIONS 202