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