xref: /oneTBB/test/tbb/test_concurrent_queue.cpp (revision d86ed7fb)
1 /*
2     Copyright (c) 2005-2020 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